Merge "Add feature tag android.hardware.vr.headtracking."
diff --git a/api/current.txt b/api/current.txt
index f2e1492..9557ee5 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -751,6 +751,7 @@
     field public static final int isModifier = 16843334; // 0x1010246
     field public static final int isRepeatable = 16843336; // 0x1010248
     field public static final int isScrollContainer = 16843342; // 0x101024e
+    field public static final int isStatic = 16844125; // 0x101055d
     field public static final int isSticky = 16843335; // 0x1010247
     field public static final int isolatedProcess = 16843689; // 0x10103a9
     field public static final int isolatedSplits = 16844109; // 0x101054d
@@ -1052,6 +1053,7 @@
     field public static final int ratingBarStyleSmall = 16842877; // 0x101007d
     field public static final int readPermission = 16842759; // 0x1010007
     field public static final int recognitionService = 16843932; // 0x101049c
+    field public static final int recycleEnabled = 16844124; // 0x101055c
     field public static final int relinquishTaskIdentity = 16843894; // 0x1010476
     field public static final int reparent = 16843964; // 0x10104bc
     field public static final int reparentWithOverlay = 16843965; // 0x10104bd
@@ -1788,6 +1790,7 @@
     field public static final int accessibilityActionSetProgress = 16908349; // 0x102003d
     field public static final int accessibilityActionShowOnScreen = 16908342; // 0x1020036
     field public static final int addToDictionary = 16908330; // 0x102002a
+    field public static final int autofill = 16908355; // 0x1020043
     field public static final int background = 16908288; // 0x1020000
     field public static final int button1 = 16908313; // 0x1020019
     field public static final int button2 = 16908314; // 0x102001a
@@ -5491,7 +5494,6 @@
 
   public final class NotificationChannel implements android.os.Parcelable {
     ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
-    ctor public NotificationChannel(java.lang.String, int, int);
     ctor protected NotificationChannel(android.os.Parcel);
     method public boolean canBypassDnd();
     method public boolean canShowBadge();
@@ -5505,7 +5507,6 @@
     method public int getLightColor();
     method public int getLockscreenVisibility();
     method public java.lang.CharSequence getName();
-    method public int getNameResId();
     method public android.net.Uri getSound();
     method public long[] getVibrationPattern();
     method public void setBypassDnd(boolean);
@@ -5513,6 +5514,7 @@
     method public void setImportance(int);
     method public void setLightColor(int);
     method public void setLockscreenVisibility(int);
+    method public void setName(java.lang.CharSequence);
     method public void setShowBadge(boolean);
     method public void setSound(android.net.Uri, android.media.AudioAttributes);
     method public void setVibrationPattern(long[]);
@@ -5525,14 +5527,12 @@
 
   public final class NotificationChannelGroup implements android.os.Parcelable {
     ctor public NotificationChannelGroup(java.lang.String, java.lang.CharSequence);
-    ctor public NotificationChannelGroup(java.lang.String, int);
     ctor protected NotificationChannelGroup(android.os.Parcel);
     method public android.app.NotificationChannelGroup clone();
     method public int describeContents();
     method public java.util.List<android.app.NotificationChannel> getChannels();
     method public java.lang.String getId();
     method public java.lang.CharSequence getName();
-    method public int getNameResId();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.NotificationChannelGroup> CREATOR;
   }
@@ -5548,12 +5548,14 @@
     method public void createNotificationChannelGroups(java.util.List<android.app.NotificationChannelGroup>);
     method public void createNotificationChannels(java.util.List<android.app.NotificationChannel>);
     method public void deleteNotificationChannel(java.lang.String);
+    method public void deleteNotificationChannelGroup(java.lang.String);
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
     method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String);
     method public java.util.Map<java.lang.String, android.app.AutomaticZenRule> getAutomaticZenRules();
     method public final int getCurrentInterruptionFilter();
     method public int getImportance();
     method public android.app.NotificationChannel getNotificationChannel(java.lang.String);
+    method public java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups();
     method public java.util.List<android.app.NotificationChannel> getNotificationChannels();
     method public android.app.NotificationManager.Policy getNotificationPolicy();
     method public boolean isNotificationPolicyAccessGranted();
@@ -6565,7 +6567,7 @@
 
   public static class AssistStructure.ViewNode {
     method public float getAlpha();
-    method public int getAutoFillHint();
+    method public java.lang.String[] getAutoFillHint();
     method public android.view.autofill.AutofillId getAutofillId();
     method public java.lang.String[] getAutofillOptions();
     method public int getAutofillType();
@@ -21826,7 +21828,7 @@
     method public deprecated java.nio.ByteBuffer[] getInputBuffers();
     method public final android.media.MediaFormat getInputFormat();
     method public android.media.Image getInputImage(int);
-    method public android.os.Bundle getMetrics();
+    method public android.media.MediaMetricsSet getMetrics();
     method public final java.lang.String getName();
     method public java.nio.ByteBuffer getOutputBuffer(int);
     method public deprecated java.nio.ByteBuffer[] getOutputBuffers();
@@ -22378,7 +22380,7 @@
     method public boolean advance();
     method public long getCachedDuration();
     method public android.media.DrmInitData getDrmInitData();
-    method public android.os.Bundle getMetrics();
+    method public android.media.MediaMetricsSet getMetrics();
     method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
     method public boolean getSampleCryptoInfo(android.media.MediaCodec.CryptoInfo);
     method public int getSampleFlags();
@@ -22633,6 +22635,69 @@
     field public static final int OPTION_PREVIOUS_SYNC = 0; // 0x0
   }
 
+  public final class MediaMetricsSet {
+    method public double getDouble(java.lang.String, double);
+    method public int getInt(java.lang.String, int);
+    method public long getLong(java.lang.String, long);
+    method public java.lang.String getString(java.lang.String, java.lang.String);
+    method public boolean isEmpty();
+    method public java.util.Set<java.lang.String> keySet();
+    method public int size();
+  }
+
+  public static final class MediaMetricsSet.MediaCodec {
+    field public static final java.lang.String KEY_CODEC = "android.media.mediacodec.codec";
+    field public static final java.lang.String KEY_ENCODER = "android.media.mediacodec.encoder";
+    field public static final java.lang.String KEY_HEIGHT = "android.media.mediacodec.height";
+    field public static final java.lang.String KEY_MIME = "android.media.mediacodec.mime";
+    field public static final java.lang.String KEY_MODE = "android.media.mediacodec.mode";
+    field public static final java.lang.String KEY_ROTATION = "android.media.mediacodec.rotation";
+    field public static final java.lang.String KEY_SECURE = "android.media.mediacodec.secure";
+    field public static final java.lang.String KEY_WIDTH = "android.media.mediacodec.width";
+    field public static final java.lang.String MODE_AUDIO = "audio";
+    field public static final java.lang.String MODE_VIDEO = "video";
+  }
+
+  public static final class MediaMetricsSet.MediaExtractor {
+    field public static final java.lang.String KEY_FORMAT = "android.media.mediaextractor.fmt";
+    field public static final java.lang.String KEY_MIME = "android.media.mediaextractor.mime";
+    field public static final java.lang.String KEY_TRACKS = "android.media.mediaextractor.ntrk";
+  }
+
+  public static final class MediaMetricsSet.MediaPlayer {
+    field public static final java.lang.String KEY_CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
+    field public static final java.lang.String KEY_CODEC_VIDEO = "android.media.mediaplayer.video.codec";
+    field public static final java.lang.String KEY_DURATION = "android.media.mediaplayer.durationMs";
+    field public static final java.lang.String KEY_ERRORS = "android.media.mediaplayer.err";
+    field public static final java.lang.String KEY_ERROR_CODE = "android.media.mediaplayer.errcode";
+    field public static final java.lang.String KEY_FRAMES = "android.media.mediaplayer.frames";
+    field public static final java.lang.String KEY_FRAMES_DROPPED = "android.media.mediaplayer.dropped";
+    field public static final java.lang.String KEY_HEIGHT = "android.media.mediaplayer.height";
+    field public static final java.lang.String KEY_MIME_AUDIO = "android.media.mediaplayer.audio.mime";
+    field public static final java.lang.String KEY_MIME_VIDEO = "android.media.mediaplayer.video.mime";
+    field public static final java.lang.String KEY_PLAYING = "android.media.mediaplayer.playingMs";
+    field public static final java.lang.String KEY_WIDTH = "android.media.mediaplayer.width";
+  }
+
+  public static final class MediaMetricsSet.MediaRecorder {
+    field public static final java.lang.String KEY_AUDIO_BITRATE = "android.media.mediarecorder.audio-bitrate";
+    field public static final java.lang.String KEY_AUDIO_CHANNELS = "android.media.mediarecorder.audio-channels";
+    field public static final java.lang.String KEY_AUDIO_SAMPLERATE = "android.media.mediarecorder.audio-samplerate";
+    field public static final java.lang.String KEY_AUDIO_TIMESCALE = "android.media.mediarecorder.audio-timescale";
+    field public static final java.lang.String KEY_CAPTURE_FPS = "android.media.mediarecorder.capture-fps";
+    field public static final java.lang.String KEY_CAPTURE_FPS_ENABLE = "android.media.mediarecorder.capture-fpsenable";
+    field public static final java.lang.String KEY_FRAMERATE = "android.media.mediarecorder.frame-rate";
+    field public static final java.lang.String KEY_HEIGHT = "android.media.mediarecorder.height";
+    field public static final java.lang.String KEY_MOVIE_TIMESCALE = "android.media.mediarecorder.movie-timescale";
+    field public static final java.lang.String KEY_ROTATION = "android.media.mediarecorder.rotation";
+    field public static final java.lang.String KEY_VIDEO_BITRATE = "android.media.mediarecorder.video-bitrate";
+    field public static final java.lang.String KEY_VIDEO_IFRAME_INTERVAL = "android.media.mediarecorder.video-iframe-interval";
+    field public static final java.lang.String KEY_VIDEO_LEVEL = "android.media.mediarecorder.video-encoder-level";
+    field public static final java.lang.String KEY_VIDEO_PROFILE = "android.media.mediarecorder.video-encoder-profile";
+    field public static final java.lang.String KEY_VIDEO_TIMESCALE = "android.media.mediarecorder.video-timescale";
+    field public static final java.lang.String KEY_WIDTH = "android.media.mediarecorder.width";
+  }
+
   public final class MediaMuxer {
     ctor public MediaMuxer(java.lang.String, int) throws java.io.IOException;
     ctor public MediaMuxer(java.io.FileDescriptor, int) throws java.io.IOException;
@@ -22673,7 +22738,7 @@
     method public java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer.NoDrmSchemeException;
     method public int getDuration();
     method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
-    method public android.os.Bundle getMetrics();
+    method public android.media.MediaMetricsSet getMetrics();
     method public android.media.PlaybackParams getPlaybackParams();
     method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
     method public android.media.SyncParams getSyncParams();
@@ -22841,7 +22906,7 @@
     ctor public MediaRecorder();
     method public static final int getAudioSourceMax();
     method public int getMaxAmplitude() throws java.lang.IllegalStateException;
-    method public android.os.Bundle getMetrics();
+    method public android.media.MediaMetricsSet getMetrics();
     method public android.view.Surface getSurface();
     method public void pause() throws java.lang.IllegalStateException;
     method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
@@ -24652,7 +24717,7 @@
 
   public final class TvInputInfo implements android.os.Parcelable {
     method public boolean canRecord();
-    method public android.content.Intent createSettingsIntent();
+    method public deprecated android.content.Intent createSettingsIntent();
     method public android.content.Intent createSetupIntent();
     method public int describeContents();
     method public android.os.Bundle getExtras();
@@ -25193,8 +25258,8 @@
     method public void reportNetworkConnectivity(android.net.Network, boolean);
     method public boolean requestBandwidthUpdate(android.net.Network);
     method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
-    method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback);
     method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
+    method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback);
     method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
     method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
     method public deprecated void setNetworkPreference(int);
@@ -31917,6 +31982,7 @@
     method public boolean hasKey();
     method public boolean isEnabled();
     method public boolean isPersistent();
+    method public boolean isRecycleEnabled();
     method public boolean isSelectable();
     method protected void notifyChanged();
     method public void notifyDependencyChange(boolean);
@@ -31956,6 +32022,7 @@
     method public void setOrder(int);
     method public void setPersistent(boolean);
     method public void setPreferenceDataStore(android.preference.PreferenceDataStore);
+    method public void setRecycleEnabled(boolean);
     method public void setSelectable(boolean);
     method public void setShouldDisableView(boolean);
     method public void setSummary(java.lang.CharSequence);
@@ -33875,6 +33942,7 @@
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID";
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_NAME = "android.provider.extra.RECIPIENT_CONTACT_NAME";
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_URI = "android.provider.extra.RECIPIENT_CONTACT_URI";
+    field public static final java.lang.String EXTRA_SENDER_ACCOUNT_HASH = "android.provider.extra.SENDER_ACCOUNT_HASH";
     field public static final java.lang.String INVITE_CONTACT = "com.android.contacts.action.INVITE_CONTACT";
     field public static final java.lang.String METADATA_ACCOUNT_TYPE = "android.provider.account_type";
     field public static final java.lang.String METADATA_MIMETYPE = "android.provider.mimetype";
@@ -34647,6 +34715,7 @@
     field public static final java.lang.String ACTION_NFCSHARING_SETTINGS = "android.settings.NFCSHARING_SETTINGS";
     field public static final java.lang.String ACTION_NFC_PAYMENT_SETTINGS = "android.settings.NFC_PAYMENT_SETTINGS";
     field public static final java.lang.String ACTION_NFC_SETTINGS = "android.settings.NFC_SETTINGS";
+    field public static final java.lang.String ACTION_NIGHT_DISPLAY_SETTINGS = "android.settings.NIGHT_DISPLAY_SETTINGS";
     field public static final java.lang.String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS";
     field public static final java.lang.String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS";
@@ -36738,7 +36807,7 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
     method public void onDisconnected();
-    method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback);
+    method public void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
     method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillService";
     field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
@@ -36793,11 +36862,11 @@
   }
 
   public static final class SaveInfo.Builder {
-    ctor public SaveInfo.Builder(int);
-    method public android.service.autofill.SaveInfo.Builder addSavableIds(android.view.autofill.AutofillId...);
+    ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
     method public android.service.autofill.SaveInfo build();
     method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
     method public android.service.autofill.SaveInfo.Builder setNegativeAction(java.lang.CharSequence, android.content.IntentSender);
+    method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
   }
 
 }
@@ -39175,6 +39244,7 @@
     method public android.os.PersistableBundle getConfigForSubId(int);
     method public void notifyConfigChangedForSubId(int);
     field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+    field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
     field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
     field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
     field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
@@ -39217,6 +39287,8 @@
     field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
     field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
     field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
+    field public static final java.lang.String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
+    field public static final java.lang.String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
     field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
     field public static final java.lang.String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
     field public static final java.lang.String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
@@ -39272,6 +39344,7 @@
     field public static final java.lang.String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName";
     field public static final java.lang.String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
     field public static final java.lang.String KEY_MMS_USER_AGENT_STRING = "userAgent";
+    field public static final java.lang.String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
     field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
     field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
     field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
@@ -39804,7 +39877,6 @@
     method public android.os.PersistableBundle getCarrierConfig();
     method public android.telephony.CellLocation getCellLocation();
     method public int getDataActivity();
-    method public boolean getDataEnabled();
     method public int getDataNetworkType();
     method public int getDataState();
     method public java.lang.String getDeviceId();
@@ -39845,6 +39917,7 @@
     method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String);
     method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
     method public boolean isConcurrentVoiceAndDataAllowed();
+    method public boolean isDataEnabled();
     method public boolean isHearingAidCompatibilitySupported();
     method public boolean isNetworkRoaming();
     method public boolean isSmsCapable();
@@ -40882,11 +40955,10 @@
   }
 
   public final class FontConfig implements android.os.Parcelable {
-    ctor public FontConfig();
-    ctor public FontConfig(android.text.FontConfig);
+    ctor public FontConfig(android.text.FontConfig.Family[], android.text.FontConfig.Alias[]);
     method public int describeContents();
-    method public java.util.List<android.text.FontConfig.Alias> getAliases();
-    method public java.util.List<android.text.FontConfig.Family> getFamilies();
+    method public android.text.FontConfig.Alias[] getAliases();
+    method public android.text.FontConfig.Family[] getFamilies();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.text.FontConfig> CREATOR;
   }
@@ -40911,22 +40983,22 @@
   }
 
   public static final class FontConfig.Family implements android.os.Parcelable {
-    ctor public FontConfig.Family(java.lang.String, java.util.List<android.text.FontConfig.Font>, java.lang.String, java.lang.String);
-    ctor public FontConfig.Family(android.text.FontConfig.Family);
+    ctor public FontConfig.Family(java.lang.String, android.text.FontConfig.Font[], java.lang.String, int);
     method public int describeContents();
-    method public java.util.List<android.text.FontConfig.Font> getFonts();
+    method public android.text.FontConfig.Font[] getFonts();
     method public java.lang.String getLanguage();
     method public java.lang.String getName();
-    method public java.lang.String getVariant();
+    method public int getVariant();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.text.FontConfig.Family> CREATOR;
+    field public static final int VARIANT_COMPACT = 1; // 0x1
+    field public static final int VARIANT_DEFAULT = 0; // 0x0
+    field public static final int VARIANT_ELEGANT = 2; // 0x2
   }
 
   public static final class FontConfig.Font implements android.os.Parcelable {
-    ctor public FontConfig.Font(java.lang.String, int, java.util.List<android.text.FontConfig.Axis>, int, boolean);
-    ctor public FontConfig.Font(android.text.FontConfig.Font);
     method public int describeContents();
-    method public java.util.List<android.text.FontConfig.Axis> getAxes();
+    method public android.text.FontConfig.Axis[] getAxes();
     method public android.os.ParcelFileDescriptor getFd();
     method public java.lang.String getFontName();
     method public int getTtcIndex();
@@ -45095,7 +45167,7 @@
     method public float getAlpha();
     method public android.view.animation.Animation getAnimation();
     method public android.os.IBinder getApplicationWindowToken();
-    method public int getAutofillHint();
+    method public java.lang.String[] getAutofillHint();
     method public int getAutofillMode();
     method public int getAutofillType();
     method public android.view.autofill.AutofillValue getAutofillValue();
@@ -45416,7 +45488,7 @@
     method public void setActivated(boolean);
     method public void setAlpha(float);
     method public void setAnimation(android.view.animation.Animation);
-    method public void setAutofillHint(int);
+    method public void setAutofillHint(java.lang.String...);
     method public void setAutofillMode(int);
     method public void setBackground(android.graphics.drawable.Drawable);
     method public void setBackgroundColor(int);
@@ -45559,20 +45631,19 @@
     field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
     field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
     field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
-    field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE = 512; // 0x200
-    field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = 4096; // 0x1000
-    field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = 1024; // 0x400
-    field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = 2048; // 0x800
-    field public static final int AUTOFILL_HINT_CREDIT_CARD_NUMBER = 128; // 0x80
-    field public static final int AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = 256; // 0x100
-    field public static final int AUTOFILL_HINT_EMAIL_ADDRESS = 1; // 0x1
-    field public static final int AUTOFILL_HINT_NAME = 2; // 0x2
-    field public static final int AUTOFILL_HINT_NONE = 0; // 0x0
-    field public static final int AUTOFILL_HINT_PASSWORD = 8; // 0x8
-    field public static final int AUTOFILL_HINT_PHONE = 16; // 0x10
-    field public static final int AUTOFILL_HINT_POSTAL_ADDRESS = 32; // 0x20
-    field public static final int AUTOFILL_HINT_POSTAL_CODE = 64; // 0x40
-    field public static final int AUTOFILL_HINT_USERNAME = 4; // 0x4
+    field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE = "creditCardExpirationDate";
+    field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = "creditCardExpirationDay";
+    field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = "creditCardExpirationMonth";
+    field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = "creditCardExpirationYear";
+    field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_NUMBER = "creditCardNumber";
+    field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = "creditCardSecurityCode";
+    field public static final java.lang.String AUTOFILL_HINT_EMAIL_ADDRESS = "emailAddress";
+    field public static final java.lang.String AUTOFILL_HINT_NAME = "name";
+    field public static final java.lang.String AUTOFILL_HINT_PASSWORD = "password";
+    field public static final java.lang.String AUTOFILL_HINT_PHONE = "phone";
+    field public static final java.lang.String AUTOFILL_HINT_POSTAL_ADDRESS = "postalAddress";
+    field public static final java.lang.String AUTOFILL_HINT_POSTAL_CODE = "postalCode";
+    field public static final java.lang.String AUTOFILL_HINT_USERNAME = "username";
     field public static final int AUTOFILL_MODE_AUTO = 1; // 0x1
     field public static final int AUTOFILL_MODE_INHERIT = 0; // 0x0
     field public static final int AUTOFILL_MODE_MANUAL = 2; // 0x2
@@ -46234,7 +46305,7 @@
     method public abstract void setAccessibilityFocused(boolean);
     method public abstract void setActivated(boolean);
     method public abstract void setAlpha(float);
-    method public abstract void setAutofillHint(int);
+    method public abstract void setAutofillHint(java.lang.String[]);
     method public abstract void setAutofillOptions(java.lang.String[]);
     method public abstract void setAutofillType(int);
     method public abstract void setAutofillValue(android.view.autofill.AutofillValue);
@@ -46245,6 +46316,7 @@
     method public abstract void setClickable(boolean);
     method public abstract void setContentDescription(java.lang.CharSequence);
     method public abstract void setContextClickable(boolean);
+    method public abstract void setDataIsSensitive(boolean);
     method public abstract void setDimens(int, int, int, int, int, int);
     method public abstract void setElevation(float);
     method public abstract void setEnabled(boolean);
@@ -46255,7 +46327,6 @@
     method public abstract void setInputType(int);
     method public abstract void setLongClickable(boolean);
     method public abstract void setOpaque(boolean);
-    method public abstract void setSanitized(boolean);
     method public abstract void setSelected(boolean);
     method public abstract void setText(java.lang.CharSequence);
     method public abstract void setText(java.lang.CharSequence, int, int);
@@ -47498,17 +47569,22 @@
   }
 
   public final class AutofillManager {
+    method public void cancel();
+    method public void commit();
+    method public boolean isEnabled();
+    method public void notifyValueChanged(android.view.View);
+    method public void notifyViewEntered(android.view.View);
+    method public void notifyViewExited(android.view.View);
+    method public void notifyVirtualValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
+    method public void notifyVirtualViewEntered(android.view.View, int, android.graphics.Rect);
+    method public void notifyVirtualViewExited(android.view.View, int);
     method public void registerCallback(android.view.autofill.AutofillManager.AutofillCallback);
-    method public void reset();
-    method public void startAutofillRequest(android.view.View);
-    method public void startAutofillRequestOnVirtualView(android.view.View, int, android.graphics.Rect);
-    method public void stopAutofillRequest(android.view.View);
-    method public void stopAutofillRequestOnVirtualView(android.view.View, int);
+    method public void requestAutofill(android.view.View);
+    method public void requestAutofill(android.view.View, int, android.graphics.Rect);
     method public void unregisterCallback(android.view.autofill.AutofillManager.AutofillCallback);
-    method public void valueChanged(android.view.View);
-    method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
     field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
     field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
+    field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1
   }
 
   public static abstract class AutofillManager.AutofillCallback {
@@ -47529,6 +47605,10 @@
     method public int getListValue();
     method public java.lang.CharSequence getTextValue();
     method public boolean getToggleValue();
+    method public boolean isDate();
+    method public boolean isList();
+    method public boolean isText();
+    method public boolean isToggle();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.autofill.AutofillValue> CREATOR;
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index 58e37a0..6d01c6b 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -864,6 +864,7 @@
     field public static final int isModifier = 16843334; // 0x1010246
     field public static final int isRepeatable = 16843336; // 0x1010248
     field public static final int isScrollContainer = 16843342; // 0x101024e
+    field public static final int isStatic = 16844125; // 0x101055d
     field public static final int isSticky = 16843335; // 0x1010247
     field public static final int isolatedProcess = 16843689; // 0x10103a9
     field public static final int isolatedSplits = 16844109; // 0x101054d
@@ -1165,6 +1166,7 @@
     field public static final int ratingBarStyleSmall = 16842877; // 0x101007d
     field public static final int readPermission = 16842759; // 0x1010007
     field public static final int recognitionService = 16843932; // 0x101049c
+    field public static final int recycleEnabled = 16844124; // 0x101055c
     field public static final int relinquishTaskIdentity = 16843894; // 0x1010476
     field public static final int reparent = 16843964; // 0x10104bc
     field public static final int reparentWithOverlay = 16843965; // 0x10104bd
@@ -1905,6 +1907,7 @@
     field public static final int accessibilityActionSetProgress = 16908349; // 0x102003d
     field public static final int accessibilityActionShowOnScreen = 16908342; // 0x1020036
     field public static final int addToDictionary = 16908330; // 0x102002a
+    field public static final int autofill = 16908355; // 0x1020043
     field public static final int background = 16908288; // 0x1020000
     field public static final int button1 = 16908313; // 0x1020019
     field public static final int button2 = 16908314; // 0x102001a
@@ -5678,7 +5681,6 @@
 
   public final class NotificationChannel implements android.os.Parcelable {
     ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
-    ctor public NotificationChannel(java.lang.String, int, int);
     ctor protected NotificationChannel(android.os.Parcel);
     method public boolean canBypassDnd();
     method public boolean canShowBadge();
@@ -5692,19 +5694,17 @@
     method public int getLightColor();
     method public int getLockscreenVisibility();
     method public java.lang.CharSequence getName();
-    method public int getNameResId();
     method public android.net.Uri getSound();
     method public int getUserLockedFields();
     method public long[] getVibrationPattern();
     method public boolean isDeleted();
-    method public void lockFields(int);
     method public void populateFromXml(org.xmlpull.v1.XmlPullParser);
     method public void setBypassDnd(boolean);
-    method public void setDeleted(boolean);
     method public void setGroup(java.lang.String);
     method public void setImportance(int);
     method public void setLightColor(int);
     method public void setLockscreenVisibility(int);
+    method public void setName(java.lang.CharSequence);
     method public void setShowBadge(boolean);
     method public void setSound(android.net.Uri, android.media.AudioAttributes);
     method public void setVibrationPattern(long[]);
@@ -5729,18 +5729,14 @@
 
   public final class NotificationChannelGroup implements android.os.Parcelable {
     ctor public NotificationChannelGroup(java.lang.String, java.lang.CharSequence);
-    ctor public NotificationChannelGroup(java.lang.String, int);
     ctor protected NotificationChannelGroup(android.os.Parcel);
-    method public void addChannel(android.app.NotificationChannel);
     method public android.app.NotificationChannelGroup clone();
     method public int describeContents();
     method public java.util.List<android.app.NotificationChannel> getChannels();
     method public java.lang.String getId();
     method public java.lang.CharSequence getName();
-    method public int getNameResId();
     method public org.json.JSONObject toJson() throws org.json.JSONException;
     method public void writeToParcel(android.os.Parcel, int);
-    method public void writeXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
     field public static final android.os.Parcelable.Creator<android.app.NotificationChannelGroup> CREATOR;
   }
 
@@ -5755,12 +5751,14 @@
     method public void createNotificationChannelGroups(java.util.List<android.app.NotificationChannelGroup>);
     method public void createNotificationChannels(java.util.List<android.app.NotificationChannel>);
     method public void deleteNotificationChannel(java.lang.String);
+    method public void deleteNotificationChannelGroup(java.lang.String);
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
     method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String);
     method public java.util.Map<java.lang.String, android.app.AutomaticZenRule> getAutomaticZenRules();
     method public final int getCurrentInterruptionFilter();
     method public int getImportance();
     method public android.app.NotificationChannel getNotificationChannel(java.lang.String);
+    method public java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups();
     method public java.util.List<android.app.NotificationChannel> getNotificationChannels();
     method public android.app.NotificationManager.Policy getNotificationPolicy();
     method public boolean isNotificationPolicyAccessGranted();
@@ -6815,7 +6813,7 @@
 
   public static class AssistStructure.ViewNode {
     method public float getAlpha();
-    method public int getAutoFillHint();
+    method public java.lang.String[] getAutoFillHint();
     method public android.view.autofill.AutofillId getAutofillId();
     method public java.lang.String[] getAutofillOptions();
     method public int getAutofillType();
@@ -11240,6 +11238,7 @@
     method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
     method public abstract boolean setInstantAppCookie(byte[]);
+    method public abstract void setUpdateAvailable(java.lang.String, boolean);
     method public abstract boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
     method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
     method public abstract void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
@@ -23621,7 +23620,7 @@
     method public deprecated java.nio.ByteBuffer[] getInputBuffers();
     method public final android.media.MediaFormat getInputFormat();
     method public android.media.Image getInputImage(int);
-    method public android.os.Bundle getMetrics();
+    method public android.media.MediaMetricsSet getMetrics();
     method public final java.lang.String getName();
     method public java.nio.ByteBuffer getOutputBuffer(int);
     method public deprecated java.nio.ByteBuffer[] getOutputBuffers();
@@ -24173,7 +24172,7 @@
     method public boolean advance();
     method public long getCachedDuration();
     method public android.media.DrmInitData getDrmInitData();
-    method public android.os.Bundle getMetrics();
+    method public android.media.MediaMetricsSet getMetrics();
     method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
     method public boolean getSampleCryptoInfo(android.media.MediaCodec.CryptoInfo);
     method public int getSampleFlags();
@@ -24428,6 +24427,69 @@
     field public static final int OPTION_PREVIOUS_SYNC = 0; // 0x0
   }
 
+  public final class MediaMetricsSet {
+    method public double getDouble(java.lang.String, double);
+    method public int getInt(java.lang.String, int);
+    method public long getLong(java.lang.String, long);
+    method public java.lang.String getString(java.lang.String, java.lang.String);
+    method public boolean isEmpty();
+    method public java.util.Set<java.lang.String> keySet();
+    method public int size();
+  }
+
+  public static final class MediaMetricsSet.MediaCodec {
+    field public static final java.lang.String KEY_CODEC = "android.media.mediacodec.codec";
+    field public static final java.lang.String KEY_ENCODER = "android.media.mediacodec.encoder";
+    field public static final java.lang.String KEY_HEIGHT = "android.media.mediacodec.height";
+    field public static final java.lang.String KEY_MIME = "android.media.mediacodec.mime";
+    field public static final java.lang.String KEY_MODE = "android.media.mediacodec.mode";
+    field public static final java.lang.String KEY_ROTATION = "android.media.mediacodec.rotation";
+    field public static final java.lang.String KEY_SECURE = "android.media.mediacodec.secure";
+    field public static final java.lang.String KEY_WIDTH = "android.media.mediacodec.width";
+    field public static final java.lang.String MODE_AUDIO = "audio";
+    field public static final java.lang.String MODE_VIDEO = "video";
+  }
+
+  public static final class MediaMetricsSet.MediaExtractor {
+    field public static final java.lang.String KEY_FORMAT = "android.media.mediaextractor.fmt";
+    field public static final java.lang.String KEY_MIME = "android.media.mediaextractor.mime";
+    field public static final java.lang.String KEY_TRACKS = "android.media.mediaextractor.ntrk";
+  }
+
+  public static final class MediaMetricsSet.MediaPlayer {
+    field public static final java.lang.String KEY_CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
+    field public static final java.lang.String KEY_CODEC_VIDEO = "android.media.mediaplayer.video.codec";
+    field public static final java.lang.String KEY_DURATION = "android.media.mediaplayer.durationMs";
+    field public static final java.lang.String KEY_ERRORS = "android.media.mediaplayer.err";
+    field public static final java.lang.String KEY_ERROR_CODE = "android.media.mediaplayer.errcode";
+    field public static final java.lang.String KEY_FRAMES = "android.media.mediaplayer.frames";
+    field public static final java.lang.String KEY_FRAMES_DROPPED = "android.media.mediaplayer.dropped";
+    field public static final java.lang.String KEY_HEIGHT = "android.media.mediaplayer.height";
+    field public static final java.lang.String KEY_MIME_AUDIO = "android.media.mediaplayer.audio.mime";
+    field public static final java.lang.String KEY_MIME_VIDEO = "android.media.mediaplayer.video.mime";
+    field public static final java.lang.String KEY_PLAYING = "android.media.mediaplayer.playingMs";
+    field public static final java.lang.String KEY_WIDTH = "android.media.mediaplayer.width";
+  }
+
+  public static final class MediaMetricsSet.MediaRecorder {
+    field public static final java.lang.String KEY_AUDIO_BITRATE = "android.media.mediarecorder.audio-bitrate";
+    field public static final java.lang.String KEY_AUDIO_CHANNELS = "android.media.mediarecorder.audio-channels";
+    field public static final java.lang.String KEY_AUDIO_SAMPLERATE = "android.media.mediarecorder.audio-samplerate";
+    field public static final java.lang.String KEY_AUDIO_TIMESCALE = "android.media.mediarecorder.audio-timescale";
+    field public static final java.lang.String KEY_CAPTURE_FPS = "android.media.mediarecorder.capture-fps";
+    field public static final java.lang.String KEY_CAPTURE_FPS_ENABLE = "android.media.mediarecorder.capture-fpsenable";
+    field public static final java.lang.String KEY_FRAMERATE = "android.media.mediarecorder.frame-rate";
+    field public static final java.lang.String KEY_HEIGHT = "android.media.mediarecorder.height";
+    field public static final java.lang.String KEY_MOVIE_TIMESCALE = "android.media.mediarecorder.movie-timescale";
+    field public static final java.lang.String KEY_ROTATION = "android.media.mediarecorder.rotation";
+    field public static final java.lang.String KEY_VIDEO_BITRATE = "android.media.mediarecorder.video-bitrate";
+    field public static final java.lang.String KEY_VIDEO_IFRAME_INTERVAL = "android.media.mediarecorder.video-iframe-interval";
+    field public static final java.lang.String KEY_VIDEO_LEVEL = "android.media.mediarecorder.video-encoder-level";
+    field public static final java.lang.String KEY_VIDEO_PROFILE = "android.media.mediarecorder.video-encoder-profile";
+    field public static final java.lang.String KEY_VIDEO_TIMESCALE = "android.media.mediarecorder.video-timescale";
+    field public static final java.lang.String KEY_WIDTH = "android.media.mediarecorder.width";
+  }
+
   public final class MediaMuxer {
     ctor public MediaMuxer(java.lang.String, int) throws java.io.IOException;
     ctor public MediaMuxer(java.io.FileDescriptor, int) throws java.io.IOException;
@@ -24468,7 +24530,7 @@
     method public java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer.NoDrmSchemeException;
     method public int getDuration();
     method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
-    method public android.os.Bundle getMetrics();
+    method public android.media.MediaMetricsSet getMetrics();
     method public android.media.PlaybackParams getPlaybackParams();
     method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
     method public android.media.SyncParams getSyncParams();
@@ -24636,7 +24698,7 @@
     ctor public MediaRecorder();
     method public static final int getAudioSourceMax();
     method public int getMaxAmplitude() throws java.lang.IllegalStateException;
-    method public android.os.Bundle getMetrics();
+    method public android.media.MediaMetricsSet getMetrics();
     method public android.view.Surface getSurface();
     method public void pause() throws java.lang.IllegalStateException;
     method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
@@ -26616,11 +26678,15 @@
     method public int describeContents();
     method public java.lang.String getAudioAddress();
     method public int getAudioType();
+    method public int getCableConnectionStatus();
     method public int getDeviceId();
     method public int getHdmiPortId();
     method public int getType();
     method public void readFromParcel(android.os.Parcel);
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CABLE_CONNECTION_STATUS_CONNECTED = 1; // 0x1
+    field public static final int CABLE_CONNECTION_STATUS_DISCONNECTED = 2; // 0x2
+    field public static final int CABLE_CONNECTION_STATUS_UNKNOWN = 0; // 0x0
     field public static final android.os.Parcelable.Creator<android.media.tv.TvInputHardwareInfo> CREATOR;
     field public static final int TV_INPUT_TYPE_COMPONENT = 6; // 0x6
     field public static final int TV_INPUT_TYPE_COMPOSITE = 3; // 0x3
@@ -26639,6 +26705,7 @@
     method public android.media.tv.TvInputHardwareInfo.Builder audioAddress(java.lang.String);
     method public android.media.tv.TvInputHardwareInfo.Builder audioType(int);
     method public android.media.tv.TvInputHardwareInfo build();
+    method public android.media.tv.TvInputHardwareInfo.Builder cableConnectionStatus(int);
     method public android.media.tv.TvInputHardwareInfo.Builder deviceId(int);
     method public android.media.tv.TvInputHardwareInfo.Builder hdmiPortId(int);
     method public android.media.tv.TvInputHardwareInfo.Builder type(int);
@@ -26646,7 +26713,7 @@
 
   public final class TvInputInfo implements android.os.Parcelable {
     method public boolean canRecord();
-    method public android.content.Intent createSettingsIntent();
+    method public deprecated android.content.Intent createSettingsIntent();
     method public android.content.Intent createSetupIntent();
     method public static deprecated android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.hardware.hdmi.HdmiDeviceInfo, java.lang.String, java.lang.String, android.net.Uri) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public static deprecated android.media.tv.TvInputInfo createTvInputInfo(android.content.Context, android.content.pm.ResolveInfo, android.hardware.hdmi.HdmiDeviceInfo, java.lang.String, int, android.graphics.drawable.Icon) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
@@ -27312,8 +27379,8 @@
     method public void reportNetworkConnectivity(android.net.Network, boolean);
     method public boolean requestBandwidthUpdate(android.net.Network);
     method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
-    method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback);
     method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
+    method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback);
     method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
     method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
     method public deprecated void setNetworkPreference(int);
@@ -27721,7 +27788,6 @@
     method public boolean clearScores() throws java.lang.SecurityException;
     method public void disableScoring() throws java.lang.SecurityException;
     method public java.lang.String getActiveScorerPackage();
-    method public android.net.RecommendationResult requestRecommendation(android.net.RecommendationRequest) throws java.lang.SecurityException;
     method public boolean setActiveScorer(java.lang.String) throws java.lang.SecurityException;
     method public boolean updateScores(android.net.ScoredNetwork[]) throws java.lang.SecurityException;
     field public static final java.lang.String ACTION_CHANGE_ACTIVE = "android.net.scoring.CHANGE_ACTIVE";
@@ -34766,6 +34832,7 @@
     method public boolean hasKey();
     method public boolean isEnabled();
     method public boolean isPersistent();
+    method public boolean isRecycleEnabled();
     method public boolean isSelectable();
     method protected void notifyChanged();
     method public void notifyDependencyChange(boolean);
@@ -34805,6 +34872,7 @@
     method public void setOrder(int);
     method public void setPersistent(boolean);
     method public void setPreferenceDataStore(android.preference.PreferenceDataStore);
+    method public void setRecycleEnabled(boolean);
     method public void setSelectable(boolean);
     method public void setShouldDisableView(boolean);
     method public void setSummary(java.lang.CharSequence);
@@ -36750,6 +36818,7 @@
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID";
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_NAME = "android.provider.extra.RECIPIENT_CONTACT_NAME";
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_URI = "android.provider.extra.RECIPIENT_CONTACT_URI";
+    field public static final java.lang.String EXTRA_SENDER_ACCOUNT_HASH = "android.provider.extra.SENDER_ACCOUNT_HASH";
     field public static final java.lang.String INVITE_CONTACT = "com.android.contacts.action.INVITE_CONTACT";
     field public static final java.lang.String METADATA_ACCOUNT_TYPE = "android.provider.account_type";
     field public static final java.lang.String METADATA_MIMETYPE = "android.provider.mimetype";
@@ -37656,6 +37725,7 @@
     field public static final java.lang.String ACTION_NFCSHARING_SETTINGS = "android.settings.NFCSHARING_SETTINGS";
     field public static final java.lang.String ACTION_NFC_PAYMENT_SETTINGS = "android.settings.NFC_PAYMENT_SETTINGS";
     field public static final java.lang.String ACTION_NFC_SETTINGS = "android.settings.NFC_SETTINGS";
+    field public static final java.lang.String ACTION_NIGHT_DISPLAY_SETTINGS = "android.settings.NIGHT_DISPLAY_SETTINGS";
     field public static final java.lang.String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS";
     field public static final java.lang.String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS";
@@ -39771,7 +39841,7 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
     method public void onDisconnected();
-    method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback);
+    method public void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
     method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillService";
     field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
@@ -39826,11 +39896,11 @@
   }
 
   public static final class SaveInfo.Builder {
-    ctor public SaveInfo.Builder(int);
-    method public android.service.autofill.SaveInfo.Builder addSavableIds(android.view.autofill.AutofillId...);
+    ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
     method public android.service.autofill.SaveInfo build();
     method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
     method public android.service.autofill.SaveInfo.Builder setNegativeAction(java.lang.CharSequence, android.content.IntentSender);
+    method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
   }
 
 }
@@ -42534,6 +42604,7 @@
     method public void notifyConfigChangedForSubId(int);
     method public void updateConfigForPhoneId(int, java.lang.String);
     field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+    field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
     field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
     field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
     field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
@@ -42576,6 +42647,8 @@
     field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
     field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
     field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
+    field public static final java.lang.String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
+    field public static final java.lang.String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
     field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
     field public static final java.lang.String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
     field public static final java.lang.String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
@@ -42631,6 +42704,7 @@
     field public static final java.lang.String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName";
     field public static final java.lang.String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
     field public static final java.lang.String KEY_MMS_USER_AGENT_STRING = "userAgent";
+    field public static final java.lang.String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
     field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
     field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
     field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
@@ -43203,8 +43277,8 @@
     method public int getCurrentPhoneType();
     method public int getCurrentPhoneType(int);
     method public int getDataActivity();
-    method public boolean getDataEnabled();
-    method public boolean getDataEnabled(int);
+    method public deprecated boolean getDataEnabled();
+    method public deprecated boolean getDataEnabled(int);
     method public int getDataNetworkType();
     method public int getDataState();
     method public java.lang.String getDeviceId();
@@ -43251,6 +43325,7 @@
     method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
     method public boolean isConcurrentVoiceAndDataAllowed();
     method public boolean isDataConnectivityPossible();
+    method public boolean isDataEnabled();
     method public boolean isHearingAidCompatibilitySupported();
     method public boolean isIdle();
     method public boolean isNetworkRoaming();
@@ -44136,6 +44211,7 @@
     method public boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
     method public boolean setInstantAppCookie(byte[]);
+    method public void setUpdateAvailable(java.lang.String, boolean);
     method public boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
     method public void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
     method public void verifyIntentFilter(int, int, java.util.List<java.lang.String>);
@@ -44341,11 +44417,10 @@
   }
 
   public final class FontConfig implements android.os.Parcelable {
-    ctor public FontConfig();
-    ctor public FontConfig(android.text.FontConfig);
+    ctor public FontConfig(android.text.FontConfig.Family[], android.text.FontConfig.Alias[]);
     method public int describeContents();
-    method public java.util.List<android.text.FontConfig.Alias> getAliases();
-    method public java.util.List<android.text.FontConfig.Family> getFamilies();
+    method public android.text.FontConfig.Alias[] getAliases();
+    method public android.text.FontConfig.Family[] getFamilies();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.text.FontConfig> CREATOR;
   }
@@ -44370,22 +44445,22 @@
   }
 
   public static final class FontConfig.Family implements android.os.Parcelable {
-    ctor public FontConfig.Family(java.lang.String, java.util.List<android.text.FontConfig.Font>, java.lang.String, java.lang.String);
-    ctor public FontConfig.Family(android.text.FontConfig.Family);
+    ctor public FontConfig.Family(java.lang.String, android.text.FontConfig.Font[], java.lang.String, int);
     method public int describeContents();
-    method public java.util.List<android.text.FontConfig.Font> getFonts();
+    method public android.text.FontConfig.Font[] getFonts();
     method public java.lang.String getLanguage();
     method public java.lang.String getName();
-    method public java.lang.String getVariant();
+    method public int getVariant();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.text.FontConfig.Family> CREATOR;
+    field public static final int VARIANT_COMPACT = 1; // 0x1
+    field public static final int VARIANT_DEFAULT = 0; // 0x0
+    field public static final int VARIANT_ELEGANT = 2; // 0x2
   }
 
   public static final class FontConfig.Font implements android.os.Parcelable {
-    ctor public FontConfig.Font(java.lang.String, int, java.util.List<android.text.FontConfig.Axis>, int, boolean);
-    ctor public FontConfig.Font(android.text.FontConfig.Font);
     method public int describeContents();
-    method public java.util.List<android.text.FontConfig.Axis> getAxes();
+    method public android.text.FontConfig.Axis[] getAxes();
     method public android.os.ParcelFileDescriptor getFd();
     method public java.lang.String getFontName();
     method public int getTtcIndex();
@@ -48555,7 +48630,7 @@
     method public float getAlpha();
     method public android.view.animation.Animation getAnimation();
     method public android.os.IBinder getApplicationWindowToken();
-    method public int getAutofillHint();
+    method public java.lang.String[] getAutofillHint();
     method public int getAutofillMode();
     method public int getAutofillType();
     method public android.view.autofill.AutofillValue getAutofillValue();
@@ -48876,7 +48951,7 @@
     method public void setActivated(boolean);
     method public void setAlpha(float);
     method public void setAnimation(android.view.animation.Animation);
-    method public void setAutofillHint(int);
+    method public void setAutofillHint(java.lang.String...);
     method public void setAutofillMode(int);
     method public void setBackground(android.graphics.drawable.Drawable);
     method public void setBackgroundColor(int);
@@ -49019,20 +49094,19 @@
     field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
     field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
     field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
-    field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE = 512; // 0x200
-    field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = 4096; // 0x1000
-    field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = 1024; // 0x400
-    field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = 2048; // 0x800
-    field public static final int AUTOFILL_HINT_CREDIT_CARD_NUMBER = 128; // 0x80
-    field public static final int AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = 256; // 0x100
-    field public static final int AUTOFILL_HINT_EMAIL_ADDRESS = 1; // 0x1
-    field public static final int AUTOFILL_HINT_NAME = 2; // 0x2
-    field public static final int AUTOFILL_HINT_NONE = 0; // 0x0
-    field public static final int AUTOFILL_HINT_PASSWORD = 8; // 0x8
-    field public static final int AUTOFILL_HINT_PHONE = 16; // 0x10
-    field public static final int AUTOFILL_HINT_POSTAL_ADDRESS = 32; // 0x20
-    field public static final int AUTOFILL_HINT_POSTAL_CODE = 64; // 0x40
-    field public static final int AUTOFILL_HINT_USERNAME = 4; // 0x4
+    field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE = "creditCardExpirationDate";
+    field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = "creditCardExpirationDay";
+    field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = "creditCardExpirationMonth";
+    field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = "creditCardExpirationYear";
+    field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_NUMBER = "creditCardNumber";
+    field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = "creditCardSecurityCode";
+    field public static final java.lang.String AUTOFILL_HINT_EMAIL_ADDRESS = "emailAddress";
+    field public static final java.lang.String AUTOFILL_HINT_NAME = "name";
+    field public static final java.lang.String AUTOFILL_HINT_PASSWORD = "password";
+    field public static final java.lang.String AUTOFILL_HINT_PHONE = "phone";
+    field public static final java.lang.String AUTOFILL_HINT_POSTAL_ADDRESS = "postalAddress";
+    field public static final java.lang.String AUTOFILL_HINT_POSTAL_CODE = "postalCode";
+    field public static final java.lang.String AUTOFILL_HINT_USERNAME = "username";
     field public static final int AUTOFILL_MODE_AUTO = 1; // 0x1
     field public static final int AUTOFILL_MODE_INHERIT = 0; // 0x0
     field public static final int AUTOFILL_MODE_MANUAL = 2; // 0x2
@@ -49694,7 +49768,7 @@
     method public abstract void setAccessibilityFocused(boolean);
     method public abstract void setActivated(boolean);
     method public abstract void setAlpha(float);
-    method public abstract void setAutofillHint(int);
+    method public abstract void setAutofillHint(java.lang.String[]);
     method public abstract void setAutofillOptions(java.lang.String[]);
     method public abstract void setAutofillType(int);
     method public abstract void setAutofillValue(android.view.autofill.AutofillValue);
@@ -49705,6 +49779,7 @@
     method public abstract void setClickable(boolean);
     method public abstract void setContentDescription(java.lang.CharSequence);
     method public abstract void setContextClickable(boolean);
+    method public abstract void setDataIsSensitive(boolean);
     method public abstract void setDimens(int, int, int, int, int, int);
     method public abstract void setElevation(float);
     method public abstract void setEnabled(boolean);
@@ -49715,7 +49790,6 @@
     method public abstract void setInputType(int);
     method public abstract void setLongClickable(boolean);
     method public abstract void setOpaque(boolean);
-    method public abstract void setSanitized(boolean);
     method public abstract void setSelected(boolean);
     method public abstract void setText(java.lang.CharSequence);
     method public abstract void setText(java.lang.CharSequence, int, int);
@@ -50961,17 +51035,22 @@
   }
 
   public final class AutofillManager {
+    method public void cancel();
+    method public void commit();
+    method public boolean isEnabled();
+    method public void notifyValueChanged(android.view.View);
+    method public void notifyViewEntered(android.view.View);
+    method public void notifyViewExited(android.view.View);
+    method public void notifyVirtualValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
+    method public void notifyVirtualViewEntered(android.view.View, int, android.graphics.Rect);
+    method public void notifyVirtualViewExited(android.view.View, int);
     method public void registerCallback(android.view.autofill.AutofillManager.AutofillCallback);
-    method public void reset();
-    method public void startAutofillRequest(android.view.View);
-    method public void startAutofillRequestOnVirtualView(android.view.View, int, android.graphics.Rect);
-    method public void stopAutofillRequest(android.view.View);
-    method public void stopAutofillRequestOnVirtualView(android.view.View, int);
+    method public void requestAutofill(android.view.View);
+    method public void requestAutofill(android.view.View, int, android.graphics.Rect);
     method public void unregisterCallback(android.view.autofill.AutofillManager.AutofillCallback);
-    method public void valueChanged(android.view.View);
-    method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
     field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
     field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
+    field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1
   }
 
   public static abstract class AutofillManager.AutofillCallback {
@@ -50992,6 +51071,10 @@
     method public int getListValue();
     method public java.lang.CharSequence getTextValue();
     method public boolean getToggleValue();
+    method public boolean isDate();
+    method public boolean isList();
+    method public boolean isText();
+    method public boolean isToggle();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.autofill.AutofillValue> CREATOR;
   }
diff --git a/api/test-current.txt b/api/test-current.txt
index 7a9b53e..1d0a289 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -751,6 +751,7 @@
     field public static final int isModifier = 16843334; // 0x1010246
     field public static final int isRepeatable = 16843336; // 0x1010248
     field public static final int isScrollContainer = 16843342; // 0x101024e
+    field public static final int isStatic = 16844125; // 0x101055d
     field public static final int isSticky = 16843335; // 0x1010247
     field public static final int isolatedProcess = 16843689; // 0x10103a9
     field public static final int isolatedSplits = 16844109; // 0x101054d
@@ -1052,6 +1053,7 @@
     field public static final int ratingBarStyleSmall = 16842877; // 0x101007d
     field public static final int readPermission = 16842759; // 0x1010007
     field public static final int recognitionService = 16843932; // 0x101049c
+    field public static final int recycleEnabled = 16844124; // 0x101055c
     field public static final int relinquishTaskIdentity = 16843894; // 0x1010476
     field public static final int reparent = 16843964; // 0x10104bc
     field public static final int reparentWithOverlay = 16843965; // 0x10104bd
@@ -1788,6 +1790,7 @@
     field public static final int accessibilityActionSetProgress = 16908349; // 0x102003d
     field public static final int accessibilityActionShowOnScreen = 16908342; // 0x1020036
     field public static final int addToDictionary = 16908330; // 0x102002a
+    field public static final int autofill = 16908355; // 0x1020043
     field public static final int background = 16908288; // 0x1020000
     field public static final int button1 = 16908313; // 0x1020019
     field public static final int button2 = 16908314; // 0x102001a
@@ -5501,7 +5504,6 @@
 
   public final class NotificationChannel implements android.os.Parcelable {
     ctor public NotificationChannel(java.lang.String, java.lang.CharSequence, int);
-    ctor public NotificationChannel(java.lang.String, int, int);
     ctor protected NotificationChannel(android.os.Parcel);
     method public boolean canBypassDnd();
     method public boolean canShowBadge();
@@ -5515,7 +5517,6 @@
     method public int getLightColor();
     method public int getLockscreenVisibility();
     method public java.lang.CharSequence getName();
-    method public int getNameResId();
     method public android.net.Uri getSound();
     method public long[] getVibrationPattern();
     method public void setBypassDnd(boolean);
@@ -5523,6 +5524,7 @@
     method public void setImportance(int);
     method public void setLightColor(int);
     method public void setLockscreenVisibility(int);
+    method public void setName(java.lang.CharSequence);
     method public void setShowBadge(boolean);
     method public void setSound(android.net.Uri, android.media.AudioAttributes);
     method public void setVibrationPattern(long[]);
@@ -5535,14 +5537,12 @@
 
   public final class NotificationChannelGroup implements android.os.Parcelable {
     ctor public NotificationChannelGroup(java.lang.String, java.lang.CharSequence);
-    ctor public NotificationChannelGroup(java.lang.String, int);
     ctor protected NotificationChannelGroup(android.os.Parcel);
     method public android.app.NotificationChannelGroup clone();
     method public int describeContents();
     method public java.util.List<android.app.NotificationChannel> getChannels();
     method public java.lang.String getId();
     method public java.lang.CharSequence getName();
-    method public int getNameResId();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.NotificationChannelGroup> CREATOR;
   }
@@ -5558,6 +5558,7 @@
     method public void createNotificationChannelGroups(java.util.List<android.app.NotificationChannelGroup>);
     method public void createNotificationChannels(java.util.List<android.app.NotificationChannel>);
     method public void deleteNotificationChannel(java.lang.String);
+    method public void deleteNotificationChannelGroup(java.lang.String);
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
     method public android.app.AutomaticZenRule getAutomaticZenRule(java.lang.String);
     method public java.util.Map<java.lang.String, android.app.AutomaticZenRule> getAutomaticZenRules();
@@ -5565,6 +5566,7 @@
     method public android.content.ComponentName getEffectsSuppressor();
     method public int getImportance();
     method public android.app.NotificationChannel getNotificationChannel(java.lang.String);
+    method public java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups();
     method public java.util.List<android.app.NotificationChannel> getNotificationChannels();
     method public android.app.NotificationManager.Policy getNotificationPolicy();
     method public boolean isNotificationPolicyAccessGranted();
@@ -6592,7 +6594,7 @@
 
   public static class AssistStructure.ViewNode {
     method public float getAlpha();
-    method public int getAutoFillHint();
+    method public java.lang.String[] getAutoFillHint();
     method public android.view.autofill.AutofillId getAutofillId();
     method public java.lang.String[] getAutofillOptions();
     method public int getAutofillType();
@@ -21927,7 +21929,7 @@
     method public deprecated java.nio.ByteBuffer[] getInputBuffers();
     method public final android.media.MediaFormat getInputFormat();
     method public android.media.Image getInputImage(int);
-    method public android.os.Bundle getMetrics();
+    method public android.media.MediaMetricsSet getMetrics();
     method public final java.lang.String getName();
     method public java.nio.ByteBuffer getOutputBuffer(int);
     method public deprecated java.nio.ByteBuffer[] getOutputBuffers();
@@ -22479,7 +22481,7 @@
     method public boolean advance();
     method public long getCachedDuration();
     method public android.media.DrmInitData getDrmInitData();
-    method public android.os.Bundle getMetrics();
+    method public android.media.MediaMetricsSet getMetrics();
     method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
     method public boolean getSampleCryptoInfo(android.media.MediaCodec.CryptoInfo);
     method public int getSampleFlags();
@@ -22734,6 +22736,69 @@
     field public static final int OPTION_PREVIOUS_SYNC = 0; // 0x0
   }
 
+  public final class MediaMetricsSet {
+    method public double getDouble(java.lang.String, double);
+    method public int getInt(java.lang.String, int);
+    method public long getLong(java.lang.String, long);
+    method public java.lang.String getString(java.lang.String, java.lang.String);
+    method public boolean isEmpty();
+    method public java.util.Set<java.lang.String> keySet();
+    method public int size();
+  }
+
+  public static final class MediaMetricsSet.MediaCodec {
+    field public static final java.lang.String KEY_CODEC = "android.media.mediacodec.codec";
+    field public static final java.lang.String KEY_ENCODER = "android.media.mediacodec.encoder";
+    field public static final java.lang.String KEY_HEIGHT = "android.media.mediacodec.height";
+    field public static final java.lang.String KEY_MIME = "android.media.mediacodec.mime";
+    field public static final java.lang.String KEY_MODE = "android.media.mediacodec.mode";
+    field public static final java.lang.String KEY_ROTATION = "android.media.mediacodec.rotation";
+    field public static final java.lang.String KEY_SECURE = "android.media.mediacodec.secure";
+    field public static final java.lang.String KEY_WIDTH = "android.media.mediacodec.width";
+    field public static final java.lang.String MODE_AUDIO = "audio";
+    field public static final java.lang.String MODE_VIDEO = "video";
+  }
+
+  public static final class MediaMetricsSet.MediaExtractor {
+    field public static final java.lang.String KEY_FORMAT = "android.media.mediaextractor.fmt";
+    field public static final java.lang.String KEY_MIME = "android.media.mediaextractor.mime";
+    field public static final java.lang.String KEY_TRACKS = "android.media.mediaextractor.ntrk";
+  }
+
+  public static final class MediaMetricsSet.MediaPlayer {
+    field public static final java.lang.String KEY_CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
+    field public static final java.lang.String KEY_CODEC_VIDEO = "android.media.mediaplayer.video.codec";
+    field public static final java.lang.String KEY_DURATION = "android.media.mediaplayer.durationMs";
+    field public static final java.lang.String KEY_ERRORS = "android.media.mediaplayer.err";
+    field public static final java.lang.String KEY_ERROR_CODE = "android.media.mediaplayer.errcode";
+    field public static final java.lang.String KEY_FRAMES = "android.media.mediaplayer.frames";
+    field public static final java.lang.String KEY_FRAMES_DROPPED = "android.media.mediaplayer.dropped";
+    field public static final java.lang.String KEY_HEIGHT = "android.media.mediaplayer.height";
+    field public static final java.lang.String KEY_MIME_AUDIO = "android.media.mediaplayer.audio.mime";
+    field public static final java.lang.String KEY_MIME_VIDEO = "android.media.mediaplayer.video.mime";
+    field public static final java.lang.String KEY_PLAYING = "android.media.mediaplayer.playingMs";
+    field public static final java.lang.String KEY_WIDTH = "android.media.mediaplayer.width";
+  }
+
+  public static final class MediaMetricsSet.MediaRecorder {
+    field public static final java.lang.String KEY_AUDIO_BITRATE = "android.media.mediarecorder.audio-bitrate";
+    field public static final java.lang.String KEY_AUDIO_CHANNELS = "android.media.mediarecorder.audio-channels";
+    field public static final java.lang.String KEY_AUDIO_SAMPLERATE = "android.media.mediarecorder.audio-samplerate";
+    field public static final java.lang.String KEY_AUDIO_TIMESCALE = "android.media.mediarecorder.audio-timescale";
+    field public static final java.lang.String KEY_CAPTURE_FPS = "android.media.mediarecorder.capture-fps";
+    field public static final java.lang.String KEY_CAPTURE_FPS_ENABLE = "android.media.mediarecorder.capture-fpsenable";
+    field public static final java.lang.String KEY_FRAMERATE = "android.media.mediarecorder.frame-rate";
+    field public static final java.lang.String KEY_HEIGHT = "android.media.mediarecorder.height";
+    field public static final java.lang.String KEY_MOVIE_TIMESCALE = "android.media.mediarecorder.movie-timescale";
+    field public static final java.lang.String KEY_ROTATION = "android.media.mediarecorder.rotation";
+    field public static final java.lang.String KEY_VIDEO_BITRATE = "android.media.mediarecorder.video-bitrate";
+    field public static final java.lang.String KEY_VIDEO_IFRAME_INTERVAL = "android.media.mediarecorder.video-iframe-interval";
+    field public static final java.lang.String KEY_VIDEO_LEVEL = "android.media.mediarecorder.video-encoder-level";
+    field public static final java.lang.String KEY_VIDEO_PROFILE = "android.media.mediarecorder.video-encoder-profile";
+    field public static final java.lang.String KEY_VIDEO_TIMESCALE = "android.media.mediarecorder.video-timescale";
+    field public static final java.lang.String KEY_WIDTH = "android.media.mediarecorder.width";
+  }
+
   public final class MediaMuxer {
     ctor public MediaMuxer(java.lang.String, int) throws java.io.IOException;
     ctor public MediaMuxer(java.io.FileDescriptor, int) throws java.io.IOException;
@@ -22774,7 +22839,7 @@
     method public java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer.NoDrmSchemeException;
     method public int getDuration();
     method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
-    method public android.os.Bundle getMetrics();
+    method public android.media.MediaMetricsSet getMetrics();
     method public android.media.PlaybackParams getPlaybackParams();
     method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
     method public android.media.SyncParams getSyncParams();
@@ -22942,7 +23007,7 @@
     ctor public MediaRecorder();
     method public static final int getAudioSourceMax();
     method public int getMaxAmplitude() throws java.lang.IllegalStateException;
-    method public android.os.Bundle getMetrics();
+    method public android.media.MediaMetricsSet getMetrics();
     method public android.view.Surface getSurface();
     method public void pause() throws java.lang.IllegalStateException;
     method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
@@ -24753,7 +24818,7 @@
 
   public final class TvInputInfo implements android.os.Parcelable {
     method public boolean canRecord();
-    method public android.content.Intent createSettingsIntent();
+    method public deprecated android.content.Intent createSettingsIntent();
     method public android.content.Intent createSetupIntent();
     method public int describeContents();
     method public android.os.Bundle getExtras();
@@ -25294,8 +25359,8 @@
     method public void reportNetworkConnectivity(android.net.Network, boolean);
     method public boolean requestBandwidthUpdate(android.net.Network);
     method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
-    method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback);
     method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
+    method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback);
     method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
     method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
     method public deprecated void setNetworkPreference(int);
@@ -32041,6 +32106,7 @@
     method public boolean hasKey();
     method public boolean isEnabled();
     method public boolean isPersistent();
+    method public boolean isRecycleEnabled();
     method public boolean isSelectable();
     method protected void notifyChanged();
     method public void notifyDependencyChange(boolean);
@@ -32080,6 +32146,7 @@
     method public void setOrder(int);
     method public void setPersistent(boolean);
     method public void setPreferenceDataStore(android.preference.PreferenceDataStore);
+    method public void setRecycleEnabled(boolean);
     method public void setSelectable(boolean);
     method public void setShouldDisableView(boolean);
     method public void setSummary(java.lang.CharSequence);
@@ -34002,6 +34069,7 @@
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_CHAT_ID = "android.provider.extra.RECIPIENT_CONTACT_CHAT_ID";
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_NAME = "android.provider.extra.RECIPIENT_CONTACT_NAME";
     field public static final java.lang.String EXTRA_RECIPIENT_CONTACT_URI = "android.provider.extra.RECIPIENT_CONTACT_URI";
+    field public static final java.lang.String EXTRA_SENDER_ACCOUNT_HASH = "android.provider.extra.SENDER_ACCOUNT_HASH";
     field public static final java.lang.String INVITE_CONTACT = "com.android.contacts.action.INVITE_CONTACT";
     field public static final java.lang.String METADATA_ACCOUNT_TYPE = "android.provider.account_type";
     field public static final java.lang.String METADATA_MIMETYPE = "android.provider.mimetype";
@@ -34775,6 +34843,7 @@
     field public static final java.lang.String ACTION_NFCSHARING_SETTINGS = "android.settings.NFCSHARING_SETTINGS";
     field public static final java.lang.String ACTION_NFC_PAYMENT_SETTINGS = "android.settings.NFC_PAYMENT_SETTINGS";
     field public static final java.lang.String ACTION_NFC_SETTINGS = "android.settings.NFC_SETTINGS";
+    field public static final java.lang.String ACTION_NIGHT_DISPLAY_SETTINGS = "android.settings.NIGHT_DISPLAY_SETTINGS";
     field public static final java.lang.String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
     field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS";
     field public static final java.lang.String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS";
@@ -36882,7 +36951,7 @@
     method public final android.os.IBinder onBind(android.content.Intent);
     method public void onConnected();
     method public void onDisconnected();
-    method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback);
+    method public void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
     method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillService";
     field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
@@ -36937,11 +37006,11 @@
   }
 
   public static final class SaveInfo.Builder {
-    ctor public SaveInfo.Builder(int);
-    method public android.service.autofill.SaveInfo.Builder addSavableIds(android.view.autofill.AutofillId...);
+    ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
     method public android.service.autofill.SaveInfo build();
     method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
     method public android.service.autofill.SaveInfo.Builder setNegativeAction(java.lang.CharSequence, android.content.IntentSender);
+    method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
   }
 
 }
@@ -39364,6 +39433,7 @@
     method public android.os.PersistableBundle getConfigForSubId(int);
     method public void notifyConfigChangedForSubId(int);
     field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+    field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe
     field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
     field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
     field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
@@ -39406,6 +39476,8 @@
     field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
     field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
     field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
+    field public static final java.lang.String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
+    field public static final java.lang.String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG = "data_warning_threshold_bytes_long";
     field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string";
     field public static final java.lang.String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
     field public static final java.lang.String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
@@ -39461,6 +39533,7 @@
     field public static final java.lang.String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName";
     field public static final java.lang.String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
     field public static final java.lang.String KEY_MMS_USER_AGENT_STRING = "userAgent";
+    field public static final java.lang.String KEY_MONTHLY_DATA_CYCLE_DAY_INT = "monthly_data_cycle_day_int";
     field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
     field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
     field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
@@ -39993,7 +40066,6 @@
     method public android.os.PersistableBundle getCarrierConfig();
     method public android.telephony.CellLocation getCellLocation();
     method public int getDataActivity();
-    method public boolean getDataEnabled();
     method public int getDataNetworkType();
     method public int getDataState();
     method public java.lang.String getDeviceId();
@@ -40034,6 +40106,7 @@
     method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String);
     method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
     method public boolean isConcurrentVoiceAndDataAllowed();
+    method public boolean isDataEnabled();
     method public boolean isHearingAidCompatibilitySupported();
     method public boolean isNetworkRoaming();
     method public boolean isSmsCapable();
@@ -41075,11 +41148,10 @@
   }
 
   public final class FontConfig implements android.os.Parcelable {
-    ctor public FontConfig();
-    ctor public FontConfig(android.text.FontConfig);
+    ctor public FontConfig(android.text.FontConfig.Family[], android.text.FontConfig.Alias[]);
     method public int describeContents();
-    method public java.util.List<android.text.FontConfig.Alias> getAliases();
-    method public java.util.List<android.text.FontConfig.Family> getFamilies();
+    method public android.text.FontConfig.Alias[] getAliases();
+    method public android.text.FontConfig.Family[] getFamilies();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.text.FontConfig> CREATOR;
   }
@@ -41104,22 +41176,22 @@
   }
 
   public static final class FontConfig.Family implements android.os.Parcelable {
-    ctor public FontConfig.Family(java.lang.String, java.util.List<android.text.FontConfig.Font>, java.lang.String, java.lang.String);
-    ctor public FontConfig.Family(android.text.FontConfig.Family);
+    ctor public FontConfig.Family(java.lang.String, android.text.FontConfig.Font[], java.lang.String, int);
     method public int describeContents();
-    method public java.util.List<android.text.FontConfig.Font> getFonts();
+    method public android.text.FontConfig.Font[] getFonts();
     method public java.lang.String getLanguage();
     method public java.lang.String getName();
-    method public java.lang.String getVariant();
+    method public int getVariant();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.text.FontConfig.Family> CREATOR;
+    field public static final int VARIANT_COMPACT = 1; // 0x1
+    field public static final int VARIANT_DEFAULT = 0; // 0x0
+    field public static final int VARIANT_ELEGANT = 2; // 0x2
   }
 
   public static final class FontConfig.Font implements android.os.Parcelable {
-    ctor public FontConfig.Font(java.lang.String, int, java.util.List<android.text.FontConfig.Axis>, int, boolean);
-    ctor public FontConfig.Font(android.text.FontConfig.Font);
     method public int describeContents();
-    method public java.util.List<android.text.FontConfig.Axis> getAxes();
+    method public android.text.FontConfig.Axis[] getAxes();
     method public android.os.ParcelFileDescriptor getFd();
     method public java.lang.String getFontName();
     method public int getTtcIndex();
@@ -45455,7 +45527,7 @@
     method public float getAlpha();
     method public android.view.animation.Animation getAnimation();
     method public android.os.IBinder getApplicationWindowToken();
-    method public int getAutofillHint();
+    method public java.lang.String[] getAutofillHint();
     method public int getAutofillMode();
     method public int getAutofillType();
     method public android.view.autofill.AutofillValue getAutofillValue();
@@ -45779,7 +45851,7 @@
     method public void setActivated(boolean);
     method public void setAlpha(float);
     method public void setAnimation(android.view.animation.Animation);
-    method public void setAutofillHint(int);
+    method public void setAutofillHint(java.lang.String...);
     method public void setAutofillMode(int);
     method public void setBackground(android.graphics.drawable.Drawable);
     method public void setBackgroundColor(int);
@@ -45922,20 +45994,19 @@
     field public static final int ACCESSIBILITY_LIVE_REGION_NONE = 0; // 0x0
     field public static final int ACCESSIBILITY_LIVE_REGION_POLITE = 1; // 0x1
     field public static final android.util.Property<android.view.View, java.lang.Float> ALPHA;
-    field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE = 512; // 0x200
-    field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = 4096; // 0x1000
-    field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = 1024; // 0x400
-    field public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = 2048; // 0x800
-    field public static final int AUTOFILL_HINT_CREDIT_CARD_NUMBER = 128; // 0x80
-    field public static final int AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = 256; // 0x100
-    field public static final int AUTOFILL_HINT_EMAIL_ADDRESS = 1; // 0x1
-    field public static final int AUTOFILL_HINT_NAME = 2; // 0x2
-    field public static final int AUTOFILL_HINT_NONE = 0; // 0x0
-    field public static final int AUTOFILL_HINT_PASSWORD = 8; // 0x8
-    field public static final int AUTOFILL_HINT_PHONE = 16; // 0x10
-    field public static final int AUTOFILL_HINT_POSTAL_ADDRESS = 32; // 0x20
-    field public static final int AUTOFILL_HINT_POSTAL_CODE = 64; // 0x40
-    field public static final int AUTOFILL_HINT_USERNAME = 4; // 0x4
+    field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE = "creditCardExpirationDate";
+    field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = "creditCardExpirationDay";
+    field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = "creditCardExpirationMonth";
+    field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = "creditCardExpirationYear";
+    field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_NUMBER = "creditCardNumber";
+    field public static final java.lang.String AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = "creditCardSecurityCode";
+    field public static final java.lang.String AUTOFILL_HINT_EMAIL_ADDRESS = "emailAddress";
+    field public static final java.lang.String AUTOFILL_HINT_NAME = "name";
+    field public static final java.lang.String AUTOFILL_HINT_PASSWORD = "password";
+    field public static final java.lang.String AUTOFILL_HINT_PHONE = "phone";
+    field public static final java.lang.String AUTOFILL_HINT_POSTAL_ADDRESS = "postalAddress";
+    field public static final java.lang.String AUTOFILL_HINT_POSTAL_CODE = "postalCode";
+    field public static final java.lang.String AUTOFILL_HINT_USERNAME = "username";
     field public static final int AUTOFILL_MODE_AUTO = 1; // 0x1
     field public static final int AUTOFILL_MODE_INHERIT = 0; // 0x0
     field public static final int AUTOFILL_MODE_MANUAL = 2; // 0x2
@@ -46601,7 +46672,7 @@
     method public abstract void setAccessibilityFocused(boolean);
     method public abstract void setActivated(boolean);
     method public abstract void setAlpha(float);
-    method public abstract void setAutofillHint(int);
+    method public abstract void setAutofillHint(java.lang.String[]);
     method public abstract void setAutofillOptions(java.lang.String[]);
     method public abstract void setAutofillType(int);
     method public abstract void setAutofillValue(android.view.autofill.AutofillValue);
@@ -46612,6 +46683,7 @@
     method public abstract void setClickable(boolean);
     method public abstract void setContentDescription(java.lang.CharSequence);
     method public abstract void setContextClickable(boolean);
+    method public abstract void setDataIsSensitive(boolean);
     method public abstract void setDimens(int, int, int, int, int, int);
     method public abstract void setElevation(float);
     method public abstract void setEnabled(boolean);
@@ -46622,7 +46694,6 @@
     method public abstract void setInputType(int);
     method public abstract void setLongClickable(boolean);
     method public abstract void setOpaque(boolean);
-    method public abstract void setSanitized(boolean);
     method public abstract void setSelected(boolean);
     method public abstract void setText(java.lang.CharSequence);
     method public abstract void setText(java.lang.CharSequence, int, int);
@@ -47867,17 +47938,22 @@
   }
 
   public final class AutofillManager {
+    method public void cancel();
+    method public void commit();
+    method public boolean isEnabled();
+    method public void notifyValueChanged(android.view.View);
+    method public void notifyViewEntered(android.view.View);
+    method public void notifyViewExited(android.view.View);
+    method public void notifyVirtualValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
+    method public void notifyVirtualViewEntered(android.view.View, int, android.graphics.Rect);
+    method public void notifyVirtualViewExited(android.view.View, int);
     method public void registerCallback(android.view.autofill.AutofillManager.AutofillCallback);
-    method public void reset();
-    method public void startAutofillRequest(android.view.View);
-    method public void startAutofillRequestOnVirtualView(android.view.View, int, android.graphics.Rect);
-    method public void stopAutofillRequest(android.view.View);
-    method public void stopAutofillRequestOnVirtualView(android.view.View, int);
+    method public void requestAutofill(android.view.View);
+    method public void requestAutofill(android.view.View, int, android.graphics.Rect);
     method public void unregisterCallback(android.view.autofill.AutofillManager.AutofillCallback);
-    method public void valueChanged(android.view.View);
-    method public void virtualValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
     field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
     field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
+    field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1
   }
 
   public static abstract class AutofillManager.AutofillCallback {
@@ -47898,6 +47974,10 @@
     method public int getListValue();
     method public java.lang.CharSequence getTextValue();
     method public boolean getToggleValue();
+    method public boolean isDate();
+    method public boolean isList();
+    method public boolean isText();
+    method public boolean isToggle();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.autofill.AutofillValue> CREATOR;
   }
diff --git a/cmds/idmap/Android.mk b/cmds/idmap/Android.mk
index 50ccb07..aeb8a0c 100644
--- a/cmds/idmap/Android.mk
+++ b/cmds/idmap/Android.mk
@@ -17,7 +17,7 @@
 
 LOCAL_SRC_FILES := idmap.cpp create.cpp scan.cpp inspect.cpp
 
-LOCAL_SHARED_LIBRARIES := liblog libutils libandroidfw
+LOCAL_SHARED_LIBRARIES := liblog libutils libandroidfw libcutils
 
 LOCAL_MODULE := idmap
 
diff --git a/cmds/idmap/idmap.cpp b/cmds/idmap/idmap.cpp
index 3ab1915..3a237ff 100644
--- a/cmds/idmap/idmap.cpp
+++ b/cmds/idmap/idmap.cpp
@@ -49,8 +49,8 @@
       --path: create idmap for target package 'target' (path to apk) and overlay package \n\
               'overlay' (path to apk); write results to 'idmap' (path). \n\
 \n\
-      --scan: non-recursively search directory 'dir-to-scan' (path) for overlay packages with \n\
-              target package 'target-package-name-to-look-for' (package name) present at\n\
+      --scan: non-recursively search directory 'dir-to-scan' (path) for static overlay packages \n\
+              with target package 'target-package-name-to-look-for' (package name) present at\n\
               'path-to-target-apk' (path to apk). For each overlay package found, create an\n\
               idmap file in 'dir-to-hold-idmaps' (path). \n\
 \n\
diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp
index 8122395..67874a8 100644
--- a/cmds/idmap/scan.cpp
+++ b/cmds/idmap/scan.cpp
@@ -9,6 +9,7 @@
 #include <androidfw/ResourceTypes.h>
 #include <androidfw/StreamingZipInflater.h>
 #include <androidfw/ZipFileRO.h>
+#include <cutils/jstring.h>
 #include <private/android_filesystem_config.h> // for AID_SYSTEM
 #include <utils/SortedVector.h>
 #include <utils/String16.h>
@@ -81,7 +82,8 @@
         return String8(tmp);
     }
 
-    int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name)
+    int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name,
+            bool* is_static_overlay)
     {
         const size_t N = parser.getAttributeCount();
         String16 target;
@@ -102,6 +104,11 @@
                         return -1;
                     }
                 }
+            } else if (key == String16("isStatic")) {
+                Res_value v;
+                if (parser.getAttributeValue(i, &v) == sizeof(Res_value)) {
+                    *is_static_overlay = (v.data != 0);
+                }
             }
         }
         if (target == String16(target_package_name)) {
@@ -110,6 +117,28 @@
         return NO_OVERLAY_TAG;
     }
 
+    String16 parse_package_name(const ResXMLTree& parser)
+    {
+        const size_t N = parser.getAttributeCount();
+        String16 package_name;
+        for (size_t i = 0; i < N; ++i) {
+            size_t len;
+            String16 key(parser.getAttributeName(i, &len));
+            if (key == String16("package")) {
+                const char16_t *p = parser.getAttributeStringValue(i, &len);
+                if (p != NULL) {
+                    package_name = String16(p, len);
+                }
+            }
+        }
+        return package_name;
+    }
+
+    bool isValidStaticOverlayPackage(const String16& package_name) {
+        // TODO(b/35742444): Need to support selection method based on a package name.
+        return package_name.size() > 0;
+    }
+
     int parse_manifest(const void *data, size_t size, const char *target_package_name)
     {
         ResXMLTree parser;
@@ -120,17 +149,26 @@
         }
 
         ResXMLParser::event_code_t type;
+        String16 package_name;
+        bool is_static_overlay = false;
+        int priority = NO_OVERLAY_TAG;
         do {
             type = parser.next();
             if (type == ResXMLParser::START_TAG) {
                 size_t len;
                 String16 tag(parser.getElementName(&len));
-                if (tag == String16("overlay")) {
-                    return parse_overlay_tag(parser, target_package_name);
+                if (tag == String16("manifest")) {
+                    package_name = parse_package_name(parser);
+                } else if (tag == String16("overlay")) {
+                    priority = parse_overlay_tag(parser, target_package_name, &is_static_overlay);
+                    break;
                 }
             }
         } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);
 
+        if (is_static_overlay && isValidStaticOverlayPackage(package_name)) {
+            return priority;
+        }
         return NO_OVERLAY_TAG;
     }
 
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index e5df278..75d4f32 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -21,8 +21,11 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
 import android.annotation.Size;
 import android.annotation.SystemApi;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.BroadcastBehavior;
 import android.app.Activity;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -335,6 +338,8 @@
      *
      * @deprecated use #addOnAccountsUpdatedListener to get account updates in runtime.
      */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(includeBackground = true)
     public static final String LOGIN_ACCOUNTS_CHANGED_ACTION =
         "android.accounts.LOGIN_ACCOUNTS_CHANGED";
 
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 86adbb0..5c7a12c 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -431,31 +431,28 @@
 
     // Force all the animations to end when the duration scale is 0.
     private void forceToEnd() {
-        if (mEndCanBeCalled) {
-            end();
+        // TODO: Below is commented out to temp work around b/36241584, uncomment this when it's
+        // fixed.
+//        if (mEndCanBeCalled) {
+//            end();
+//            return;
+//        }
+
+        // Note: we don't want to combine this case with the end() method below because in
+        // the case of developer calling end(), we still need to make sure end() is explicitly
+        // called on the child animators to maintain the old behavior.
+        if (mReversing) {
+            handleAnimationEvents(mLastEventId, 0, getTotalDuration());
         } else {
-            // Note: we don't want to combine this case with the end() method below because in
-            // the case of developer calling end(), we still need to make sure end() is explicitly
-            // called on the child animators to maintain the old behavior.
-            if (mReversing) {
-                mLastEventId = mLastEventId == -1 ? mEvents.size() : mLastEventId;
-                for (int j = mLastEventId - 1; j >= 0; j--) {
-                    AnimationEvent event = mEvents.get(j);
-                    if (event.mEvent == AnimationEvent.ANIMATION_END) {
-                        event.mNode.mAnimation.reverse();
-                    }
-                }
-            } else {
-                for (int j = mLastEventId + 1; j < mEvents.size(); j++) {
-                    AnimationEvent event = mEvents.get(j);
-                    if (event.mEvent == AnimationEvent.ANIMATION_START) {
-                        event.mNode.mAnimation.start();
-                    }
-                }
+            long zeroScalePlayTime = getTotalDuration();
+            if (zeroScalePlayTime == DURATION_INFINITE) {
+                // Use a large number for the play time.
+                zeroScalePlayTime = Integer.MAX_VALUE;
             }
-            mPlayingSet.clear();
-            endAnimation();
+            handleAnimationEvents(mLastEventId, mEvents.size() - 1, zeroScalePlayTime);
         }
+        mPlayingSet.clear();
+        endAnimation();
     }
 
     /**
@@ -730,7 +727,7 @@
         if (isEmptySet) {
             // In the case of empty AnimatorSet, or 0 duration scale, we will trigger the
             // onAnimationEnd() right away.
-            forceToEnd();
+            end();
         }
     }
 
@@ -1130,8 +1127,10 @@
      */
     private void pulseFrame(Node node, long animPlayTime) {
         if (!node.mEnded) {
+            float durationScale = ValueAnimator.getDurationScale();
+            durationScale = durationScale == 0  ? 1 : durationScale;
             node.mEnded = node.mAnimation.pulseAnimationFrame(
-                    (long) (animPlayTime * ValueAnimator.getDurationScale()));
+                    (long) (animPlayTime * durationScale));
         }
     }
 
diff --git a/core/java/android/annotation/BroadcastBehavior.java b/core/java/android/annotation/BroadcastBehavior.java
new file mode 100644
index 0000000..9b2ca31
--- /dev/null
+++ b/core/java/android/annotation/BroadcastBehavior.java
@@ -0,0 +1,56 @@
+/*
+ * 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.annotation;
+
+import android.content.Intent;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Description of how the annotated broadcast action behaves.
+ *
+ * @hide
+ */
+@Target({ ElementType.FIELD })
+@Retention(RetentionPolicy.SOURCE)
+public @interface BroadcastBehavior {
+    /**
+     * This broadcast will only be delivered to an explicit target.
+     *
+     * @see Intent#setPackage(String)
+     * @see Intent#setComponent(android.content.ComponentName)
+     */
+    boolean explicitOnly() default false;
+
+    /**
+     * This broadcast will only be delivered to registered receivers.
+     *
+     * @see Intent#FLAG_RECEIVER_REGISTERED_ONLY
+     */
+    boolean registeredOnly() default false;
+
+    /**
+     * This broadcast will include all {@code AndroidManifest.xml} receivers
+     * regardless of process state.
+     *
+     * @see Intent#FLAG_RECEIVER_INCLUDE_BACKGROUND
+     */
+    boolean includeBackground() default false;
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 1969f8b..78c29e8 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1783,7 +1783,7 @@
         mTranslucentCallback = null;
         mCalled = true;
         if (isFinishing() && mAutoFillResetNeeded) {
-            getSystemService(AutofillManager.class).reset();
+            getSystemService(AutofillManager.class).commit();
         }
     }
 
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index fa64a0f..b36b664 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -25,6 +25,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.service.voice.IVoiceInteractionSession;
+import android.util.SparseIntArray;
 
 import com.android.internal.app.IVoiceInteractor;
 
@@ -47,9 +48,9 @@
 
     /**
      * Type for {@link #notifyAppTransitionStarting}: The transition was started because we drew
-     * the starting window.
+     * the splash screen.
      */
-    public static final int APP_TRANSITION_STARTING_WINDOW = 1;
+    public static final int APP_TRANSITION_SPLASH_SCREEN = 1;
 
     /**
      * Type for {@link #notifyAppTransitionStarting}: The transition was started because we all
@@ -64,6 +65,12 @@
     public static final int APP_TRANSITION_TIMEOUT = 3;
 
     /**
+     * Type for {@link #notifyAppTransitionStarting}: The transition was started because of a
+     * we drew a task snapshot.
+     */
+    public static final int APP_TRANSITION_SNAPSHOT = 4;
+
+    /**
      * Grant Uri permissions from one app to another. This method only extends
      * permission grants if {@code callingUid} has permission to them.
      */
@@ -122,19 +129,13 @@
             IVoiceInteractor mInteractor);
 
     /**
-     * Callback for window manager to let activity manager know that the starting window has been
-     * drawn
-     */
-    public abstract void notifyStartingWindowDrawn();
-
-    /**
      * Callback for window manager to let activity manager know that we are finally starting the
      * app transition;
      *
-     * @param reason The reason why the app transition started. Must be one of the APP_TRANSITION_*
-     *               values.
+     * @param reasons A map from stack id to a reason integer why the transition was started,, which
+     *                must be one of the APP_TRANSITION_* values.
      */
-    public abstract void notifyAppTransitionStarting(int reason);
+    public abstract void notifyAppTransitionStarting(SparseIntArray reasons);
 
     /**
      * Callback for window manager to let activity manager know that the app transition was
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 44cc5b4..5cdfb8e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -50,6 +50,7 @@
 import android.database.sqlite.SQLiteDebug.DbStats;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Typeface;
 import android.hardware.display.DisplayManagerGlobal;
 import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
@@ -5665,6 +5666,7 @@
         }
 
         // Preload fonts resources
+        Typeface.setApplicationContext(appContext);
         try {
             final ApplicationInfo info =
                     getPackageManager().getApplicationInfo(
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index a512350..21a7ca7 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -28,6 +28,7 @@
 import android.transition.TransitionSet;
 import android.transition.Visibility;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.view.GhostView;
 import android.view.View;
 import android.view.ViewGroup;
@@ -394,6 +395,60 @@
         return transition;
     }
 
+    /**
+     * Looks through the transition to see which Views have been included and which have been
+     * excluded. {@code views} will be modified to contain only those Views that are included
+     * in the transition. If {@code transition} is a TransitionSet, it will search through all
+     * contained Transitions to find targeted Views.
+     *
+     * @param transition The transition to look through for inclusion of Views
+     * @param views The list of Views that are to be checked for inclusion. Will be modified
+     *              to remove all excluded Views, possibly leaving an empty list.
+     */
+    protected static void removeExcludedViews(Transition transition, ArrayList<View> views) {
+        ArraySet<View> included = new ArraySet<>();
+        findIncludedViews(transition, views, included);
+        views.clear();
+        views.addAll(included);
+    }
+
+    /**
+     * Looks through the transition to see which Views have been included. Only {@code views}
+     * will be examined for inclusion. If {@code transition} is a TransitionSet, it will search
+     * through all contained Transitions to find targeted Views.
+     *
+     * @param transition The transition to look through for inclusion of Views
+     * @param views The list of Views that are to be checked for inclusion.
+     * @param included Modified to contain all Views in views that have at least one Transition
+     *                 that affects it.
+     */
+    private static void findIncludedViews(Transition transition, ArrayList<View> views,
+            ArraySet<View> included) {
+        if (transition instanceof TransitionSet) {
+            TransitionSet set = (TransitionSet) transition;
+            ArrayList<View> includedViews = new ArrayList<>();
+            final int numViews = views.size();
+            for (int i = 0; i < numViews; i++) {
+                final View view = views.get(i);
+                if (transition.isValidTarget(view)) {
+                    includedViews.add(view);
+                }
+            }
+            final int count = set.getTransitionCount();
+            for (int i = 0; i < count; i++) {
+                findIncludedViews(set.getTransitionAt(i), includedViews, included);
+            }
+        } else {
+            final int numViews = views.size();
+            for (int i = 0; i < numViews; i++) {
+                final View view = views.get(i);
+                if (transition.isValidTarget(view)) {
+                    included.add(view);
+                }
+            }
+        }
+    }
+
     protected static Transition mergeTransitions(Transition transition1, Transition transition2) {
         if (transition1 == null) {
             return transition2;
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 0f2ce3c..09e7595 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -362,6 +362,8 @@
     public static final String OPSTR_ANSWER_PHONE_CALLS
             = "android:answer_phone_calls";
 
+    // Warning: If an permission is added here it also has to be added to
+    // com.android.packageinstaller.permission.utils.EventLogger
     private static final int[] RUNTIME_AND_APPOP_PERMISSIONS_OPS = {
             // RUNTIME PERMISSIONS
             // Contacts
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 97992ca..55407e6 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1814,6 +1814,15 @@
     }
 
     @Override
+    public void setUpdateAvailable(String packageName, boolean updateAvailable) {
+        try {
+            mPM.setUpdateAvailable(packageName, updateAvailable);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
     public String getInstallerPackageName(String packageName) {
         try {
             return mPM.getInstallerPackageName(packageName);
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 445b687..ab847fd 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -132,7 +132,9 @@
         super.viewsReady(sharedElements);
         mIsReadyForTransition = true;
         hideViews(mSharedElements);
-        if (getViewsTransition() != null && mTransitioningViews != null) {
+        Transition viewsTransition = getViewsTransition();
+        if (viewsTransition != null && mTransitioningViews != null) {
+            removeExcludedViews(viewsTransition, mTransitioningViews);
             stripOffscreenViews();
             hideViews(mTransitioningViews);
         }
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 29e10d8..df31da9 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -321,6 +321,10 @@
         Transition viewsTransition = null;
         if (mTransitioningViews != null && !mTransitioningViews.isEmpty()) {
             viewsTransition = configureTransition(getViewsTransition(), true);
+            removeExcludedViews(viewsTransition, mTransitioningViews);
+            if (mTransitioningViews.isEmpty()) {
+                viewsTransition = null;
+            }
         }
         if (viewsTransition == null) {
             viewsTransitionComplete();
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 5ab767b..5ea2480 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -66,6 +66,9 @@
     ParceledListSlice getNotificationChannels(String pkg);
     ParceledListSlice getNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
     int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted);
+    int getDeletedChannelCount(String pkg, int uid);
+    void deleteNotificationChannelGroup(String pkg, String channelGroupId);
+    ParceledListSlice getNotificationChannelGroups(String pkg);
 
     // TODO: Remove this when callers have been migrated to the equivalent
     // INotificationListener method.
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index be38f42..77c4c7e 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -597,8 +597,7 @@
         // Avoid the binder call when the package is the current application package.
         // The activity manager will perform ensure that dexopt is performed before
         // spinning up the process.
-        if (!Objects.equals(mPackageName, ActivityThread.currentPackageName())) {
-            VMRuntime.getRuntime().vmInstructionSet();
+        if (!Objects.equals(mPackageName, ActivityThread.currentPackageName()) && mIncludeCode) {
             try {
                 ActivityThread.getPackageManager().notifyPackageUse(mPackageName,
                         PackageManager.NOTIFY_PACKAGE_USE_CROSS_PACKAGE);
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 16c85f5..29c4520 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -24,6 +24,7 @@
 import android.annotation.Nullable;
 import android.annotation.StringRes;
 import android.annotation.SystemApi;
+import android.content.Intent;
 import android.media.AudioAttributes;
 import android.net.Uri;
 import android.os.Parcel;
@@ -47,7 +48,6 @@
 
     private static final String TAG_CHANNEL = "channel";
     private static final String ATT_NAME = "name";
-    private static final String ATT_NAME_RES_ID = "name_res_id";
     private static final String ATT_ID = "id";
     private static final String ATT_DELETED = "deleted";
     private static final String ATT_PRIORITY = "priority";
@@ -141,7 +141,6 @@
 
     private final String mId;
     private CharSequence mName;
-    private int mNameResId = 0;
     private int mImportance = DEFAULT_IMPORTANCE;
     private boolean mBypassDnd;
     private int mLockscreenVisibility = DEFAULT_VISIBILITY;
@@ -162,7 +161,9 @@
      * @param id The id of the channel. Must be unique per package.
      * @param name The user visible name of the channel. Unchangeable once created; use this
      *             constructor if the channel represents a user-defined category that does not
-     *             need to be translated.
+     *             need to be translated. You can rename this channel when the system
+     *             locale changes by listening for the {@link Intent#ACTION_LOCALE_CHANGED}
+     *             broadcast.
      * @param importance The importance of the channel. This controls how interruptive notifications
      *                   posted to this channel are. See e.g.
      *                   {@link NotificationManager#IMPORTANCE_DEFAULT}.
@@ -173,21 +174,6 @@
         this.mImportance = importance;
     }
 
-    /**
-     * Creates a notification channel.
-     *
-     * @param id The id of the channel. Must be unique per package.
-     * @param nameResId The resource id of the string containing the channel name.
-     * @param importance The importance of the channel. This controls how interruptive notifications
-     *                   posted to this channel are. See e.g.
-     *                   {@link NotificationManager#IMPORTANCE_DEFAULT}.
-     */
-    public NotificationChannel(String id, @StringRes int nameResId, int importance) {
-        this.mId = id;
-        this.mNameResId = nameResId;
-        this.mImportance = importance;
-    }
-
     protected NotificationChannel(Parcel in) {
         if (in.readByte() != 0) {
             mId = in.readString();
@@ -195,7 +181,6 @@
             mId = null;
         }
         mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
-        mNameResId = in.readInt();
         mImportance = in.readInt();
         mBypassDnd = in.readByte() != 0;
         mLockscreenVisibility = in.readInt();
@@ -228,7 +213,6 @@
             dest.writeByte((byte) 0);
         }
         TextUtils.writeToParcel(mName, dest, flags);
-        dest.writeInt(mNameResId);
         dest.writeInt(mImportance);
         dest.writeByte(mBypassDnd ? (byte) 1 : (byte) 0);
         dest.writeInt(mLockscreenVisibility);
@@ -262,7 +246,6 @@
     /**
      * @hide
      */
-    @SystemApi
     public void lockFields(int field) {
         mUserLockedFields |= field;
     }
@@ -270,16 +253,15 @@
     /**
      * @hide
      */
-    @SystemApi
     public void setDeleted(boolean deleted) {
         mDeleted = deleted;
     }
 
     /**
-     * @hide
+     * Sets the name of this channel.
      */
-    public void setNameResId(@StringRes int nameResId) {
-        this.mNameResId = nameResId;
+    public void setName(CharSequence name) {
+        mName = name;
     }
 
     // Modifiable by a notification ranker.
@@ -417,13 +399,6 @@
     }
 
     /**
-     * Returns the resource id of the user visible name of this channel.
-     */
-    public int getNameResId() {
-        return mNameResId;
-    }
-
-    /**
      * Returns the user specified importance {e.g. @link NotificationManager#IMPORTANCE_LOW} for
      * notifications posted to this channel.
      */
@@ -556,7 +531,6 @@
         if (getName() != null) {
             out.attribute(null, ATT_NAME, getName().toString());
         }
-        out.attribute(null, ATT_NAME_RES_ID, Integer.toString(getNameResId()));
         if (getImportance() != DEFAULT_IMPORTANCE) {
             out.attribute(
                     null, ATT_IMPORTANCE, Integer.toString(getImportance()));
@@ -614,7 +588,6 @@
         JSONObject record = new JSONObject();
         record.put(ATT_ID, getId());
         record.put(ATT_NAME, getName());
-        record.put(ATT_NAME_RES_ID, getNameResId());
         if (getImportance() != DEFAULT_IMPORTANCE) {
             record.put(ATT_IMPORTANCE,
                     NotificationListenerService.Ranking.importanceToString(getImportance()));
@@ -732,7 +705,6 @@
 
         NotificationChannel that = (NotificationChannel) o;
 
-        if (getNameResId() != that.getNameResId()) return false;
         if (getImportance() != that.getImportance()) return false;
         if (mBypassDnd != that.mBypassDnd) return false;
         if (getLockscreenVisibility() != that.getLockscreenVisibility()) return false;
@@ -762,7 +734,6 @@
     public int hashCode() {
         int result = getId() != null ? getId().hashCode() : 0;
         result = 31 * result + (getName() != null ? getName().hashCode() : 0);
-        result = 31 * result + getNameResId();
         result = 31 * result + getImportance();
         result = 31 * result + (mBypassDnd ? 1 : 0);
         result = 31 * result + getLockscreenVisibility();
@@ -784,7 +755,6 @@
         return "NotificationChannel{" +
                 "mId='" + mId + '\'' +
                 ", mName=" + mName +
-                ", mNameResId=" + mNameResId +
                 ", mImportance=" + mImportance +
                 ", mBypassDnd=" + mBypassDnd +
                 ", mLockscreenVisibility=" + mLockscreenVisibility +
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 288d39a..2b0cd04 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -17,6 +17,7 @@
 
 import android.annotation.StringRes;
 import android.annotation.SystemApi;
+import android.content.Intent;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -41,38 +42,25 @@
 
     private static final String TAG_GROUP = "channelGroup";
     private static final String ATT_NAME = "name";
-    private static final String ATT_NAME_RES_ID = "name_res_id";
     private static final String ATT_ID = "id";
 
     private final String mId;
     private CharSequence mName;
-    private int mNameResId = 0;
     private List<NotificationChannel> mChannels = new ArrayList<>();
 
     /**
-     * Creates a notification channel.
+     * Creates a notification channel group.
      *
      * @param id The id of the group. Must be unique per package.
-     * @param name The user visible name of the group. Unchangeable once created; use this
-     *             constructor if the group represents something user-defined that does not
-     *             need to be translated.
+     * @param name The user visible name of the group. You can rename this group when the system
+     *             locale changes by listening for the {@link Intent#ACTION_LOCALE_CHANGED}
+     *             broadcast.
      */
     public NotificationChannelGroup(String id, CharSequence name) {
         this.mId = id;
         this.mName = name;
     }
 
-    /**
-     * Creates a notification channel.
-     *
-     * @param id The id of the group. Must be unique per package.
-     * @param nameResId String resource id of the user visible name of the group.
-     */
-    public NotificationChannelGroup(String id, @StringRes int nameResId) {
-        this.mId = id;
-        this.mNameResId = nameResId;
-    }
-
     protected NotificationChannelGroup(Parcel in) {
         if (in.readByte() != 0) {
             mId = in.readString();
@@ -80,7 +68,6 @@
             mId = null;
         }
         mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
-        mNameResId = in.readInt();
         in.readParcelableList(mChannels, NotificationChannel.class.getClassLoader());
     }
 
@@ -93,7 +80,6 @@
             dest.writeByte((byte) 0);
         }
         TextUtils.writeToParcel(mName, dest, flags);
-        dest.writeInt(mNameResId);
         dest.writeParcelableList(mChannels, flags);
     }
 
@@ -111,19 +97,11 @@
         return mName;
     }
 
-    /**
-     * Returns the resource id of the user visible name of this group.
-     */
-    public @StringRes int getNameResId() {
-        return mNameResId;
-    }
-
     /*
      * Returns the list of channels that belong to this group
      *
      * @hide
      */
-    @SystemApi
     public List<NotificationChannel> getChannels() {
         return mChannels;
     }
@@ -131,7 +109,6 @@
     /**
      * @hide
      */
-    @SystemApi
     public void addChannel(NotificationChannel channel) {
         mChannels.add(channel);
     }
@@ -139,7 +116,6 @@
     /**
      * @hide
      */
-    @SystemApi
     public void writeXml(XmlSerializer out) throws IOException {
         out.startTag(null, TAG_GROUP);
 
@@ -147,9 +123,6 @@
         if (getName() != null) {
             out.attribute(null, ATT_NAME, getName().toString());
         }
-        if (getNameResId() != 0) {
-            out.attribute(null, ATT_NAME_RES_ID, Integer.toString(getNameResId()));
-        }
 
         out.endTag(null, TAG_GROUP);
     }
@@ -162,7 +135,6 @@
         JSONObject record = new JSONObject();
         record.put(ATT_ID, getId());
         record.put(ATT_NAME, getName());
-        record.put(ATT_NAME_RES_ID, getNameResId());
         return record;
     }
 
@@ -191,31 +163,22 @@
 
         NotificationChannelGroup that = (NotificationChannelGroup) o;
 
-        if (getNameResId() != that.getNameResId()) return false;
         if (getId() != null ? !getId().equals(that.getId()) : that.getId() != null) return false;
         if (getName() != null ? !getName().equals(that.getName()) : that.getName() != null) {
             return false;
         }
-        return getChannels() != null ? getChannels().equals(that.getChannels())
-                : that.getChannels() == null;
-
+        return true;
     }
 
     @Override
     public NotificationChannelGroup clone() {
-        if (getName() != null) {
-            return new NotificationChannelGroup(getId(), getName());
-        } else {
-            return new NotificationChannelGroup(getId(), getNameResId());
-        }
+        return new NotificationChannelGroup(getId(), getName());
     }
 
     @Override
     public int hashCode() {
         int result = getId() != null ? getId().hashCode() : 0;
         result = 31 * result + (getName() != null ? getName().hashCode() : 0);
-        result = 31 * result + getNameResId();
-        result = 31 * result + (getChannels() != null ? getChannels().hashCode() : 0);
         return result;
     }
 
@@ -224,7 +187,6 @@
         return "NotificationChannelGroup{" +
                 "mId='" + mId + '\'' +
                 ", mName=" + mName +
-                ", mNameResId=" + mNameResId +
                 ", mChannels=" + mChannels +
                 '}';
     }
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 2296838..0379970 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -385,7 +385,7 @@
     /**
      * Creates a group container for {@link NotificationChannel} objects.
      *
-     * This is a no-op for groups that already exist.
+     * This can be used to rename an existing group.
      * <p>
      *     Group information is only used for presentation, not for behavior. Groups are optional
      *     for channels, and you can have a mix of channels that belong to groups and channels
@@ -421,21 +421,22 @@
     /**
      * Creates a notification channel that notifications can be posted to.
      *
-     * This is a no-op for channels that already exist.
+     * This can also be used to restore a deleted channel and to rename an existing channel. All
+     * other fields are ignored for channels that already exist.
      *
      * @param channel  the channel to create.  Note that the created channel may differ from this
      *                 value. If the provided channel is malformed, a RemoteException will be
-     *                 thrown. If the channel already exists, it will not be modified.
+     *                 thrown.
      */
     public void createNotificationChannel(@NonNull NotificationChannel channel) {
         createNotificationChannels(Arrays.asList(channel));
     }
 
     /**
-     * Creates multiple notification channels that different notifications can be posted to.
+     * Creates multiple notification channels that different notifications can be posted to. See
+     * {@link #createNotificationChannel(NotificationChannel)}.
      *
-     * @param channels the list of channels to attempt to create.  If any of these channels already
-     *                 exist, they will not be modified.
+     * @param channels the list of channels to attempt to create.
      */
     public void createNotificationChannels(@NonNull List<NotificationChannel> channels) {
         INotificationManager service = getService();
@@ -497,6 +498,30 @@
     }
 
     /**
+     * Returns all notification channel groups belonging to the calling app.
+     */
+    public List<NotificationChannelGroup> getNotificationChannelGroups() {
+        INotificationManager service = getService();
+        try {
+            return service.getNotificationChannelGroups(mContext.getPackageName()).getList();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Deletes the given notification channel group.
+     */
+    public void deleteNotificationChannelGroup(String groupId) {
+        INotificationManager service = getService();
+        try {
+            service.deleteNotificationChannelGroup(mContext.getPackageName(), groupId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * @hide
      */
     @TestApi
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index b219f2a..8a4f8a6 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -164,6 +164,15 @@
     @Override
     public void onClick(DialogInterface dialog, int which) {
         switch (which) {
+            case BUTTON_POSITIVE:
+                // Note this skips input validation and just uses the last valid time and hour
+                // entry. This will only be invoked programmatically. User clicks on BUTTON_POSITIVE
+                // are handled in show().
+                if (mTimeSetListener != null) {
+                    mTimeSetListener.onTimeSet(mTimePicker, mTimePicker.getCurrentHour(),
+                            mTimePicker.getCurrentMinute());
+                }
+                break;
             case BUTTON_NEGATIVE:
                 cancel();
                 break;
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 0fb5966..d9b6eed 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -17,6 +17,7 @@
 package android.app.admin;
 
 import android.accounts.AccountManager;
+import android.annotation.BroadcastBehavior;
 import android.annotation.IntDef;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -81,6 +82,7 @@
      * that other applications can not abuse it.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_DEVICE_ADMIN_ENABLED
             = "android.app.action.DEVICE_ADMIN_ENABLED";
 
@@ -94,6 +96,7 @@
      * to the user before they disable your admin.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED
             = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
 
@@ -115,6 +118,7 @@
      * its intent filter.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_DEVICE_ADMIN_DISABLED
             = "android.app.action.DEVICE_ADMIN_DISABLED";
 
@@ -131,6 +135,7 @@
      * this broadcast.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_PASSWORD_CHANGED
             = "android.app.action.ACTION_PASSWORD_CHANGED";
 
@@ -147,6 +152,7 @@
      * this broadcast.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_PASSWORD_FAILED
             = "android.app.action.ACTION_PASSWORD_FAILED";
 
@@ -160,6 +166,7 @@
      * this broadcast.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_PASSWORD_SUCCEEDED
             = "android.app.action.ACTION_PASSWORD_SUCCEEDED";
 
@@ -173,6 +180,7 @@
      * this broadcast.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_PASSWORD_EXPIRING
             = "android.app.action.ACTION_PASSWORD_EXPIRING";
 
@@ -187,6 +195,7 @@
      * @see DevicePolicyManager#isLockTaskPermitted(String)
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_LOCK_TASK_ENTERING
             = "android.app.action.LOCK_TASK_ENTERING";
 
@@ -200,6 +209,7 @@
      * @see DevicePolicyManager#isLockTaskPermitted(String)
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_LOCK_TASK_EXITING
             = "android.app.action.LOCK_TASK_EXITING";
 
@@ -232,6 +242,7 @@
      * <p>Output: Nothing</p>
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_PROFILE_PROVISIONING_COMPLETE =
             "android.app.action.PROFILE_PROVISIONING_COMPLETE";
 
@@ -244,6 +255,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_BUGREPORT_SHARING_DECLINED =
             "android.app.action.BUGREPORT_SHARING_DECLINED";
 
@@ -256,6 +268,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_BUGREPORT_FAILED = "android.app.action.BUGREPORT_FAILED";
 
     /**
@@ -266,6 +279,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_BUGREPORT_SHARE =
             "android.app.action.BUGREPORT_SHARE";
 
@@ -274,6 +288,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_SECURITY_LOGS_AVAILABLE
             = "android.app.action.SECURITY_LOGS_AVAILABLE";
 
@@ -283,6 +298,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_NETWORK_LOGS_AVAILABLE
             = "android.app.action.NETWORK_LOGS_AVAILABLE";
 
@@ -314,7 +330,8 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_USER_ADDED  = "android.app.action.USER_ADDED";
+    @BroadcastBehavior(explicitOnly = true)
+    public static final String ACTION_USER_ADDED = "android.app.action.USER_ADDED";
 
     /**
      * Broadcast action: notify the device owner that a user or profile has been removed.
@@ -323,6 +340,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_USER_REMOVED = "android.app.action.USER_REMOVED";
 
     /**
@@ -401,6 +419,7 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_NOTIFY_PENDING_SYSTEM_UPDATE =
             "android.app.action.NOTIFY_PENDING_SYSTEM_UPDATE";
 
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 1f2ed00..b1fbc8f 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -1,5 +1,6 @@
 package android.app.assist;
 
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.content.ComponentName;
 import android.graphics.Matrix;
@@ -590,7 +591,7 @@
         // fields (viewId and childId) of the field.
         AutofillId mAutofillId;
         @View.AutofillType int mAutofillType;
-        @View.AutofillHint int mAutofillHint;
+        @Nullable String[] mAutofillHint;
         AutofillValue mAutofillValue;
         String[] mAutofillOptions;
         boolean mSanitized;
@@ -676,7 +677,7 @@
                 mSanitized = in.readInt() == 1;
                 mAutofillId = in.readParcelable(null);
                 mAutofillType = in.readInt();
-                mAutofillHint = in.readInt();
+                mAutofillHint = in.readStringArray();
                 mAutofillValue = in.readParcelable(null);
                 mAutofillOptions = in.readStringArray();
             }
@@ -810,7 +811,7 @@
                 out.writeInt(mSanitized ? 1 : 0);
                 out.writeParcelable(mAutofillId, 0);
                 out.writeInt(mAutofillType);
-                out.writeInt(mAutofillHint);
+                out.writeStringArray(mAutofillHint);
                 final AutofillValue sanitizedValue = writeSensitive ? mAutofillValue : null;
                 out.writeParcelable(sanitizedValue,  0);
                 out.writeStringArray(mAutofillOptions);
@@ -949,7 +950,7 @@
          *
          * @return The hint for this view
          */
-        @View.AutofillHint public int getAutoFillHint() {
+        @Nullable public String[] getAutoFillHint() {
             return mAutofillHint;
         }
 
@@ -1012,9 +1013,8 @@
             mAutofillValue = value;
             // TODO(b/33197203, b/33802548): decide whether to set text as well (so it would work
             // with "legacy" views) or just the autofill value
-            final CharSequence text = value.getTextValue();
-            if (text != null) {
-                mText.mText = text;
+            if (value.isText()) {
+                mText.mText = value.getTextValue();
             }
         }
 
@@ -1663,7 +1663,7 @@
         }
 
         @Override
-        public void setAutofillHint(@View.AutofillHint int hint) {
+        public void setAutofillHint(@Nullable String[] hint) {
             mNode.mAutofillHint = hint;
         }
 
@@ -1683,8 +1683,8 @@
         }
 
         @Override
-        public void setSanitized(boolean sanitized) {
-            mNode.mSanitized = sanitized;
+        public void setDataIsSensitive(boolean sensitive) {
+            mNode.mSanitized = !sensitive;
         }
 
         @Override
@@ -1812,7 +1812,7 @@
                     + ", type=" + node.getAutofillType()
                     + ", options=" + Arrays.toString(node.getAutofillOptions())
                     + ", inputType=" + node.getInputType()
-                    + ", hint=" + Integer.toHexString(node.getAutoFillHint())
+                    + ", hint=" + Arrays.toString(node.getAutoFillHint())
                     + ", value=" + node.getAutofillValue()
                     + ", sanitized=" + node.isSanitized());
         }
diff --git a/core/java/android/app/usage/StorageStatsManager.java b/core/java/android/app/usage/StorageStatsManager.java
index 8276229..b808c2b 100644
--- a/core/java/android/app/usage/StorageStatsManager.java
+++ b/core/java/android/app/usage/StorageStatsManager.java
@@ -81,9 +81,9 @@
     /**
      * Return the free space on the requested storage volume.
      * <p>
-     * The free space is equivalent to {@link File#getFreeSpace()} plus the size
-     * of any cached data that can be automatically deleted by the system as
-     * additional space is needed.
+     * The free space is equivalent to {@link File#getUsableSpace()} plus the
+     * size of any cached data that can be automatically deleted by the system
+     * as additional space is needed.
      * <p>
      * This method may take several seconds to calculate the requested values,
      * so it should only be called from a worker thread.
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 67c791d..74a39e8 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -16,8 +16,11 @@
 
 package android.appwidget;
 
+import android.annotation.BroadcastBehavior;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -81,12 +84,14 @@
      *
      * @see #ACTION_APPWIDGET_CONFIGURE
      */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_APPWIDGET_PICK = "android.appwidget.action.APPWIDGET_PICK";
 
     /**
      * Similar to ACTION_APPWIDGET_PICK, but used from keyguard
      * @hide
      */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String
             ACTION_KEYGUARD_APPWIDGET_PICK = "android.appwidget.action.KEYGUARD_APPWIDGET_PICK";
 
@@ -133,6 +138,7 @@
      * @see #ACTION_APPWIDGET_CONFIGURE
      *
      */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_APPWIDGET_BIND = "android.appwidget.action.APPWIDGET_BIND";
 
     /**
@@ -157,6 +163,7 @@
      * and not display this AppWidget, and you will receive a {@link #ACTION_APPWIDGET_DELETED}
      * broadcast.
      */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_APPWIDGET_CONFIGURE = "android.appwidget.action.APPWIDGET_CONFIGURE";
 
     /**
@@ -290,6 +297,8 @@
      *
      * @see AppWidgetProvider#onUpdate AppWidgetProvider.onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
      */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE";
 
     /**
@@ -302,6 +311,8 @@
      *      AppWidgetProvider.onAppWidgetOptionsChanged(Context context,
      *      AppWidgetManager appWidgetManager, int appWidgetId, Bundle newExtras)
      */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_APPWIDGET_OPTIONS_CHANGED = "android.appwidget.action.APPWIDGET_UPDATE_OPTIONS";
 
     /**
@@ -312,6 +323,8 @@
      *
      * @see AppWidgetProvider#onDeleted AppWidgetProvider.onDeleted(Context context, int[] appWidgetIds)
      */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED";
 
     /**
@@ -322,6 +335,8 @@
      *
      * @see AppWidgetProvider#onEnabled AppWidgetProvider.onDisabled(Context context)
      */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED";
 
     /**
@@ -334,6 +349,8 @@
      *
      * @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context)
      */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
 
     /**
@@ -365,6 +382,8 @@
      *
      * @see #ACTION_APPWIDGET_HOST_RESTORED
      */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_APPWIDGET_RESTORED
             = "android.appwidget.action.APPWIDGET_RESTORED";
 
@@ -402,6 +421,8 @@
      *
      * @see #ACTION_APPWIDGET_RESTORED
      */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(explicitOnly = true)
     public static final String ACTION_APPWIDGET_HOST_RESTORED
             = "android.appwidget.action.APPWIDGET_HOST_RESTORED";
 
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index d428a3a..2f87633 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -24,6 +24,7 @@
 import android.database.CursorToBulkCursorAdaptor;
 import android.database.DatabaseUtils;
 import android.database.IContentObserver;
+import android.database.PageViewCursor;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -103,6 +104,7 @@
                     if (cursor != null) {
                         CursorToBulkCursorAdaptor adaptor = null;
 
+                        cursor = PageViewCursor.wrap(cursor, queryArgs);
                         try {
                             adaptor = new CursorToBulkCursorAdaptor(cursor, observer,
                                     getProviderName());
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 1f01e28e..4dc6fd2 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -17,6 +17,7 @@
 package android.content;
 
 import android.annotation.AnyRes;
+import android.annotation.BroadcastBehavior;
 import android.annotation.IntDef;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -1992,6 +1993,7 @@
      * This is a protected intent that can only be sent by the system.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @BroadcastBehavior(includeBackground = true)
     public static final String ACTION_BOOT_COMPLETED = "android.intent.action.BOOT_COMPLETED";
 
     /**
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 147b3e1..4de64c4 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -608,6 +608,12 @@
 
     boolean setRequiredForSystemUser(String packageName, boolean systemUserApp);
 
+    /**
+     * Sets whether or not an update is available. Ostensibly for instant apps
+     * to force exteranl resolution.
+     */
+    void setUpdateAvailable(String packageName, boolean updateAvaialble);
+
     String getServicesSystemSharedLibraryPackageName();
     String getSharedSystemSharedLibraryPackageName();
 
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 5d5696b..8ff2f35 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -271,6 +271,9 @@
      */
     public String overlayTarget;
 
+    /** @hide */
+    public boolean isStaticOverlay;
+
     public PackageInfo() {
     }
 
@@ -323,6 +326,7 @@
         dest.writeString(restrictedAccountType);
         dest.writeString(requiredAccountType);
         dest.writeString(overlayTarget);
+        dest.writeInt(isStaticOverlay ? 1 : 0);
     }
 
     public static final Parcelable.Creator<PackageInfo> CREATOR
@@ -372,6 +376,7 @@
         restrictedAccountType = source.readString();
         requiredAccountType = source.readString();
         overlayTarget = source.readString();
+        isStaticOverlay = source.readInt() != 0;
 
         // The component lists were flattened with the redundant ApplicationInfo
         // instances omitted.  Distribute the canonical one here as appropriate.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ddde96175..10ffab2 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5293,6 +5293,11 @@
     public abstract void setInstallerPackageName(String targetPackage,
             String installerPackageName);
 
+    /** @hide */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
+    public abstract void setUpdateAvailable(String packageName, boolean updateAvaialble);
+
     /**
      * Attempts to delete a package. Since this may take a little while, the
      * result will be posted back to the given observer. A deletion will fail if
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index a1c325a..5a28e87 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -211,6 +211,14 @@
         CHILD_PACKAGE_TAGS.add(TAG_EAT_COMMENT);
     }
 
+    private static final boolean LOG_UNSAFE_BROADCASTS = false;
+
+    // Set of broadcast actions that are safe for manifest receivers
+    private static final Set<String> SAFE_BROADCASTS = new ArraySet<>();
+    static {
+        SAFE_BROADCASTS.add(Intent.ACTION_BOOT_COMPLETED);
+    }
+
     /** @hide */
     public static class NewPermissionInfo {
         public final String name;
@@ -603,6 +611,7 @@
         pi.restrictedAccountType = p.mRestrictedAccountType;
         pi.requiredAccountType = p.mRequiredAccountType;
         pi.overlayTarget = p.mOverlayTarget;
+        pi.isStaticOverlay = p.mIsStaticOverlay;
         pi.firstInstallTime = firstInstallTime;
         pi.lastUpdateTime = lastUpdateTime;
         if ((flags&PackageManager.GET_GIDS) != 0) {
@@ -2097,6 +2106,9 @@
                         com.android.internal.R.styleable.AndroidManifestResourceOverlay);
                 pkg.mOverlayTarget = sa.getString(
                         com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
+                pkg.mIsStaticOverlay = sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestResourceOverlay_isStatic,
+                        false);
                 sa.recycle();
 
                 if (pkg.mOverlayTarget == null) {
@@ -2104,6 +2116,9 @@
                     mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                     return null;
                 }
+                if (pkg.mIsStaticOverlay) {
+                    // TODO(b/35742444): Need to support selection method based on a package name.
+                }
                 XmlUtils.skipCurrentTag(parser);
 
             } else if (tagName.equals(TAG_KEY_SETS)) {
@@ -4240,6 +4255,18 @@
                 if (intent.isVisibleToInstantApp()) {
                     a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
                 }
+                if (LOG_UNSAFE_BROADCASTS && receiver
+                        && (owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O)) {
+                    for (int i = 0; i < intent.countActions(); i++) {
+                        final String action = intent.getAction(i);
+                        if (action == null || !action.startsWith("android.")) continue;
+                        if (!SAFE_BROADCASTS.contains(action)) {
+                            Slog.w(TAG, "Broadcast " + action + " may never be delivered to "
+                                    + owner.packageName + " as requested at: "
+                                    + parser.getPositionDescription());
+                        }
+                    }
+                }
             } else if (!receiver && parser.getName().equals("preferred")) {
                 ActivityIntentInfo intent = new ActivityIntentInfo(a);
                 if (!parseIntent(res, parser, false /*allowGlobs*/, false /*allowAutoVerify*/,
@@ -5580,6 +5607,7 @@
         public String mRequiredAccountType;
 
         public String mOverlayTarget;
+        public boolean mIsStaticOverlay;
         public boolean mTrustedOverlay;
 
         /**
@@ -6056,6 +6084,7 @@
             mRestrictedAccountType = dest.readString();
             mRequiredAccountType = dest.readString();
             mOverlayTarget = dest.readString();
+            mIsStaticOverlay = (dest.readInt() == 1);
             mTrustedOverlay = (dest.readInt() == 1);
             mSigningKeys = (ArraySet<PublicKey>) dest.readArraySet(boot);
             mUpgradeKeySets = (ArraySet<String>) dest.readArraySet(boot);
@@ -6171,6 +6200,7 @@
             dest.writeString(mRestrictedAccountType);
             dest.writeString(mRequiredAccountType);
             dest.writeString(mOverlayTarget);
+            dest.writeInt(mIsStaticOverlay ? 1 : 0);
             dest.writeInt(mTrustedOverlay ? 1 : 0);
             dest.writeArraySet(mSigningKeys);
             dest.writeArraySet(mUpgradeKeySets);
diff --git a/core/java/android/content/res/FontResourcesParser.java b/core/java/android/content/res/FontResourcesParser.java
index 50fc344..091cc26 100644
--- a/core/java/android/content/res/FontResourcesParser.java
+++ b/core/java/android/content/res/FontResourcesParser.java
@@ -16,7 +16,8 @@
 package android.content.res;
 
 import com.android.internal.R;
-import android.text.FontConfig;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.util.AttributeSet;
 import android.util.Xml;
 
@@ -35,7 +36,81 @@
     private static final int NORMAL_WEIGHT = 400;
     private static final int ITALIC = 1;
 
-    public static FontConfig parse(XmlPullParser parser, Resources resources)
+    // A class represents single entry of font-family in xml file.
+    public interface FamilyResourceEntry {}
+
+    // A class represents font provider based font-family element in xml file.
+    public static final class ProviderResourceEntry implements FamilyResourceEntry {
+        private final @NonNull String mProviderAuthority;
+        private final @NonNull String mProviderPackage;
+        private final @NonNull String mQuery;
+
+        public ProviderResourceEntry(@NonNull String authority, @NonNull String pkg,
+                @NonNull String query) {
+            mProviderAuthority = authority;
+            mProviderPackage = pkg;
+            mQuery = query;
+        }
+
+        public @NonNull String getAuthority() {
+            return mProviderAuthority;
+        }
+
+        public @NonNull String getPackage() {
+            return mProviderPackage;
+        }
+
+        public @NonNull String getQuery() {
+            return mQuery;
+        }
+    }
+
+    // A class represents font element in xml file which points a file in resource.
+    public static final class FontFileResourceEntry {
+        private final @NonNull String mFileName;
+        private int mWeight;
+        private boolean mItalic;
+        private int mResourceId;
+
+        public FontFileResourceEntry(@NonNull String fileName, int weight, boolean italic,
+                int resourceId) {
+            mFileName = fileName;
+            mWeight = weight;
+            mItalic = italic;
+            mResourceId = resourceId;
+        }
+
+        public @NonNull String getFileName() {
+            return mFileName;
+        }
+
+        public int getWeight() {
+            return mWeight;
+        }
+
+        public boolean isItalic() {
+            return mItalic;
+        }
+
+        public int getResourceId() {
+            return mResourceId;
+        }
+    }
+
+    // A class represents file based font-family element in xml file.
+    public static final class FontFamilyFilesResourceEntry implements FamilyResourceEntry {
+        private final @NonNull FontFileResourceEntry[] mEntries;
+
+        public FontFamilyFilesResourceEntry(@NonNull FontFileResourceEntry[] entries) {
+            mEntries = entries;
+        }
+
+        public @NonNull FontFileResourceEntry[] getEntries() {
+            return mEntries;
+        }
+    }
+
+    public static @Nullable FamilyResourceEntry parse(XmlPullParser parser, Resources resources)
             throws XmlPullParserException, IOException {
         int type;
         while ((type=parser.next()) != XmlPullParser.START_TAG
@@ -49,21 +124,21 @@
         return readFamilies(parser, resources);
     }
 
-    private static FontConfig readFamilies(XmlPullParser parser, Resources resources)
-            throws XmlPullParserException, IOException {
-        FontConfig config = new FontConfig();
+    private static @Nullable FamilyResourceEntry readFamilies(XmlPullParser parser,
+            Resources resources) throws XmlPullParserException, IOException {
         parser.require(XmlPullParser.START_TAG, null, "font-family");
         String tag = parser.getName();
+        FamilyResourceEntry result = null;
         if (tag.equals("font-family")) {
-            config.getFamilies().add(readFamily(parser, resources));
+            return readFamily(parser, resources);
         } else {
             skip(parser);
+            return null;
         }
-        return config;
     }
 
-    private static FontConfig.Family readFamily(XmlPullParser parser, Resources resources)
-            throws XmlPullParserException, IOException {
+    private static @Nullable FamilyResourceEntry readFamily(XmlPullParser parser,
+            Resources resources) throws XmlPullParserException, IOException {
         AttributeSet attrs = Xml.asAttributeSet(parser);
         TypedArray array = resources.obtainAttributes(attrs, R.styleable.FontFamily);
         String authority = array.getString(R.styleable.FontFamily_fontProviderAuthority);
@@ -74,9 +149,9 @@
             while (parser.next() != XmlPullParser.END_TAG) {
                 skip(parser);
             }
-            return new FontConfig.Family(authority, providerPackage, query);
+            return new ProviderResourceEntry(authority, providerPackage, query);
         }
-        List<FontConfig.Font> fonts = new ArrayList<>();
+        List<FontFileResourceEntry> fonts = new ArrayList<>();
         while (parser.next() != XmlPullParser.END_TAG) {
             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
             String tag = parser.getName();
@@ -86,10 +161,14 @@
                 skip(parser);
             }
         }
-        return new FontConfig.Family(null, fonts, null, null);
+        if (fonts.isEmpty()) {
+            return null;
+        }
+        return new FontFamilyFilesResourceEntry(fonts.toArray(
+                new FontFileResourceEntry[fonts.size()]));
     }
 
-    private static FontConfig.Font readFont(XmlPullParser parser, Resources resources)
+    private static FontFileResourceEntry readFont(XmlPullParser parser, Resources resources)
             throws XmlPullParserException, IOException {
         AttributeSet attrs = Xml.asAttributeSet(parser);
         TypedArray array = resources.obtainAttributes(attrs, R.styleable.FontFamilyFont);
@@ -101,7 +180,7 @@
         while (parser.next() != XmlPullParser.END_TAG) {
             skip(parser);
         }
-        return new FontConfig.Font(filename, 0, null, weight, isItalic, resourceId);
+        return new FontFileResourceEntry(filename, weight, isItalic, resourceId);
     }
 
     private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 38efa49..949d644 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -769,8 +769,13 @@
             if (file.endsWith("xml")) {
                 final XmlResourceParser rp = loadXmlResourceParser(
                         file, id, value.assetCookie, "font");
-                final FontConfig config = FontResourcesParser.parse(rp, wrapper);
-                return Typeface.createFromResources(config, mAssets, file);
+                final FontResourcesParser.FamilyResourceEntry familyEntry =
+                        FontResourcesParser.parse(rp, wrapper);
+                if (familyEntry == null) {
+                    Log.e(TAG, "Failed to find font-family tag");
+                    return null;
+                }
+                return Typeface.createFromResources(familyEntry, mAssets, file);
             }
             return Typeface.createFromResources(mAssets, file, value.assetCookie);
         } catch (XmlPullParserException e) {
@@ -796,20 +801,23 @@
 
         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
         try {
+            // TODO: Stop re-ussing font-family xml tag structure and use ResourceArray instead.
             final XmlResourceParser rp = loadXmlResourceParser(
                     file, id, value.assetCookie, "font");
-            final FontConfig config = FontResourcesParser.parse(rp, wrapper);
-            final List<FontConfig.Family> families = config.getFamilies();
-            if (families == null || families.isEmpty()) {
+            final FontResourcesParser.FamilyResourceEntry familyEntry =
+                    FontResourcesParser.parse(rp, wrapper);
+            if (familyEntry == null) {
+                Log.e(TAG, "failed to find font-family tag");
                 return;
             }
-            for (int j = 0; j < families.size(); j++) {
-                final FontConfig.Family family = families.get(j);
-                final List<FontConfig.Font> fonts = family.getFonts();
-                for (int i = 0; i < fonts.size(); i++) {
-                    int resourceId = fonts.get(i).getResourceId();
-                    wrapper.getFont(resourceId);
-                }
+            if (familyEntry instanceof FontResourcesParser.ProviderResourceEntry) {
+                throw new IllegalArgumentException("Provider based fonts can not be used.");
+            }
+            final FontResourcesParser.FontFamilyFilesResourceEntry filesEntry =
+                    (FontResourcesParser.FontFamilyFilesResourceEntry) familyEntry;
+            for (FontResourcesParser.FontFileResourceEntry fileEntry : filesEntry.getEntries()) {
+                int resourceId = fileEntry.getResourceId();
+                wrapper.getFont(resourceId);
             }
         } catch (XmlPullParserException e) {
             Log.e(TAG, "Failed to parse xml resource " + file, e);
diff --git a/core/java/android/database/PageViewCursor.java b/core/java/android/database/PageViewCursor.java
new file mode 100644
index 0000000..fbd039d
--- /dev/null
+++ b/core/java/android/database/PageViewCursor.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.database;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.MathUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+/**
+ * Cursor wrapper that provides visibility into a subset of a wrapped cursor.
+ *
+ * The window is specified by offset and limit.
+ *
+ * @hide
+ */
+public final class PageViewCursor extends CrossProcessCursorWrapper {
+
+    /**
+     * An extra added to results that are auto-paged using the wrapper.
+     */
+    public static final String EXTRA_AUTO_PAGED = "android.content.extra.AUTO_PAGED";
+
+    private static final String TAG = "PageViewCursor";
+    private static final boolean DEBUG = false;
+    private static final boolean VERBOSE = false;
+
+    private final int mOffset;  // aka first index
+    private final int mCount;
+    private final Bundle mExtras;
+
+    private int mPos = -1;
+
+    /**
+     * @see PageViewCursor#wrap(Cursor, Bundle)
+     */
+    @VisibleForTesting
+    public PageViewCursor(Cursor cursor, int offset, int limit) {
+        super(cursor);
+
+        checkArgument(offset > -1);
+        checkArgument(limit > -1);
+
+        mOffset = offset;
+
+        mExtras = new Bundle();
+        Bundle extras = cursor.getExtras();
+        if (extras != null) {
+            mExtras.putAll(extras);
+        }
+        mExtras.putBoolean(EXTRA_AUTO_PAGED, true);
+
+        // We need a mutable bundle so we can add QUERY_RESULT_SIZE.
+        // Direct equality check is correct here. Bundle.EMPTY is a specific instance
+        // of Bundle that is immutable by way of implementation.
+        // mExtras = (extras == Bundle.EMPTY) ? new Bundle() : extras;
+
+        // When we're wrapping another cursor, it should not already be "paged".
+        checkArgument(!mExtras.containsKey(ContentResolver.EXTRA_TOTAL_SIZE));
+
+        int count = mCursor.getCount();
+        mExtras.putInt(ContentResolver.EXTRA_TOTAL_SIZE, count);
+
+        mCount = MathUtils.constrain(count - offset, 0, limit);
+
+        if (DEBUG) Log.d(TAG, "Wrapped cursor"
+            + " offset: " + mOffset
+            + ", limit: " + limit
+            + ", delegate_size: " + count
+            + ", paged_count: " + mCount);
+    }
+
+    @Override
+    public Bundle getExtras() {
+        return mExtras;
+    }
+
+    @Override
+    public int getPosition() {
+        return mPos;
+    }
+
+    @Override
+    public boolean isBeforeFirst() {
+        if (mCount == 0) {
+            return true;
+        }
+        return mPos == -1;
+    }
+
+    @Override
+    public boolean isAfterLast() {
+        if (mCount == 0) {
+            return true;
+        }
+        return mPos == mCount;
+    }
+
+    @Override
+    public boolean isFirst() {
+        return mPos == 0;
+    }
+
+    @Override
+    public boolean isLast() {
+        return mPos == mCount - 1;
+    }
+
+    @Override
+    public boolean moveToFirst() {
+        return moveToPosition(0);
+    }
+
+    @Override
+    public boolean moveToLast() {
+        return moveToPosition(mCount - 1);
+    }
+
+    @Override
+    public boolean moveToNext() {
+        return move(1);
+    }
+
+    @Override
+    public boolean moveToPrevious() {
+        return move(-1);
+    }
+
+    @Override
+    public boolean move(int offset) {
+        return moveToPosition(mPos + offset);
+    }
+
+    @Override
+    public boolean moveToPosition(int position) {
+        if (position >= mCount) {
+            if (VERBOSE) Log.v(TAG, "Invalid Positon: " + position + " >= count: " + mCount
+                    + ". Moving to last record.");
+            mPos = mCount;
+            super.moveToPosition(mOffset + mPos);  // move into "after last" state.
+            return false;
+        }
+
+        // Make sure position isn't before the beginning of the cursor
+        if (position < 0) {
+            if (VERBOSE) Log.v(TAG, "Ignoring invalid move to position: " + position);
+            mPos = -1;
+            super.moveToPosition(mPos);
+            return false;
+        }
+
+        if (position == mPos) {
+            if (VERBOSE) Log.v(TAG, "Ignoring no-op move to position: " + position);
+            return true;
+        }
+
+        int delegatePosition = position + mOffset;
+        if (VERBOSE) Log.v(TAG, "Moving delegate cursor to position: " + delegatePosition);
+        if (super.moveToPosition(delegatePosition)) {
+            mPos = position;
+            return true;
+        } else {
+            mPos = -1;
+            super.moveToPosition(-1);
+            return false;
+        }
+    }
+
+    @Override
+    public boolean onMove(int oldPosition, int newPosition) {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+    @Override
+    public int getCount() {
+        return mCount;
+    }
+
+    /**
+     * Wraps the cursor such that it will honor paging args (if present), AND if the cursor
+     * does not report paging size.
+     *
+     * <p>No-op if cursor already contains paging or is less than specified page size.
+     */
+    public static Cursor wrap(Cursor cursor, @Nullable Bundle queryArgs) {
+
+        boolean hasPagingArgs =
+                queryArgs != null
+                && (queryArgs.containsKey(ContentResolver.QUERY_ARG_OFFSET)
+                || queryArgs.containsKey(ContentResolver.QUERY_ARG_LIMIT));
+
+        if (!hasPagingArgs) {
+            if (VERBOSE) Log.d(TAG, "No-wrap: No paging args in request.");
+            return cursor;
+        }
+
+        if (hasPagedResponseDetails(cursor.getExtras())) {
+            if (VERBOSE) Log.d(TAG, "No-wrap. Cursor has paging details.");
+            return cursor;
+        }
+
+        return new PageViewCursor(
+                cursor,
+                queryArgs.getInt(ContentResolver.QUERY_ARG_OFFSET, 0),
+                queryArgs.getInt(ContentResolver.QUERY_ARG_LIMIT, Integer.MAX_VALUE));
+    }
+
+    /**
+     * @return true if the extras contains information indicating the associated
+     * cursor is paged.
+     */
+    private static boolean hasPagedResponseDetails(@Nullable Bundle extras) {
+        if (extras != null && extras.containsKey(ContentResolver.EXTRA_TOTAL_SIZE)) {
+            return true;
+        }
+
+        String[] honoredArgs = extras.getStringArray(ContentResolver.EXTRA_HONORED_ARGS);
+        if (honoredArgs != null && (
+                ArrayUtils.contains(honoredArgs, ContentResolver.QUERY_ARG_OFFSET)
+                || ArrayUtils.contains(honoredArgs, ContentResolver.QUERY_ARG_LIMIT))) {
+            return true;
+        }
+
+        return false;
+    }
+}
diff --git a/core/java/android/hardware/LegacySensorManager.java b/core/java/android/hardware/LegacySensorManager.java
index f959093..f5cf3f7 100644
--- a/core/java/android/hardware/LegacySensorManager.java
+++ b/core/java/android/hardware/LegacySensorManager.java
@@ -16,6 +16,8 @@
 
 package android.hardware;
 
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.view.IRotationWatcher;
@@ -57,8 +59,7 @@
                                     public void onRotationChanged(int rotation) {
                                         LegacySensorManager.onRotationChanged(rotation);
                                     }
-                                }
-                        );
+                                }, DEFAULT_DISPLAY);
                     } catch (RemoteException e) {
                     }
                 }
diff --git a/core/java/android/net/ConnectivityMetricsEvent.java b/core/java/android/net/ConnectivityMetricsEvent.java
index 6fdc739..4faff62 100644
--- a/core/java/android/net/ConnectivityMetricsEvent.java
+++ b/core/java/android/net/ConnectivityMetricsEvent.java
@@ -19,39 +19,40 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-/** {@hide} */
+/**
+ * Represents a core networking event defined in package android.net.metrics.
+ * Logged by IpConnectivityLog and managed by ConnectivityMetrics service.
+ * {@hide}
+ * */
 public final class ConnectivityMetricsEvent implements Parcelable {
 
-    /**  The time when this event was collected, as returned by System.currentTimeMillis(). */
-    final public long timestamp;
-
-    /** The subsystem that generated the event. One of the COMPONENT_TAG_xxx constants. */
-    final public int componentTag;
-
-    /** The subsystem-specific event ID. */
-    final public int eventTag;
-
+    /** Time when this event was collected, as returned by System.currentTimeMillis(). */
+    public long timestamp;
+    /** Transports of the network associated with the event, as defined in NetworkCapabilities. */
+    public long transports;
+    /** Network id of the network associated with the event, or 0 if unspecified. */
+    public int netId;
+    /** Name of the network interface associated with the event, or null if unspecified. */
+    public String ifname;
     /** Opaque event-specific data. */
-    final public Parcelable data;
+    public Parcelable data;
 
-    public ConnectivityMetricsEvent(long timestamp, int componentTag,
-                                    int eventTag, Parcelable data) {
-        this.timestamp = timestamp;
-        this.componentTag = componentTag;
-        this.eventTag = eventTag;
-        this.data = data;
+    public ConnectivityMetricsEvent() {
+    }
+
+    private ConnectivityMetricsEvent(Parcel in) {
+        timestamp = in.readLong();
+        transports = in.readLong();
+        netId = in.readInt();
+        ifname = in.readString();
+        data = in.readParcelable(null);
     }
 
     /** Implement the Parcelable interface */
     public static final Parcelable.Creator<ConnectivityMetricsEvent> CREATOR
             = new Parcelable.Creator<ConnectivityMetricsEvent> (){
         public ConnectivityMetricsEvent createFromParcel(Parcel source) {
-            final long timestamp = source.readLong();
-            final int componentTag = source.readInt();
-            final int eventTag = source.readInt();
-            final Parcelable data = source.readParcelable(null);
-            return new ConnectivityMetricsEvent(timestamp, componentTag,
-                    eventTag, data);
+            return new ConnectivityMetricsEvent(source);
         }
 
         public ConnectivityMetricsEvent[] newArray(int size) {
@@ -59,7 +60,6 @@
         }
     };
 
-    /** Implement the Parcelable interface */
     @Override
     public int describeContents() {
         return 0;
@@ -68,13 +68,15 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeLong(timestamp);
-        dest.writeInt(componentTag);
-        dest.writeInt(eventTag);
+        dest.writeLong(transports);
+        dest.writeInt(netId);
+        dest.writeString(ifname);
         dest.writeParcelable(data, 0);
     }
 
+    @Override
     public String toString() {
-        return String.format("ConnectivityMetricsEvent(%tT.%tL, %d, %d): %s",
-                timestamp, timestamp, componentTag, eventTag, data);
+        // TODO: add transports, netId, ifname
+        return String.format("ConnectivityMetricsEvent(%tT.%tL): %s", timestamp, timestamp, data);
     }
 }
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index e6fe0d0..70ecf89 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -419,6 +419,7 @@
      *         to connect to
      * @throws SecurityException if the caller does not hold the
      *         {@link android.Manifest.permission#REQUEST_NETWORK_SCORES} permission.
+     * @hide
      */
     public RecommendationResult requestRecommendation(RecommendationRequest request)
             throws SecurityException {
diff --git a/core/java/android/net/metrics/ApfProgramEvent.java b/core/java/android/net/metrics/ApfProgramEvent.java
index c2795a2a..ad4588f 100644
--- a/core/java/android/net/metrics/ApfProgramEvent.java
+++ b/core/java/android/net/metrics/ApfProgramEvent.java
@@ -47,23 +47,19 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface Flags {}
 
-    public final long lifetime;     // Lifetime of the program in seconds
-    public final int filteredRas;   // Number of RAs filtered by the APF program
-    public final int currentRas;    // Total number of current RAs at generation time
-    public final int programLength; // Length of the APF program in bytes
-    public final int flags;         // Bitfield compound of FLAG_* constants
+    public long lifetime;       // Maximum computed lifetime of the program in seconds
+    public long actualLifetime; // Effective program lifetime in seconds
+    public int filteredRas;     // Number of RAs filtered by the APF program
+    public int currentRas;      // Total number of current RAs at generation time
+    public int programLength;   // Length of the APF program in bytes
+    public int flags;           // Bitfield compound of FLAG_* constants
 
-    public ApfProgramEvent(
-            long lifetime, int filteredRas, int currentRas, int programLength, @Flags int flags) {
-        this.lifetime = lifetime;
-        this.filteredRas = filteredRas;
-        this.currentRas = currentRas;
-        this.programLength = programLength;
-        this.flags = flags;
+    public ApfProgramEvent() {
     }
 
     private ApfProgramEvent(Parcel in) {
         this.lifetime = in.readLong();
+        this.actualLifetime = in.readLong();
         this.filteredRas = in.readInt();
         this.currentRas = in.readInt();
         this.programLength = in.readInt();
@@ -73,6 +69,7 @@
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeLong(lifetime);
+        out.writeLong(actualLifetime);
         out.writeInt(filteredRas);
         out.writeInt(currentRas);
         out.writeInt(programLength);
@@ -87,8 +84,8 @@
     @Override
     public String toString() {
         String lifetimeString = (lifetime < Long.MAX_VALUE) ? lifetime + "s" : "forever";
-        return String.format("ApfProgramEvent(%d/%d RAs %dB %s %s)",
-                filteredRas, currentRas, programLength, lifetimeString, namesOf(flags));
+        return String.format("ApfProgramEvent(%d/%d RAs %dB %ds/%s %s)", filteredRas, currentRas,
+                programLength, actualLifetime, lifetimeString, namesOf(flags));
     }
 
     public static final Parcelable.Creator<ApfProgramEvent> CREATOR
diff --git a/core/java/android/net/metrics/ApfStats.java b/core/java/android/net/metrics/ApfStats.java
index f8d7fa9..3b0dc7e 100644
--- a/core/java/android/net/metrics/ApfStats.java
+++ b/core/java/android/net/metrics/ApfStats.java
@@ -25,25 +25,28 @@
  */
 public final class ApfStats implements Parcelable {
 
-    public final long durationMs;     // time interval in milliseconds these stastistics covers
-    public final int receivedRas;     // number of received RAs
-    public final int matchingRas;     // number of received RAs matching a known RA
-    public final int droppedRas;      // number of received RAs ignored due to the MAX_RAS limit
-    public final int zeroLifetimeRas; // number of received RAs with a minimum lifetime of 0
-    public final int parseErrors;     // number of received RAs that could not be parsed
-    public final int programUpdates;  // number of APF program updates
-    public final int maxProgramSize;  // maximum APF program size advertised by hardware
+    /** time interval in milliseconds these stastistics covers. */
+    public long durationMs;
+    /** number of received RAs. */
+    public int receivedRas;
+    /** number of received RAs matching a known RA. */
+    public int matchingRas;
+    /** number of received RAs ignored due to the MAX_RAS limit. */
+    public int droppedRas;
+    /** number of received RAs with a minimum lifetime of 0. */
+    public int zeroLifetimeRas;
+    /** number of received RAs that could not be parsed. */
+    public int parseErrors;
+    /** number of APF program updates from receiving RAs.. */
+    public int programUpdates;
+    /** total number of APF program updates. */
+    public int programUpdatesAll;
+    /** number of APF program updates from allowing multicast traffic. */
+    public int programUpdatesAllowingMulticast;
+    /** maximum APF program size advertised by hardware. */
+    public int maxProgramSize;
 
-    public ApfStats(long durationMs, int receivedRas, int matchingRas, int droppedRas,
-            int zeroLifetimeRas, int parseErrors, int programUpdates, int maxProgramSize) {
-        this.durationMs = durationMs;
-        this.receivedRas = receivedRas;
-        this.matchingRas = matchingRas;
-        this.droppedRas = droppedRas;
-        this.zeroLifetimeRas = zeroLifetimeRas;
-        this.parseErrors = parseErrors;
-        this.programUpdates = programUpdates;
-        this.maxProgramSize = maxProgramSize;
+    public ApfStats() {
     }
 
     private ApfStats(Parcel in) {
@@ -54,6 +57,8 @@
         this.zeroLifetimeRas = in.readInt();
         this.parseErrors = in.readInt();
         this.programUpdates = in.readInt();
+        this.programUpdatesAll = in.readInt();
+        this.programUpdatesAllowingMulticast = in.readInt();
         this.maxProgramSize = in.readInt();
     }
 
@@ -66,6 +71,8 @@
         out.writeInt(zeroLifetimeRas);
         out.writeInt(parseErrors);
         out.writeInt(programUpdates);
+        out.writeInt(programUpdatesAll);
+        out.writeInt(programUpdatesAllowingMulticast);
         out.writeInt(maxProgramSize);
     }
 
@@ -83,8 +90,9 @@
                 .append(String.format("%d matching, ", matchingRas))
                 .append(String.format("%d dropped, ", droppedRas))
                 .append(String.format("%d zero lifetime, ", zeroLifetimeRas))
-                .append(String.format("%d parse errors, ", parseErrors))
-                .append(String.format("%d program updates})", programUpdates))
+                .append(String.format("%d parse errors}, ", parseErrors))
+                .append(String.format("updates: {all: %d, RAs: %d, allow multicast: %d})",
+                        programUpdatesAll, programUpdates, programUpdatesAllowingMulticast))
                 .toString();
     }
 
diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java
index 173e5fd..79094c0 100644
--- a/core/java/android/net/metrics/IpConnectivityLog.java
+++ b/core/java/android/net/metrics/IpConnectivityLog.java
@@ -60,21 +60,23 @@
     }
 
     /**
-     * Log an IpConnectivity event.
-     * @param timestamp is the epoch timestamp of the event in ms.
-     * @param data is a Parcelable instance representing the event.
+     * Log a ConnectivityMetricsEvent.
+     * @param ev the event to log. If the event timestamp is 0,
+     * the timestamp is set to the current time in milliseconds.
      * @return true if the event was successfully logged.
      */
-    public boolean log(long timestamp, Parcelable data) {
+    public boolean log(ConnectivityMetricsEvent ev) {
         if (!checkLoggerService()) {
             if (DBG) {
                 Log.d(TAG, SERVICE_NAME + " service was not ready");
             }
             return false;
         }
-
+        if (ev.timestamp == 0) {
+            ev.timestamp = System.currentTimeMillis();
+        }
         try {
-            int left = mService.logEvent(new ConnectivityMetricsEvent(timestamp, 0, 0, data));
+            int left = mService.logEvent(ev);
             return left >= 0;
         } catch (RemoteException e) {
             Log.e(TAG, "Error logging event", e);
@@ -82,7 +84,31 @@
         }
     }
 
-    public void log(Parcelable event) {
-        log(System.currentTimeMillis(), event);
+    /**
+     * Log an IpConnectivity event.
+     * @param timestamp is the epoch timestamp of the event in ms.
+     * If the timestamp is 0, the timestamp is set to the current time in milliseconds.
+     * @param data is a Parcelable instance representing the event.
+     * @return true if the event was successfully logged.
+     */
+    public boolean log(long timestamp, Parcelable data) {
+        ConnectivityMetricsEvent ev = makeEv(data);
+        ev.timestamp = timestamp;
+        return log(ev);
+    }
+
+    /**
+     * Log an IpConnectivity event.
+     * @param data is a Parcelable instance representing the event.
+     * @return true if the event was successfully logged.
+     */
+    public boolean log(Parcelable data) {
+        return log(makeEv(data));
+    }
+
+    private static ConnectivityMetricsEvent makeEv(Parcelable data) {
+        ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent();
+        ev.data = data;
+        return ev;
     }
 }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index dc170ed..29884b1 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2042,8 +2042,8 @@
 
     public static final String[] HISTORY_EVENT_NAMES = new String[] {
             "null", "proc", "fg", "top", "sync", "wake_lock_in", "job", "user", "userfg", "conn",
-            "active", "pkginst", "pkgunin", "alarm", "stats", "inactive", "active", "tmpwhitelist",
-            "screenwake", "wakeupap", "longwake", "est_capacity"
+            "active", "pkginst", "pkgunin", "alarm", "stats", "pkginactive", "pkgactive",
+            "tmpwhitelist", "screenwake", "wakeupap", "longwake", "est_capacity"
     };
 
     public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] {
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
index a265dd0..94fd5b0 100644
--- a/core/java/android/os/HwParcel.java
+++ b/core/java/android/os/HwParcel.java
@@ -219,6 +219,7 @@
     public native final void writeStatus(int status);
     public native final void verifySuccess();
     public native final void releaseTemporaryStorage();
+    public native final void release();
 
     public native final void send();
 
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index 9db58ee..6656b00 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -18,6 +18,8 @@
 
 import android.util.ArrayMap;
 
+import java.util.function.Consumer;
+
 /**
  * Takes care of the grunt work of maintaining a list of remote interfaces,
  * typically for the use of performing callbacks from a
@@ -308,6 +310,23 @@
     }
 
     /**
+     * Performs {@code action} on each callback, calling
+     * {@link #beginBroadcast()}/{@link #finishBroadcast()} before/after looping
+     *
+     * @hide
+     */
+    public void broadcast(Consumer<E> action) {
+        int itemCount = beginBroadcast();
+        try {
+            for (int i = 0; i < itemCount; i++) {
+                action.accept(getBroadcastItem(i));
+            }
+        } finally {
+            finishBroadcast();
+        }
+    }
+
+    /**
      * Returns the number of registered callbacks. Note that the number of registered
      * callbacks may differ from the value returned by {@link #beginBroadcast()} since
      * the former returns the number of callbacks registered at the time of the call
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index b525193..8632194 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -35,6 +35,12 @@
     private static final String TAG = "SystemProperties";
     private static final boolean TRACK_KEY_ACCESS = false;
 
+    /**
+     * Android O removed the property name length limit, but com.amazon.kindle 7.8.1.5
+     * uses reflection to read this whenever text is selected (http://b/36095274).
+     */
+    public static final int PROP_NAME_MAX = Integer.MAX_VALUE;
+
     public static final int PROP_VALUE_MAX = 91;
 
     private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>();
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 46f2d38..1fc0b82 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -373,8 +373,7 @@
     }
 
     /** {@hide} */
-    // TODO(b/26742218): find out where toString() is called internally and replace these calls by
-    // dump().
+    // TODO: find out where toString() is called internally and replace these calls by dump().
     public String dump() {
         final CharArrayWriter writer = new CharArrayWriter();
         dump(new IndentingPrintWriter(writer, "    ", 80));
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index 443a3e9..d9c749a 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -81,6 +81,7 @@
  * @attr ref android.R.styleable#Preference_persistent
  * @attr ref android.R.styleable#Preference_defaultValue
  * @attr ref android.R.styleable#Preference_shouldDisableView
+ * @attr ref android.R.styleable#Preference_recycleEnabled
  */
 public class Preference implements Comparable<Preference> {
     /**
@@ -131,6 +132,7 @@
     private Object mDefaultValue;
     private boolean mDependencyMet = true;
     private boolean mParentDependencyMet = true;
+    private boolean mRecycleEnabled = true;
 
     /**
      * @see #setShouldDisableView(boolean)
@@ -139,7 +141,6 @@
 
     private int mLayoutResId = com.android.internal.R.layout.preference;
     private int mWidgetLayoutResId;
-    private boolean mCanRecycleLayout = true;
 
     private OnPreferenceChangeInternalListener mListener;
 
@@ -291,15 +292,13 @@
                 case com.android.internal.R.styleable.Preference_shouldDisableView:
                     mShouldDisableView = a.getBoolean(attr, mShouldDisableView);
                     break;
+
+                case com.android.internal.R.styleable.Preference_recycleEnabled:
+                    mRecycleEnabled = a.getBoolean(attr, mRecycleEnabled);
+                    break;
             }
         }
         a.recycle();
-
-        if (!getClass().getName().startsWith("android.preference")
-                && !getClass().getName().startsWith("com.android")) {
-            // For non-framework subclasses, assume the worst and don't cache views.
-            mCanRecycleLayout = false;
-        }
     }
 
     /**
@@ -458,8 +457,8 @@
     }
 
     /**
-     * Return the extras Bundle object associated with this preference,
-     * returning null if there is not currently one.
+     * Return the extras Bundle object associated with this preference, returning {@code null} if
+     * there is not currently one.
      */
     public Bundle peekExtras() {
         return mExtras;
@@ -482,7 +481,7 @@
     public void setLayoutResource(@LayoutRes int layoutResId) {
         if (layoutResId != mLayoutResId) {
             // Layout changed
-            mCanRecycleLayout = false;
+            mRecycleEnabled = false;
         }
 
         mLayoutResId = layoutResId;
@@ -511,7 +510,7 @@
     public void setWidgetLayoutResource(@LayoutRes int widgetLayoutResId) {
         if (widgetLayoutResId != mWidgetLayoutResId) {
             // Layout changed
-            mCanRecycleLayout = false;
+            mRecycleEnabled = false;
         }
         mWidgetLayoutResId = widgetLayoutResId;
     }
@@ -653,15 +652,13 @@
     }
 
     /**
-     * Sets the order of this Preference with respect to other
-     * Preference objects on the same level. If this is not specified, the
-     * default behavior is to sort alphabetically. The
-     * {@link PreferenceGroup#setOrderingAsAdded(boolean)} can be used to order
-     * Preference objects based on the order they appear in the XML.
+     * Sets the order of this Preference with respect to other Preference objects on the same level.
+     * If this is not specified, the default behavior is to sort alphabetically. The
+     * {@link PreferenceGroup#setOrderingAsAdded(boolean)} can be used to order Preference objects
+     * based on the order they appear in the XML.
      *
-     * @param order The order for this Preference. A lower value will be shown
-     *            first. Use {@link #DEFAULT_ORDER} to sort alphabetically or
-     *            allow ordering from XML.
+     * @param order the order for this Preference. A lower value will be shown first. Use
+     *              {@link #DEFAULT_ORDER} to sort alphabetically or allow ordering from XML
      * @see PreferenceGroup#setOrderingAsAdded(boolean)
      * @see #DEFAULT_ORDER
      */
@@ -669,16 +666,15 @@
         if (order != mOrder) {
             mOrder = order;
 
-            // Reorder the list 
+            // Reorder the list
             notifyHierarchyChanged();
         }
     }
 
     /**
-     * Gets the order of this Preference with respect to other Preference objects
-     * on the same level.
+     * Gets the order of this Preference with respect to other Preference objects on the same level.
      *
-     * @return The order of this Preference.
+     * @return the order of this Preference
      * @see #setOrder(int)
      */
     public int getOrder() {
@@ -686,12 +682,10 @@
     }
 
     /**
-     * Sets the title for this Preference with a CharSequence. 
-     * This title will be placed into the ID
-     * {@link android.R.id#title} within the View created by
-     * {@link #onCreateView(ViewGroup)}.
+     * Sets the title for this Preference with a CharSequence. This title will be placed into the ID
+     * {@link android.R.id#title} within the View created by {@link #onCreateView(ViewGroup)}.
      *
-     * @param title The title for this Preference.
+     * @param title the title for this Preference
      */
     public void setTitle(CharSequence title) {
         if (title == null && mTitle != null || title != null && !title.equals(mTitle)) {
@@ -702,10 +696,10 @@
     }
 
     /**
-     * Sets the title for this Preference with a resource ID. 
+     * Sets the title for this Preference with a resource ID.
      *
      * @see #setTitle(CharSequence)
-     * @param titleResId The title as a resource ID.
+     * @param titleResId the title as a resource ID
      */
     public void setTitle(@StringRes int titleResId) {
         setTitle(mContext.getString(titleResId));
@@ -713,10 +707,10 @@
     }
 
     /**
-     * Returns the title resource ID of this Preference.  If the title did
-     * not come from a resource, 0 is returned.
+     * Returns the title resource ID of this Preference. If the title did not come from a resource,
+     * {@code 0} is returned.
      *
-     * @return The title resource.
+     * @return the title resource
      * @see #setTitle(int)
      */
     @StringRes
@@ -727,7 +721,7 @@
     /**
      * Returns the title of this Preference.
      *
-     * @return The title.
+     * @return the title
      * @see #setTitle(CharSequence)
      */
     public CharSequence getTitle() {
@@ -735,12 +729,10 @@
     }
 
     /**
-     * Sets the icon for this Preference with a Drawable. 
-     * This icon will be placed into the ID
-     * {@link android.R.id#icon} within the View created by
-     * {@link #onCreateView(ViewGroup)}.
+     * Sets the icon for this Preference with a Drawable. This icon will be placed into the ID
+     * {@link android.R.id#icon} within the View created by {@link #onCreateView(ViewGroup)}.
      *
-     * @param icon The optional icon for this Preference.
+     * @param icon the optional icon for this Preference
      */
     public void setIcon(Drawable icon) {
         if ((icon == null && mIcon != null) || (icon != null && mIcon != icon)) {
@@ -751,10 +743,10 @@
     }
 
     /**
-     * Sets the icon for this Preference with a resource ID. 
+     * Sets the icon for this Preference with a resource ID.
      *
      * @see #setIcon(Drawable)
-     * @param iconResId The icon as a resource ID.
+     * @param iconResId the icon as a resource ID
      */
     public void setIcon(@DrawableRes int iconResId) {
         if (mIconResId != iconResId) {
@@ -766,7 +758,7 @@
     /**
      * Returns the icon of this Preference.
      *
-     * @return The icon.
+     * @return the icon
      * @see #setIcon(Drawable)
      */
     public Drawable getIcon() {
@@ -779,7 +771,7 @@
     /**
      * Returns the summary of this Preference.
      *
-     * @return The summary.
+     * @return the summary
      * @see #setSummary(CharSequence)
      */
     public CharSequence getSummary() {
@@ -787,9 +779,9 @@
     }
 
     /**
-     * Sets the summary for this Preference with a CharSequence. 
+     * Sets the summary for this Preference with a CharSequence.
      *
-     * @param summary The summary for the preference.
+     * @param summary the summary for the preference
      */
     public void setSummary(CharSequence summary) {
         if (summary == null && mSummary != null || summary != null && !summary.equals(mSummary)) {
@@ -799,10 +791,10 @@
     }
 
     /**
-     * Sets the summary for this Preference with a resource ID. 
+     * Sets the summary for this Preference with a resource ID.
      *
      * @see #setSummary(CharSequence)
-     * @param summaryResId The summary as a resource.
+     * @param summaryResId the summary as a resource
      */
     public void setSummary(@StringRes int summaryResId) {
         setSummary(mContext.getString(summaryResId));
@@ -812,7 +804,7 @@
      * Sets whether this Preference is enabled. If disabled, it will
      * not handle clicks.
      *
-     * @param enabled Set true to enable it.
+     * @param enabled set {@code true} to enable it
      */
     public void setEnabled(boolean enabled) {
         if (mEnabled != enabled) {
@@ -828,7 +820,7 @@
     /**
      * Checks whether this Preference should be enabled in the list.
      *
-     * @return True if this Preference is enabled, false otherwise.
+     * @return {@code true} if this Preference is enabled, false otherwise
      */
     public boolean isEnabled() {
         return mEnabled && mDependencyMet && mParentDependencyMet;
@@ -837,7 +829,7 @@
     /**
      * Sets whether this Preference is selectable.
      *
-     * @param selectable Set true to make it selectable.
+     * @param selectable set {@code true} to make it selectable
      */
     public void setSelectable(boolean selectable) {
         if (mSelectable != selectable) {
@@ -849,22 +841,21 @@
     /**
      * Checks whether this Preference should be selectable in the list.
      *
-     * @return True if it is selectable, false otherwise.
+     * @return {@code true} if it is selectable, {@code false} otherwise
      */
     public boolean isSelectable() {
         return mSelectable;
     }
 
     /**
-     * Sets whether this Preference should disable its view when it gets
-     * disabled.
-     * <p>
-     * For example, set this and {@link #setEnabled(boolean)} to false for
-     * preferences that are only displaying information and 1) should not be
-     * clickable 2) should not have the view set to the disabled state.
+     * Sets whether this Preference should disable its view when it gets disabled.
      *
-     * @param shouldDisableView Set true if this preference should disable its view
-     *            when the preference is disabled.
+     * <p>For example, set this and {@link #setEnabled(boolean)} to false for preferences that are
+     * only displaying information and 1) should not be clickable 2) should not have the view set to
+     * the disabled state.
+     *
+     * @param shouldDisableView set {@code true} if this preference should disable its view when
+     *                          the preference is disabled
      */
     public void setShouldDisableView(boolean shouldDisableView) {
         mShouldDisableView = shouldDisableView;
@@ -873,14 +864,45 @@
 
     /**
      * Checks whether this Preference should disable its view when it's action is disabled.
+     *
      * @see #setShouldDisableView(boolean)
-     * @return True if it should disable the view. 
+     * @return {@code true} if it should disable the view
      */
     public boolean getShouldDisableView() {
         return mShouldDisableView;
     }
 
     /**
+     * Sets whether this Preference has enabled to have its view recycled when used in the list
+     * view. By default the recycling is enabled.
+     *
+     * <p>The value can be changed only before this preference is added to the preference hierarchy.
+     *
+     * <p>If view recycling is not allowed then each time the list view populates this preference
+     * the {@link #getView(View, ViewGroup)} method receives a {@code null} convert view and needs
+     * to recreate the view. Otherwise view gets recycled and only {@link #onBindView(View)} gets
+     * called.
+     *
+     * @param enabled set {@code true} if this preference view should be recycled
+     */
+    @CallSuper
+    public void setRecycleEnabled(boolean enabled) {
+        mRecycleEnabled = enabled;
+        notifyChanged();
+    }
+
+    /**
+     * Checks whether this Preference has enabled to have its view recycled when used in the list
+     * view.
+     *
+     * @see #setRecycleEnabled(boolean)
+     * @return {@code true} if this preference view should be recycled
+     */
+    public boolean isRecycleEnabled() {
+        return mRecycleEnabled;
+    }
+
+    /**
      * Returns a unique ID for this Preference.  This ID should be unique across all
      * Preference objects in a hierarchy.
      *
@@ -974,7 +996,7 @@
      * the persistent {@link SharedPreferences} storage by default or into
      * {@link PreferenceDataStore} if assigned.
      *
-     * @param persistent Set true if it should store its value(s) into the {@link SharedPreferences}.
+     * @param persistent set {@code true} if it should store its value(s) into the storage.
      */
     public void setPersistent(boolean persistent) {
         mPersistent = persistent;
@@ -1035,7 +1057,7 @@
      *
      * @param preferenceScreen A {@link PreferenceScreen} whose hierarchy click
      *            listener should be called in the proper order (between other
-     *            processing). May be null.
+     *            processing). May be {@code null}.
      * @hide
      */
     public void performClick(PreferenceScreen preferenceScreen) {
@@ -1102,9 +1124,9 @@
      * {@link SharedPreferences}, this is intended behavior to improve
      * performance.
      *
-     * @return The {@link SharedPreferences} where this Preference reads its value(s), or null if it
-     *         isn't attached to a Preference hierarchy or if {@link PreferenceDataStore} is used
-     *         instead.
+     * @return the {@link SharedPreferences} where this Preference reads its value(s). If
+     *         this preference isn't attached to a Preference hierarchy or if
+     *         a {@link PreferenceDataStore} has been set, this method returns {@code null}.
      * @see #getEditor()
      * @see #setPreferenceDataStore(PreferenceDataStore)
      */
@@ -1129,9 +1151,9 @@
      * not show up in the SharedPreferences, this is intended behavior to
      * improve performance.
      *
-     * @return A {@link SharedPreferences.Editor} where this preference saves its value(s), or null
-     *         if it isn't attached to a Preference hierarchy or if {@link PreferenceDataStore} is
-     *         used instead.
+     * @return a {@link SharedPreferences.Editor} where this preference saves its value(s). If
+     *         this preference isn't attached to a Preference hierarchy or if
+     *         a {@link PreferenceDataStore} has been set, this method returns {@code null}.
      * @see #shouldCommit()
      * @see #getSharedPreferences()
      * @see #setPreferenceDataStore(PreferenceDataStore)
@@ -1149,7 +1171,7 @@
      * {@link #getEditor()}. This may return false in situations where batch
      * committing is being done (by the manager) to improve performance.
      *
-     * <p>If this preference is using {@link PreferenceDataStore} this value should be irrelevant.
+     * <p>If this preference is using {@link PreferenceDataStore} this value is irrelevant.
      *
      * @return Whether the Preference should commit its saved value(s).
      * @see #getEditor()
@@ -1253,10 +1275,10 @@
     }
 
     /**
-     * Assigns a {@link PreferenceGroup} as the parent of this Preference. Set null to remove
-     * the current parent.
+     * Assigns a {@link PreferenceGroup} as the parent of this Preference. Set {@code null} to
+     * remove the current parent.
      *
-     * @param parentGroup Parent preference group of this Preference or null if none.
+     * @param parentGroup Parent preference group of this Preference or {@code null} if none.
      */
     void assignParent(@Nullable PreferenceGroup parentGroup) {
         mParentGroup = parentGroup;
@@ -1425,10 +1447,10 @@
     }
 
     /**
-     * Returns the {@link PreferenceGroup} which is this Preference assigned to or null if this
-     * preference is not assigned to any group or is a root Preference.
+     * Returns the {@link PreferenceGroup} which is this Preference assigned to or {@code null} if
+     * this preference is not assigned to any group or is a root Preference.
      *
-     * @return The parent PreferenceGroup or null if not attached to any.
+     * @return the parent PreferenceGroup or {@code null} if not attached to any
      */
     @Nullable
     public PreferenceGroup getParent() {
@@ -1483,7 +1505,7 @@
      * if {@link #shouldPersist()} is true).
      *
      * <p>In case of using {@link PreferenceDataStore}, the <var>restorePersistedValue</var> is
-     * always false. But the default value (if provided) is set.
+     * always {@code true}. But the default value (if provided) is set.
      *
      * <p>This may not always be called. One example is if it should not persist
      * but there is no default value given.
@@ -1831,10 +1853,6 @@
         return mPreferenceManager.getSharedPreferences().getBoolean(mKey, defaultReturnValue);
     }
 
-    boolean canRecycleLayout() {
-        return mCanRecycleLayout;
-    }
-
     @Override
     public String toString() {
         return getFilterableStringBuilder().toString();
@@ -1910,9 +1928,9 @@
      * state. This state should only contain information that is not persistent
      * or can be reconstructed later.
      *
-     * @return A Parcelable object containing the current dynamic state of
-     *         this Preference, or null if there is nothing interesting to save.
-     *         The default implementation returns null.
+     * @return A Parcelable object containing the current dynamic state of this Preference, or
+     *         {@code null} if there is nothing interesting to save. The default implementation
+     *         returns {@code null}.
      * @see #onRestoreInstanceState
      * @see #saveHierarchyState
      */
@@ -1958,9 +1976,9 @@
     }
 
     /**
-     * Hook allowing a Preference to re-apply a representation of its internal
-     * state that had previously been generated by {@link #onSaveInstanceState}.
-     * This function will never be called with a null state.
+     * Hook allowing a Preference to re-apply a representation of its internal state that had
+     * previously been generated by {@link #onSaveInstanceState}. This function will never be called
+     * with a {@code null} state.
      *
      * @param state The saved state that had previously been returned by
      *            {@link #onSaveInstanceState}.
diff --git a/core/java/android/preference/PreferenceGroupAdapter.java b/core/java/android/preference/PreferenceGroupAdapter.java
index 5a0b9e9..bee45ab 100644
--- a/core/java/android/preference/PreferenceGroupAdapter.java
+++ b/core/java/android/preference/PreferenceGroupAdapter.java
@@ -164,7 +164,7 @@
             
             preferences.add(preference);
             
-            if (!mHasReturnedViewTypeCount && preference.canRecycleLayout()) {
+            if (!mHasReturnedViewTypeCount && preference.isRecycleEnabled()) {
                 addPreferenceClassName(preference);
             }
             
@@ -296,7 +296,7 @@
         }
         
         final Preference preference = this.getItem(position);
-        if (!preference.canRecycleLayout()) {
+        if (!preference.isRecycleEnabled()) {
             return IGNORE_ITEM_VIEW_TYPE;
         }
 
diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java
index 14b748a..ea32dfd 100644
--- a/core/java/android/preference/PreferenceManager.java
+++ b/core/java/android/preference/PreferenceManager.java
@@ -93,8 +93,8 @@
     private SharedPreferences mSharedPreferences;
 
     /**
-     * Data store to be used by the Preferences or null if {@link android.content.SharedPreferences}
-     * should be used.
+     * Data store to be used by the Preferences or {@code null} if
+     * {@link android.content.SharedPreferences} should be used.
      */
     @Nullable
     private PreferenceDataStore mPreferenceDataStore;
@@ -484,11 +484,16 @@
     /**
      * Gets a {@link SharedPreferences} instance that preferences managed by this will use.
      *
-     * @return A {@link SharedPreferences} instance pointing to the file that contains the values of
-     * preferences that are managed by this or null if {@link PreferenceDataStore} is used instead.
+     * @return a {@link SharedPreferences} instance pointing to the file that contains the values of
+     *         preferences that are managed by this PreferenceManager. If a
+     *         {@link PreferenceDataStore} has been set, this method returns {@code null}.
      */
     public SharedPreferences getSharedPreferences() {
-        if (mSharedPreferences == null && getPreferenceDataStore() == null) {
+        if (mPreferenceDataStore != null) {
+            return null;
+        }
+
+        if (mSharedPreferences == null) {
             final Context storageContext;
             switch (mStorage) {
                 case STORAGE_DEVICE_PROTECTED:
@@ -564,8 +569,8 @@
     /**
      * Finds a {@link Preference} based on its key.
      *
-     * @param key The key of the preference to retrieve.
-     * @return The {@link Preference} with the key, or null.
+     * @param key the key of the preference to retrieve
+     * @return the {@link Preference} with the key, or {@code null}
      * @see PreferenceGroup#findPreference(CharSequence)
      */
     @Nullable
@@ -659,11 +664,11 @@
 
     /**
      * Returns an editor to use when modifying the shared preferences.
-     * <p>
-     * Do NOT commit unless {@link #shouldCommit()} returns true.
      *
-     * @return An editor to use to write to shared preferences or null if
-     * {@link PreferenceDataStore} is used instead.
+     * <p>Do NOT commit unless {@link #shouldCommit()} returns true.
+     *
+     * @return an editor to use to write to shared preferences. If a {@link PreferenceDataStore}
+     *         has been set, this method returns {@code null}.
      * @see #shouldCommit()
      */
     SharedPreferences.Editor getEditor() {
@@ -687,6 +692,8 @@
      * {@link #getEditor()}. This will return false in cases where the writes
      * should be batched, for example when inflating preferences from XML.
      *
+     * <p>If preferences are using {@link PreferenceDataStore} this value is irrelevant.
+     *
      * @return Whether the client should commit.
      */
     boolean shouldCommit() {
@@ -711,8 +718,8 @@
      * Returns the activity that shows the preferences. This is useful for doing
      * managed queries, but in most cases the use of {@link #getContext()} is
      * preferred.
-     * <p>
-     * This will return null if this class was instantiated with a Context
+     *
+     * <p>This will return {@code null} if this class was instantiated with a Context
      * instead of Activity. For example, when setting the default values.
      *
      * @return The activity that shows the preferences.
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index a0d16bc..dac8354 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -44,7 +44,6 @@
 import android.util.DisplayMetrics;
 import android.util.Pair;
 import android.view.View;
-
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -8913,11 +8912,15 @@
          * ambiguous then the activity should prompt the user for the recipient to send the message
          * to.
          * <p>
+         * Voice Assistant may provide additional information to messaging app about which account
+         * to use for sending a message by populating {@link #EXTRA_SENDER_ACCOUNT_HASH}.
+         * <p>
          * Output: nothing
          *
          * @see #EXTRA_RECIPIENT_CONTACT_URI
          * @see #EXTRA_RECIPIENT_CONTACT_CHAT_ID
          * @see #EXTRA_RECIPIENT_CONTACT_NAME
+         * @see #EXTRA_SENDER_ACCOUNT_HASH
          * @see #METADATA_ACCOUNT_TYPE
          * @see #METADATA_MIMETYPE
          */
@@ -8975,6 +8978,16 @@
                 "android.provider.extra.RECIPIENT_CONTACT_NAME";
 
         /**
+         * This optional extra specifies the hash of the account that should be used by messaging
+         * app for sending voice message with {@link #ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS}. The
+         * value of this extra is a {@code String} and should be the value of {@link
+         * android.accounts.Account#hashCode()} for some account returned by {@link
+         * android.accounts.AccountManager#getAccounts()}.
+         */
+        public static final String EXTRA_SENDER_ACCOUNT_HASH =
+                "android.provider.extra.SENDER_ACCOUNT_HASH";
+
+        /**
          * A string associated with an {@link #ACTION_VOICE_SEND_MESSAGE_TO_CONTACTS} activity
          * describing {@link RawContacts#ACCOUNT_TYPE} for the corresponding Contacts Provider
          * implementation.
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index f53b0d7..56d4ff7 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -1563,7 +1563,7 @@
         if (resolver.getTargetSdkVersion() >= Build.VERSION_CODES.O) {
             if (e instanceof ParcelableException) {
                 ((ParcelableException) e).maybeRethrow(FileNotFoundException.class);
-            } else if (e instanceof RemoteException ) {
+            } else if (e instanceof RemoteException) {
                 ((RemoteException) e).rethrowAsRuntimeException();
             } else if (e instanceof RuntimeException) {
                 throw (RuntimeException) e;
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index 9f5d9d4..84443e9 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -138,12 +138,8 @@
     private HandlerThread mThread;
 
     /** @hide */
-    public FontsContract() {
-        // TODO: investigate if the system context is the best option here. ApplicationContext or
-        // the one passed by developer?
-        // TODO: Looks like ActivityThread.currentActivityThread() can return null. Check when it
-        // returns null and check if we need to handle null case.
-        mContext = ActivityThread.currentActivityThread().getSystemContext();
+    public FontsContract(Context context) {
+        mContext = context.getApplicationContext();
         mPackageManager = mContext.getPackageManager();
     }
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4482662..391ee83 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -506,8 +506,6 @@
      * Input: Nothing.
      * <p>
      * Output: Nothing.
-     *
-     * @hide
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_NIGHT_DISPLAY_SETTINGS =
@@ -5607,10 +5605,10 @@
             "accessibility_web_content_key_bindings";
 
         /**
-         * Setting that specifies whether the display magnification is enabled.
-         * Display magnifications allows the user to zoom in the display content
-         * and is targeted to low vision users. The current magnification scale
-         * is controlled by {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}.
+         * Setting that specifies whether the display magnification is enabled via a system-wide
+         * triple tap gesture. Display magnifications allows the user to zoom in the display content
+         * and is targeted to low vision users. The current magnification scale is controlled by
+         * {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}.
          *
          * @hide
          */
@@ -5618,11 +5616,23 @@
                 "accessibility_display_magnification_enabled";
 
         /**
+         * Setting that specifies whether the display magnification is enabled via a shortcut
+         * affordance within the system's navigation area. Display magnifications allows the user to
+         * zoom in the display content and is targeted to low vision users. The current
+         * magnification scale is controlled by {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE}.
+         *
+         * @hide
+         */
+        public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED =
+                "accessibility_display_magnification_navbar_enabled";
+
+        /**
          * Setting that specifies what the display magnification scale is.
          * Display magnifications allows the user to zoom in the display
          * content and is targeted to low vision users. Whether a display
          * magnification is performed is controlled by
-         * {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED}
+         * {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED} and
+         * {@link #ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED}
          *
          * @hide
          */
@@ -6952,6 +6962,7 @@
             ACCESSIBILITY_DISPLAY_DALTONIZER,
             ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
             ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
+            ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
             ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
             ACCESSIBILITY_SCRIPT_INJECTION,
             ACCESSIBILITY_WEB_CONTENT_KEY_BINDINGS,
@@ -7668,12 +7679,14 @@
        public static final String HDMI_CONTROL_ENABLED = "hdmi_control_enabled";
 
        /**
-        * Whether HDMI system audio is enabled. If enabled, TV internal speaker is muted,
-        * and the output is redirected to AV Receiver connected via
-        * {@Global#HDMI_SYSTEM_AUDIO_OUTPUT}.
+        * Whether HDMI System Audio Control feature is enabled. If enabled, TV will try to turn on
+        * system audio mode if there's a connected CEC-enabled AV Receiver. Then audio stream will
+        * be played on AVR instead of TV spaeker. If disabled, the system audio mode will never be
+        * activated.
         * @hide
         */
-       public static final String HDMI_SYSTEM_AUDIO_ENABLED = "hdmi_system_audio_enabled";
+        public static final String HDMI_SYSTEM_AUDIO_CONTROL_ENABLED =
+                "hdmi_system_audio_control_enabled";
 
        /**
         * Whether TV will automatically turn on upon reception of the CEC command
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 29e2073..709e5f9 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -30,6 +30,7 @@
 import android.os.ICancellationSignal;
 import android.os.Looper;
 import android.util.Log;
+import android.view.autofill.AutofillManager;
 
 import com.android.internal.os.SomeArgs;
 
@@ -90,6 +91,8 @@
     private static final int MSG_ON_FILL_REQUEST = 3;
     private static final int MSG_ON_SAVE_REQUEST = 4;
 
+    private static final int UNUSED_ARG = -1;
+
     private final IAutoFillService mInterface = new IAutoFillService.Stub() {
         @Override
         public void onInit(IAutoFillServiceConnection connection) {
@@ -102,14 +105,14 @@
 
         @Override
         public void onFillRequest(AssistStructure structure, Bundle extras,
-                IFillCallback callback) {
+                IFillCallback callback, int flags) {
             ICancellationSignal transport = CancellationSignal.createTransport();
             try {
                 callback.onCancellable(transport);
             } catch (RemoteException e) {
                 e.rethrowFromSystemServer();
             }
-            mHandlerCaller.obtainMessageOOOO(MSG_ON_FILL_REQUEST, structure,
+            mHandlerCaller.obtainMessageIIOOOO(MSG_ON_FILL_REQUEST, flags, UNUSED_ARG, structure,
                     CancellationSignal.fromTransport(transport), extras, callback)
                     .sendToTarget();
         }
@@ -135,8 +138,9 @@
                 final Bundle extras = (Bundle) args.arg3;
                 final IFillCallback callback = (IFillCallback) args.arg4;
                 final FillCallback fillCallback = new FillCallback(callback);
+                final int flags = msg.arg1;
                 args.recycle();
-                onFillRequest(structure, extras, cancellation, fillCallback);
+                onFillRequest(structure, extras, flags, cancellation, fillCallback);
                 break;
             } case MSG_ON_SAVE_REQUEST: {
                 final SomeArgs args = (SomeArgs) msg.obj;
@@ -188,7 +192,6 @@
      * <p>You should generally do initialization here rather than in {@link #onCreate}.
      */
     public void onConnected() {
-        //TODO(b/33197203): is not called anymore, fix it!
     }
 
     /**
@@ -206,11 +209,25 @@
      *     as well as when filling different sections of the UI as the system will try to
      *     aggressively unbind from the service to conserve resources. See {@link
      *     FillResponse} Javadoc for examples of multiple-sections requests.
+     * @param flags either {@code 0} or {@link AutofillManager#FLAG_MANUAL_REQUEST}.
      * @param cancellationSignal signal for observing cancellation requests. The system will use
      *     this to notify you that the fill result is no longer needed and you should stop
      *     handling this fill request in order to save resources.
      * @param callback object used to notify the result of the request.
      */
+    public void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle data, int flags,
+            @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback) {
+        //TODO(b/33197203): make non-abstract once older method is removed
+        onFillRequest(structure, data, cancellationSignal, callback);
+    }
+
+    /**
+     * @hide
+     * @deprecated - use {@link #onFillRequest(AssistStructure, Bundle, int,
+     * CancellationSignal, FillCallback)} instead
+     */
+    //TODO(b/33197203): remove once clients are not using anymore
+    @Deprecated
     public abstract void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle data,
             @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback);
 
@@ -238,7 +255,6 @@
      * <p> At this point this service may no longer be an active {@link AutofillService}.
      */
     public void onDisconnected() {
-        //TODO(b/33197203): is not called anymore, fix it!
     }
 
     /**
diff --git a/core/java/android/service/autofill/AutofillServiceInfo.java b/core/java/android/service/autofill/AutofillServiceInfo.java
index d220052..f6d40db 100644
--- a/core/java/android/service/autofill/AutofillServiceInfo.java
+++ b/core/java/android/service/autofill/AutofillServiceInfo.java
@@ -78,14 +78,12 @@
             // TODO(b/35956626): inline newSettingsActivity once clients migrate
             final String newSettingsActivity =
                     metaDataArray.getString(R.styleable.AutofillService_settingsActivity);
-            System.out.println(">>> NEW CRAP MAN: " + newSettingsActivity); // TODO(felipeal): tmp
             if (newSettingsActivity != null) {
                 mSettingsActivity = newSettingsActivity;
             } else {
                 mSettingsActivity =
                         metaDataArray.getString(R.styleable.AutoFillService_settingsActivity);
             }
-            System.out.println(">>> FINAL CRAP MAN: " + mSettingsActivity); // TODO(felipeal): tmp
             metaDataArray.recycle();
         } else {
             mSettingsActivity = null;
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index 2461947..ebe02c2 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -167,6 +167,7 @@
         public @NonNull Builder setValue(@NonNull AutoFillId id, @NonNull AutoFillValue value) {
             return setValue(id.getDaRealId(), value.getDaRealValue());
         }
+
         /**
          * Sets the value of a field.
          *
diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java
index 00b206c..e8ad14f 100644
--- a/core/java/android/service/autofill/FillCallback.java
+++ b/core/java/android/service/autofill/FillCallback.java
@@ -37,7 +37,7 @@
     /**
      * Notifies the Android System that an
      * {@link AutofillService#onFillRequest(android.app.assist.AssistStructure, Bundle,
-     * android.os.CancellationSignal, FillCallback)} was successfully fulfilled by the service.
+     * int, android.os.CancellationSignal, FillCallback)} was successfully fulfilled by the service.
      *
      * @param response autofill information for that activity, or {@code null} when the activity
      * cannot be autofilled (for example, if it only contains read-only fields). See
@@ -56,7 +56,7 @@
     /**
      * Notifies the Android System that an
      * {@link AutofillService#onFillRequest(android.app.assist.AssistStructure,
-     * Bundle, android.os.CancellationSignal, FillCallback)}
+     * Bundle, int, android.os.CancellationSignal, FillCallback)}
      * could not be fulfilled by the service.
      *
      * @param message error message to be displayed to the user.
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 069e83c..c43019d 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -31,7 +31,7 @@
 /**
  * Response for a {@link
  * AutofillService#onFillRequest(android.app.assist.AssistStructure,
- * Bundle, android.os.CancellationSignal, FillCallback)}.
+ * Bundle, int, android.os.CancellationSignal, FillCallback)}.
  *
  * <p>The response typically contains one or more {@link Dataset}s, each representing a set of
  * fields that can be autofilled together, and the Android system displays a dataset picker UI
@@ -44,8 +44,8 @@
  * <pre class="prettyprint">
  *  new FillResponse.Builder()
  *      .add(new Dataset.Builder(createPresentation())
- *          .setTextFieldValue(id1, "homer")
- *          .setTextFieldValue(id2, "D'OH!")
+ *          .setValue(id1, AutofillValue.forText("homer"))
+ *          .setValue(id2, AutofillValue.forText("D'OH!"))
  *          .build())
  *      .build();
  * </pre>
@@ -55,48 +55,19 @@
  * <pre class="prettyprint">
  *  new FillResponse.Builder()
  *      .add(new Dataset.Builder(createFirstPresentation())
- *          .setTextFieldValue(id1, "homer")
- *          .setTextFieldValue(id2, "D'OH!")
+ *          .setValue(id1, AutofillValue.forText("homer"))
+ *          .setValue(id2, AutofillValue.forText("D'OH!"))
  *          .build())
  *      .add(new Dataset.Builder(createSecondPresentation())
- *          .setTextFieldValue(id1, "elbarto")
- *          .setTextFieldValue(id2, "cowabonga")
+ *          .setValue(id1, AutofillValue.forText("elbarto")
+ *          .setValue(id2, AutofillValue.forText("cowabonga")
  *          .build())
  *      .build();
  * </pre>
  *
- * <p>If the user does not have any data associated with this {@link android.app.Activity} but
- * the service wants to offer the user the option to save the data that was entered, then the
- * service could populate the response with a {@link SaveInfo} instead of {@link Dataset}s:
- *
- * <pre class="prettyprint">
- *  new FillResponse.Builder()
- *      .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_INFO_TYPE_CREDENTIALS)
- *                   .addSavableFields(id1, id2))
- *      .build();
- * </pre>
- *
- * <p>Similarly, there might be cases where the user data on the service is enough to populate some
- * fields but not all, and the service would still be interested on saving the other fields. In this
- * scenario, the service could populate the response with both {@link Dataset}s and
- * {@link SaveInfo}:
- *
- * <pre class="prettyprint">
- *   new FillResponse.Builder()
- *       .add(new Dataset.Builder(createPresentation())
- *          .setTextFieldValue(id1, "Homer")                  // first name
- *          .setTextFieldValue(id2, "Simpson")                // last name
- *          .setTextFieldValue(id3, "742 Evergreen Terrace")  // street
- *          .setTextFieldValue(id4, "Springfield")            // city
- *          .build())
- *       .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_INFO_TYPE_ADDRESS)
- *                   .addSavableFields(id5, id6)) // state and zipcode
- *       .build();
- *
- * </pre>
- *
- * <p>Notice that the ids that are part of a dataset (ids 1 to 4, in this example) are automatically
- * added to the {@code savableIds} list.
+ * If the service is interested on saving the user-edited data back, it must set a {@link SaveInfo}
+ * in the {@link FillResponse}. Typically, the {@link SaveInfo} contains the same ids as the
+ * {@link Dataset}, but other combinations are possible - see {@link SaveInfo} for more details
  *
  * <p>If the service has multiple {@link Dataset}s for different sections of the activity,
  * for example, a user section for which there are two datasets followed by an address
@@ -113,12 +84,12 @@
  * <pre class="prettyprint">
  *  new FillResponse.Builder()
  *      .add(new Dataset.Builder(createFirstPresentation())
- *          .setTextFieldValue(id1, "Homer")
- *          .setTextFieldValue(id2, "Simpson")
+ *          .setValue(id1, AutofillValue.forText("Homer"))
+ *          .setValue(id2, AutofillValue.forText("Simpson"))
  *          .build())
  *      .add(new Dataset.Builder(createSecondPresentation())
- *          .setTextFieldValue(id1, "Bart")
- *          .setTextFieldValue(id2, "Simpson")
+ *          .setValue(id1, AutofillValue.forText("Bart"))
+ *          .setValue(id2, AutofillValue.forText("Simpson"))
  *          .build())
  *      .build();
  * </pre>
@@ -129,12 +100,12 @@
  * <pre class="prettyprint">
  *  new FillResponse.Builder()
  *      .add(new Dataset.Builder(createThirdPresentation())
- *          .setTextFieldValue(id3, "742 Evergreen Terrace")
- *          .setTextFieldValue(id4, "Springfield")
+ *          .setValue(id3, AutofillValue.forText("742 Evergreen Terrace"))
+ *          .setValue(id4, AutofillValue.forText("Springfield"))
  *          .build())
  *      .add(new Dataset.Builder(createFourthPresentation())
- *          .setTextFieldValue(id3, "Springfield Power Plant")
- *          .setTextFieldValue(id4, "Springfield")
+ *          .setValue(id3, AutofillValue.forText("Springfield Power Plant"))
+ *          .setValue(id4, AutofillValue.forText("Springfield"))
  *          .build())
  *      .build();
  * </pre>
@@ -167,16 +138,7 @@
 
     private FillResponse(@NonNull Builder builder) {
         mDatasets = builder.mDatasets;
-
         mSaveInfo = builder.mSaveInfo;
-        if (mSaveInfo != null) {
-            mSaveInfo.addSavableIds(mDatasets);
-            if (mSaveInfo.getSavableIds() == null) {
-                throw new IllegalArgumentException(
-                        "need to provide at least one savable id on SaveInfo");
-            }
-        }
-
         mExtras = builder.mExtras;
         mPresentation = builder.mPresentation;
         mAuthentication = builder.mAuthentication;
@@ -307,8 +269,8 @@
          * Sets a {@link Bundle} that will be passed to subsequent APIs that
          * manipulate this response. For example, they are passed to subsequent
          * calls to {@link AutofillService#onFillRequest(
-         * android.app.assist.AssistStructure, Bundle, android.os.CancellationSignal,
-         * FillCallback)} and {@link AutofillService#onSaveRequest(
+         * android.app.assist.AssistStructure, Bundle, int,
+         * android.os.CancellationSignal, FillCallback)} and {@link AutofillService#onSaveRequest(
          * android.app.assist.AssistStructure, Bundle, SaveCallback)}.
          *
          * @param extras The response extras.
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index 80685d8..9f296c6 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -31,7 +31,7 @@
 oneway interface IAutoFillService {
     void onInit(in IAutoFillServiceConnection connection);
     void onFillRequest(in AssistStructure structure, in Bundle extras,
-            in IFillCallback callback);
+            in IFillCallback callback, int flags);
     void onSaveRequest(in AssistStructure structure, in Bundle extras,
             in ISaveCallback callback);
 }
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 1bd88c7..6213d27 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -25,28 +25,86 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.ArraySet;
 import android.view.autofill.AutoFillId;
 import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
+import android.view.autofill.AutofillValue;
+
+import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
+import java.util.Arrays;
 
 /**
- * Information used to indicate that a service is interested on saving the user-inputed data for
- * future use.
+ * Information used to indicate that an {@link AutofillService} is interested on saving the
+ * user-inputed data for future use, through a
+ * {@link AutofillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, SaveCallback)}
+ * call.
  *
- * <p>A {@link SaveInfo} is always associated with a {@link FillResponse}.
+ * <p>A {@link SaveInfo} is always associated with a {@link FillResponse}, and it contains at least
+ * two pieces of information:
  *
- * <p>A {@link SaveInfo} must define the type it represents, and contain at least one
- * {@code savableId}. A {@code savableId} is the {@link AutofillId} of a view the service is
- * interested to save in a {@code onSaveRequest()}; the ids of all {@link Dataset} present in the
- * {@link FillResponse} associated with this {@link SaveInfo} are already marked as savable,
- * but additional ids can be added through {@link Builder#addSavableIds(AutofillId...)}.
+ * <ol>
+ *   <li>The type of user data that would be saved (like passoword or credit card info).
+ *   <li>The minimum set of views (represented by their {@link AutofillId}) that need to be changed
+ *       to trigger a save request.
+ * </ol>
  *
- * <p>See {@link AutofillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
- * SaveCallback)} and {@link FillResponse} for more info.
+ *  Typically, the {@link SaveInfo} contains the same {@code id}s as the {@link Dataset}:
+ *
+ * <pre class="prettyprint">
+ *  new FillResponse.Builder()
+ *      .add(new Dataset.Builder(createPresentation())
+ *          .setValue(id1, AutofillValue.forText("homer"))
+ *          .setValue(id2, AutofillValue.forText("D'OH!"))
+ *          .build())
+ *      .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_INFO_TYPE_PASSWORD, new int[] {id1, id2})
+ *                  .build())
+ *      .build();
+ * </pre>
+ *
+ * There might be cases where the {@link AutofillService} knows how to fill the
+ * {@link android.app.Activity}, but the user has no data for it. In that case, the
+ * {@link FillResponse} should contain just the {@link SaveInfo}, but no {@link Dataset}s:
+ *
+ * <pre class="prettyprint">
+ *  new FillResponse.Builder()
+ *      .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_INFO_TYPE_PASSWORD, new int[] {id1, id2})
+ *                  .build())
+ *      .build();
+ * </pre>
+ *
+ * <p>There might be cases where the user data in the {@link AutofillService} is enough
+ * to populate some fields but not all, and the service would still be interested on saving the
+ * other fields. In this scenario, the service could set the
+ * {@link SaveInfo.Builder#setOptionalIds(AutofillId[])} as well:
+ *
+ * <pre class="prettyprint">
+ *   new FillResponse.Builder()
+ *       .add(new Dataset.Builder(createPresentation())
+ *          .setValue(id1, AutofillValue.forText("742 Evergreen Terrace"))  // street
+ *          .setValue(id2, AutofillValue.forText("Springfield"))            // city
+ *          .build())
+ *       .setSaveInfo(new SaveInfo.Builder(SaveInfo.SAVE_INFO_TYPE_ADDRESS, new int[] {id1, id2})
+ *                   .setOptionalIds(new int[] {id3, id4}) // state and zipcode
+ *                   .build())
+ *       .build();
+ * </pre>
+ *
+ * The
+ * {@link AutofillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, SaveCallback)}
+ * is triggered after a call to {@link AutofillManager#commit()}, but only when all conditions
+ * below are met:
+ *
+ * <ol>
+ *   <li>The {@link SaveInfo} associated with the {@link FillResponse} is not {@code null}.
+ *   <li>The {@link AutofillValue} of all required views (as set by the {@code requiredIds} passed
+ *       to {@link SaveInfo.Builder} constructor are not empty.
+ *   <li>The {@link AutofillValue} of at least one view (be it required or optional) has changed
+ *       (i.e., it's not the same value passed in a {@link Dataset}).
+ *   <li>The user explicitly tapped the affordance asking to save data for autofill.
+ * </ol>
  */
 public final class SaveInfo implements Parcelable {
 
@@ -61,7 +119,6 @@
      */
     public static final int SAVE_DATA_TYPE_PASSWORD = 1;
 
-
     /**
      * Type used on when the {@link FillResponse} represents a physical address (such as street,
      * city, state, etc).
@@ -74,9 +131,10 @@
     public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3;
 
     private final @SaveDataType int mType;
-    private CharSequence mNegativeActionTitle;
-    private IntentSender mNegativeActionListener;
-    private ArraySet<AutofillId> mSavableIds;
+    private final CharSequence mNegativeActionTitle;
+    private final IntentSender mNegativeActionListener;
+    private final AutofillId[] mRequiredIds;
+    private final AutofillId[] mOptionalIds;
     private final CharSequence mDescription;
 
     /** @hide */
@@ -94,7 +152,8 @@
         mType = builder.mType;
         mNegativeActionTitle = builder.mNegativeActionTitle;
         mNegativeActionListener = builder.mNegativeActionListener;
-        mSavableIds = builder.mSavableIds;
+        mRequiredIds = builder.mRequiredIds;
+        mOptionalIds = builder.mOptionalIds;
         mDescription = builder.mDescription;
     }
 
@@ -109,8 +168,13 @@
     }
 
     /** @hide */
-    public @Nullable ArraySet<AutofillId> getSavableIds() {
-        return mSavableIds;
+    public AutofillId[] getRequiredIds() {
+        return mRequiredIds;
+    }
+
+    /** @hide */
+    public @Nullable AutofillId[] getOptionalIds() {
+        return mOptionalIds;
     }
 
     /** @hide */
@@ -123,25 +187,6 @@
         return mDescription;
     }
 
-    /** @hide */
-    public void addSavableIds(@Nullable ArrayList<Dataset> datasets) {
-        if (datasets != null) {
-            for (Dataset dataset : datasets) {
-                final ArrayList<AutofillId> ids = dataset.getFieldIds();
-                if (ids != null) {
-                    final int fieldCount = ids.size();
-                    for (int i = 0; i < fieldCount; i++) {
-                        final AutofillId id = ids.get(i);
-                        if (mSavableIds == null) {
-                            mSavableIds = new ArraySet<>();
-                        }
-                        mSavableIds.add(id);
-                    }
-                }
-            }
-        }
-    }
-
     /**
      * A builder for {@link SaveInfo} objects.
      */
@@ -150,7 +195,9 @@
         private final @SaveDataType int mType;
         private CharSequence mNegativeActionTitle;
         private IntentSender mNegativeActionListener;
-        private ArraySet<AutofillId> mSavableIds;
+        // TODO(b/33197203): make mRequiredIds final once addSavableIds() is gone
+        private AutofillId[] mRequiredIds;
+        private AutofillId[] mOptionalIds;
         private CharSequence mDescription;
         private boolean mDestroyed;
 
@@ -161,8 +208,17 @@
          * be {@link SaveInfo#SAVE_DATA_TYPE_GENERIC}, {@link SaveInfo#SAVE_DATA_TYPE_PASSWORD},
          * {@link SaveInfo#SAVE_DATA_TYPE_ADDRESS}, or {@link SaveInfo#SAVE_DATA_TYPE_CREDIT_CARD};
          * otherwise it will assume {@link SaveInfo#SAVE_DATA_TYPE_GENERIC}.
+         * @param requiredIds ids of all required views that will trigger a save request.
+         *
+         * <p>See {@link SaveInfo} for more info.
+         *
+         * @throws IllegalArgumentException if {@code requiredIds} is {@code null} or empty.
          */
-        public Builder(@SaveDataType int type) {
+        public Builder(@SaveDataType int type, @NonNull AutofillId[] requiredIds) {
+            if (false) {// TODO(b/33197203): re-move when clients use it
+            Preconditions.checkArgument(requiredIds != null && requiredIds.length > 0,
+                    "must have at least one required id: " + Arrays.toString(requiredIds));
+            }
             switch (type) {
                 case SAVE_DATA_TYPE_PASSWORD:
                 case SAVE_DATA_TYPE_ADDRESS:
@@ -172,28 +228,43 @@
                 default:
                     mType = SAVE_DATA_TYPE_GENERIC;
             }
+            mRequiredIds = requiredIds;
         }
 
         /**
-         * Adds ids of additional views the service would be interested to save, but were not
-         * indirectly set through {@link FillResponse.Builder#addDataset(Dataset)}.
-         *
-         * @param ids The savable ids.
-         * @return This builder.
-         *
-         * @see FillResponse
+         * @hide
+         * @deprecated
+         * // TODO(b/33197203): make sure is removed when clients migrated
          */
+        @Deprecated
+        public Builder(@SaveDataType int type) {
+            this(type, null);
+        }
+
+        /**
+         * @hide
+         * @deprecated
+         * // TODO(b/33197203): make sure is removed when clients migrated
+         */
+        @Deprecated
         public @NonNull Builder addSavableIds(@Nullable AutofillId... ids) {
             throwIfDestroyed();
+            mRequiredIds = ids;
+            return this;
+        }
 
-            if (ids == null) {
-                return this;
-            }
-            for (AutofillId id : ids) {
-                if (mSavableIds == null) {
-                    mSavableIds = new ArraySet<>();
-                }
-                mSavableIds.add(id);
+        /**
+         * Sets the ids of additional, optional views the service would be interested to save.
+         *
+         * <p>See {@link SaveInfo} for more info.
+         *
+         * @param ids The ids of the optional views.
+         * @return This builder.
+         */
+        public @NonNull Builder setOptionalIds(@Nullable AutofillId[] ids) {
+            throwIfDestroyed();
+            if (ids != null && ids.length != 0) {
+                mOptionalIds = ids;
             }
             return this;
         }
@@ -206,14 +277,14 @@
         public @NonNull Builder addSavableIds(@Nullable AutoFillId... ids) {
             throwIfDestroyed();
 
-            if (ids == null) {
+            if (ids == null || ids.length == 0) {
                 return this;
             }
-            for (AutoFillId id : ids) {
-                if (mSavableIds == null) {
-                    mSavableIds = new ArraySet<>();
-                }
-                mSavableIds.add(id.getDaRealId());
+            if (mRequiredIds == null) {
+                mRequiredIds = new AutofillId[ids.length];
+            }
+            for (int i = 0; i < ids.length; i++) {
+                mRequiredIds[i] = ids[i].getDaRealId();
             }
             return this;
         }
@@ -228,6 +299,7 @@
          * @return This Builder.
          */
         public @NonNull Builder setDescription(@Nullable CharSequence description) {
+            throwIfDestroyed();
             mDescription = description;
             return this;
         }
@@ -293,7 +365,9 @@
         if (!DEBUG) return super.toString();
 
         return new StringBuilder("SaveInfo: [type=").append(mType)
-                .append(", savableIds=").append(mSavableIds)
+                .append(", requiredIds=").append(Arrays.toString(mRequiredIds))
+                .append(", optionalIds=").append(Arrays.toString(mOptionalIds))
+                .append(", description=").append(mDescription)
                 .append("]").toString();
     }
 
@@ -309,9 +383,10 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeInt(mType);
+        parcel.writeParcelableArray(mRequiredIds, flags);
         parcel.writeCharSequence(mNegativeActionTitle);
         parcel.writeParcelable(mNegativeActionListener, flags);
-        parcel.writeTypedArraySet(mSavableIds, flags);
+        parcel.writeParcelableArray(mOptionalIds, flags);
         parcel.writeCharSequence(mDescription);
     }
 
@@ -321,13 +396,10 @@
             // Always go through the builder to ensure the data ingested by
             // the system obeys the contract of the builder to avoid attacks
             // using specially crafted parcels.
-            final Builder builder = new Builder(parcel.readInt());
+            final Builder builder = new Builder(parcel.readInt(),
+                    parcel.readParcelableArray(null, AutofillId.class));
             builder.setNegativeAction(parcel.readCharSequence(), parcel.readParcelable(null));
-            final ArraySet<AutofillId> savableIds = parcel.readTypedArraySet(null);
-            final int savableIdsCount = (savableIds != null) ? savableIds.size() : 0;
-            for (int i = 0; i < savableIdsCount; i++) {
-                builder.addSavableIds(savableIds.valueAt(i));
-            }
+            builder.setOptionalIds(parcel.readParcelableArray(null, AutofillId.class));
             builder.setDescription(parcel.readCharSequence());
             return builder.build();
         }
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 1087851..04596fa 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -16,42 +16,57 @@
 
 package android.text;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
+import java.lang.annotation.Retention;
+import java.util.Arrays;
+
 
 /**
  * Font configuration descriptions for System fonts.
  */
 public final class FontConfig implements Parcelable {
-    private final List<Family> mFamilies = new ArrayList<>();
-    private final List<Alias> mAliases = new ArrayList<>();
+    private final @NonNull Family[] mFamilies;
+    private final @NonNull Alias[] mAliases;
 
-    public FontConfig() {
+    public FontConfig(@NonNull Family[] families, @NonNull Alias[] aliases) {
+        mFamilies = families;
+        mAliases = aliases;
     }
 
-    public FontConfig(FontConfig config) {
-        for (int i = 0; i < config.mFamilies.size(); i++) {
-            mFamilies.add(new Family(config.mFamilies.get(i)));
+    /**
+     * For duplicating file descriptors.
+     *
+     * Note that this copy constructor can not be usable for deep copy.
+     * @hide
+     */
+    public FontConfig(@NonNull FontConfig config) {
+        mFamilies = new Family[config.mFamilies.length];
+        for (int i = 0; i < config.mFamilies.length; ++i) {
+            mFamilies[i] = new Family(config.mFamilies[i]);
         }
-        mAliases.addAll(config.mAliases);
+        mAliases = Arrays.copyOf(config.mAliases, config.mAliases.length);
     }
 
     /**
      * Returns the ordered list of families included in the system fonts.
      */
-    public List<Family> getFamilies() {
+    public @NonNull Family[] getFamilies() {
         return mFamilies;
     }
 
     /**
      * Returns the list of aliases defined for the font families in the system fonts.
      */
-    public List<Alias> getAliases() {
+    public @NonNull Alias[] getAliases() {
         return mAliases;
     }
 
@@ -59,33 +74,14 @@
      * @hide
      */
     public FontConfig(Parcel in) {
-        readFromParcel(in);
+        mFamilies = in.readTypedArray(Family.CREATOR);
+        mAliases = in.readTypedArray(Alias.CREATOR);
     }
 
     @Override
     public void writeToParcel(Parcel out, int flag) {
-        out.writeInt(mFamilies.size());
-        for (int i = 0; i < mFamilies.size(); i++) {
-            mFamilies.get(i).writeToParcel(out, flag);
-        }
-        out.writeInt(mAliases.size());
-        for (int i = 0; i < mAliases.size(); i++) {
-            mAliases.get(i).writeToParcel(out, flag);
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public void readFromParcel(Parcel in) {
-        int size = in.readInt();
-        for (int i = 0; i < size; i++) {
-            mFamilies.add(new Family(in));
-        }
-        size = in.readInt();
-        for (int i = 0; i < size; i++) {
-            mAliases.add(new Alias(in));
-        }
+        out.writeTypedArray(mFamilies, flag);
+        out.writeTypedArray(mAliases, flag);
     }
 
     @Override
@@ -164,36 +160,37 @@
      * Class that holds information about a Font.
      */
     public static final class Font implements Parcelable {
-        private String mFontName;
+        private final @NonNull String mFontName;
         private final int mTtcIndex;
-        private final List<Axis> mAxes;
+        private final @NonNull Axis[] mAxes;
         private final int mWeight;
         private final boolean mIsItalic;
-        private ParcelFileDescriptor mFd;
-        private final int mResourceId;
+        private @Nullable ParcelFileDescriptor mFd;
 
         /**
          * @hide
          */
-        public Font(String fontName, int ttcIndex, List<Axis> axes, int weight, boolean isItalic,
-                int resourceId) {
+        public Font(@NonNull String fontName, int ttcIndex, @NonNull Axis[] axes, int weight,
+                boolean isItalic) {
             mFontName = fontName;
             mTtcIndex = ttcIndex;
             mAxes = axes;
             mWeight = weight;
             mIsItalic = isItalic;
             mFd = null;
-            mResourceId = resourceId;
         }
 
-        public Font(String fontName, int ttcIndex, List<Axis> axes, int weight, boolean isItalic) {
-            this(fontName, ttcIndex, axes, weight, isItalic, 0);
-        }
-
+        /**
+         * This is for duplicating FileDescriptors.
+         *
+         * Note that this copy ctor doesn't deep copy the members.
+         *
+         * @hide
+         */
         public Font(Font origin) {
             mFontName = origin.mFontName;
             mTtcIndex = origin.mTtcIndex;
-            mAxes = new ArrayList<>(origin.mAxes);
+            mAxes = origin.mAxes;
             mWeight = origin.mWeight;
             mIsItalic = origin.mIsItalic;
             if (origin.mFd != null) {
@@ -203,24 +200,16 @@
                     e.printStackTrace();
                 }
             }
-            mResourceId = origin.mResourceId;
         }
 
         /**
          * Returns the name associated by the system to this font.
          */
-        public String getFontName() {
+        public @NonNull String getFontName() {
             return mFontName;
         }
 
         /**
-         * @hide
-         */
-        public void setFontName(String fontName) {
-            mFontName = fontName;
-        }
-
-        /**
          * Returns the index to be used to access this font when accessing a TTC file.
          */
         public int getTtcIndex() {
@@ -230,7 +219,7 @@
         /**
          * Returns the list of axes associated to this font.
          */
-        public List<Axis> getAxes() {
+        public @NonNull Axis[] getAxes() {
             return mAxes;
         }
 
@@ -251,35 +240,24 @@
         /**
          * Returns a file descriptor to access the specified font. This should be closed after use.
          */
-        public ParcelFileDescriptor getFd() {
+        public @Nullable ParcelFileDescriptor getFd() {
             return mFd;
         }
 
         /**
          * @hide
          */
-        public void setFd(ParcelFileDescriptor fd) {
+        public void setFd(@NonNull ParcelFileDescriptor fd) {
             mFd = fd;
         }
 
         /**
          * @hide
          */
-        public int getResourceId() {
-            return mResourceId;
-        }
-
-        /**
-         * @hide
-         */
         public Font(Parcel in) {
             mFontName = in.readString();
             mTtcIndex = in.readInt();
-            final int numAxes = in.readInt();
-            mAxes = new ArrayList<>();
-            for (int i = 0; i < numAxes; i++) {
-                mAxes.add(new Axis(in));
-            }
+            mAxes = in.createTypedArray(Axis.CREATOR);
             mWeight = in.readInt();
             mIsItalic = in.readInt() == 1;
             if (in.readInt() == 1) { /* has FD */
@@ -287,24 +265,19 @@
             } else {
                 mFd = null;
             }
-            mResourceId = in.readInt();
         }
 
         @Override
         public void writeToParcel(Parcel out, int flag) {
             out.writeString(mFontName);
             out.writeInt(mTtcIndex);
-            out.writeInt(mAxes.size());
-            for (int i = 0; i < mAxes.size(); i++) {
-                mAxes.get(i).writeToParcel(out, flag);
-            }
+            out.writeTypedArray(mAxes, flag);
             out.writeInt(mWeight);
             out.writeInt(mIsItalic ? 1 : 0);
             out.writeInt(mFd == null ? 0 : 1);
             if (mFd != null) {
                 mFd.writeToParcel(out, flag);
             }
-            out.writeInt(mResourceId);
         }
 
         @Override
@@ -329,27 +302,27 @@
      * Class that holds information about a Font alias.
      */
     public static final class Alias implements Parcelable {
-        private final String mName;
-        private final String mToName;
+        private final @NonNull String mName;
+        private final @NonNull String mToName;
         private final int mWeight;
 
-        public Alias(String name, String toName, int weight) {
-            this.mName = name;
-            this.mToName = toName;
-            this.mWeight = weight;
+        public Alias(@NonNull String name, @NonNull String toName, int weight) {
+            mName = name;
+            mToName = toName;
+            mWeight = weight;
         }
 
         /**
          * Returns the new name for the alias.
          */
-        public String getName() {
+        public @NonNull String getName() {
             return mName;
         }
 
         /**
          * Returns the existing name to which this alias points to.
          */
-        public String getToName() {
+        public @NonNull String getToName() {
             return mToName;
         }
 
@@ -398,149 +371,110 @@
      * Class that holds information about a Font family.
      */
     public static final class Family implements Parcelable {
-        private final String mName;
-        private final List<Font> mFonts;
-        private final String mLanguage;
-        private final String mVariant;
-        private final String mProviderAuthority;
-        private final String mProviderPackage;
-        private final String mQuery;
+        private final @NonNull String mName;
+        private final @NonNull Font[] mFonts;
+        private final @NonNull String mLanguage;
 
-        public Family(String name, List<Font> fonts, String language, String variant) {
+        /** @hide */
+        @Retention(SOURCE)
+        @IntDef({VARIANT_DEFAULT, VARIANT_COMPACT, VARIANT_ELEGANT})
+        public @interface Variant {}
+
+        /**
+         * Value for font variant.
+         *
+         * Indicates the font has no variant attribute.
+         */
+        public static final int VARIANT_DEFAULT = 0;
+
+        /**
+         * Value for font variant.
+         *
+         * Indicates the font is for compact variant.
+         * @see android.graphics.Paint#setElegantTextHeight
+         */
+        public static final int VARIANT_COMPACT = 1;
+
+        /**
+         * Value for font variant.
+         *
+         * Indiates the font is for elegant variant.
+         * @see android.graphics.Paint#setElegantTextHeight
+         */
+        public static final int VARIANT_ELEGANT = 2;
+
+        // Must be same with Minikin's variant values.
+        // See frameworks/minikin/include/minikin/FontFamily.h
+        private final @Variant int mVariant;
+
+        public Family(@NonNull String name, @NonNull Font[] fonts, @NonNull String language,
+                @Variant int variant) {
             mName = name;
             mFonts = fonts;
             mLanguage = language;
             mVariant = variant;
-            mProviderAuthority = null;
-            mProviderPackage = null;
-            mQuery = null;
         }
 
         /**
+         * For duplicating file descriptor underlying Font object.
+         *
+         * This copy constructor is not for deep copying.
          * @hide
          */
-        public Family(String providerAuthority, String providerPackage, String query) {
-            mName = null;
-            mFonts = null;
-            mLanguage = null;
-            mVariant = null;
-            mProviderAuthority = providerAuthority;
-            mProviderPackage = providerPackage;
-            mQuery = query;
-        }
-
         public Family(Family origin) {
             mName = origin.mName;
             mLanguage = origin.mLanguage;
             mVariant = origin.mVariant;
-            mFonts = new ArrayList<>();
-            for (int i = 0; i < origin.mFonts.size(); i++) {
-                mFonts.add(new Font(origin.mFonts.get(i)));
+            mFonts = new Font[origin.mFonts.length];
+            for (int i = 0; i < origin.mFonts.length; ++i) {
+                mFonts[i] = new Font(origin.mFonts[i]);
             }
-            mProviderAuthority = origin.mProviderAuthority;
-            mProviderPackage = origin.mProviderPackage;
-            mQuery = origin.mQuery;
         }
 
         /**
          * Returns the name given by the system to this font family.
          */
-        public String getName() {
+        public @Nullable String getName() {
             return mName;
         }
 
         /**
          * Returns the list of fonts included in this family.
          */
-        public List<Font> getFonts() {
+        public @Nullable Font[] getFonts() {
             return mFonts;
         }
 
         /**
          * Returns the language for this family. May be null.
          */
-        public String getLanguage() {
+        public @Nullable String getLanguage() {
             return mLanguage;
         }
 
         /**
          * Returns the font variant for this family, e.g. "elegant" or "compact". May be null.
          */
-        public String getVariant() {
+        public @Variant int getVariant() {
             return mVariant;
         }
 
         /**
          * @hide
          */
-        public String getProviderAuthority() {
-            return mProviderAuthority;
-        }
-
-        /**
-         * @hide
-         */
-        public String getProviderPackage() {
-            return mProviderPackage;
-        }
-
-        /**
-         * @hide
-         */
-        public String getQuery() {
-            return mQuery;
-        }
-
-        /**
-         * @hide
-         */
         public Family(Parcel in) {
             mName = in.readString();
-            final int size = in.readInt();
-            mFonts = new ArrayList<>();
-            for (int i = 0; i < size; i++) {
-                mFonts.add(new Font(in));
-            }
+            mFonts = in.readTypedArray(Font.CREATOR);
             mLanguage = in.readString();
-            mVariant = in.readString();
-            if (in.readInt() == 1) {
-                mProviderAuthority = in.readString();
-            } else {
-                mProviderAuthority = null;
-            }
-            if (in.readInt() == 1) {
-                mProviderPackage = in.readString();
-            } else {
-                mProviderPackage = null;
-            }
-            if (in.readInt() == 1) {
-                mQuery = in.readString();
-            } else {
-                mQuery = null;
-            }
+            mVariant = in.readInt();
         }
 
         @Override
         public void writeToParcel(Parcel out, int flag) {
             out.writeString(mName);
-            out.writeInt(mFonts.size());
-            for (int i = 0; i < mFonts.size(); i++) {
-                mFonts.get(i).writeToParcel(out, flag);
-            }
+            out.writeTypedArray(mFonts, flag);
             out.writeString(mLanguage);
-            out.writeString(mVariant);
-            out.writeInt(mProviderAuthority == null ? 0 : 1);
-            if (mProviderAuthority != null) {
-                out.writeString(mProviderAuthority);
-            }
-            out.writeInt(mProviderPackage == null ? 0 : 1);
-            if (mProviderPackage != null) {
-                out.writeString(mProviderPackage);
-            }
-            out.writeInt(mQuery == null ? 0 : 1);
-            if (mQuery != null) {
-                out.writeString(mQuery);
-            }
+            out.writeInt(mVariant);
         }
 
         @Override
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 94c463c..353dfed 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -1031,6 +1031,7 @@
                                    float avail, TextUtils.TruncateAt where,
                                    int line, float textWidth, TextPaint paint,
                                    boolean forceEllipsis) {
+        avail -= getTotalInsets(line);
         if (textWidth <= avail && !forceEllipsis) {
             // Everything fits!
             mLines[mColumns * line + ELLIPSIS_START] = 0;
@@ -1134,6 +1135,17 @@
         mLines[mColumns * line + ELLIPSIS_COUNT] = ellipsisCount;
     }
 
+    private float getTotalInsets(int line) {
+        int totalIndent = 0;
+        if (mLeftIndents != null) {
+            totalIndent = mLeftIndents[Math.min(line, mLeftIndents.length - 1)];
+        }
+        if (mRightIndents != null) {
+            totalIndent += mRightIndents[Math.min(line, mRightIndents.length - 1)];
+        }
+        return totalIndent;
+    }
+
     // Override the base class so we can directly access our members,
     // rather than relying on member functions.
     // The logic mirrors that of Layout.getLineForVertical
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index af2547e..255a029 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -799,8 +799,10 @@
      * targetId list. If the target parameter is null, then the target list
      * is not checked (this is in the case of ListView items, where the
      * views are ignored and only the ids are used).
+     *
+     * @hide
      */
-    boolean isValidTarget(View target) {
+    public boolean isValidTarget(View target) {
         if (target == null) {
             return false;
         }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 27c6d08..586b3b2 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -209,10 +209,10 @@
     int getDefaultDisplayRotation();
 
     /**
-     * Watch the rotation of the screen.  Returns the current rotation,
+     * Watch the rotation of the specified screen.  Returns the current rotation,
      * calls back when it changes.
      */
-    int watchRotation(IRotationWatcher watcher);
+    int watchRotation(IRotationWatcher watcher, int displayId);
 
     /**
      * Remove a rotation watcher set using watchRotation.
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 1facc10..7e6af11 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -98,31 +98,6 @@
             out Rect outOutsets, out Rect outBackdropFrame, out Configuration outConfig,
             out Surface outSurface);
 
-    /**
-     *  Position a window relative to it's parent (attached) window without triggering
-     *  a full relayout. This action may be deferred until a given frame number
-     *  for the parent window appears. This allows for synchronizing movement of a child
-     *  to repainting the contents of the parent.
-     *
-     *  "width" and "height" correspond to the width and height members of
-     *  WindowManager.LayoutParams in the {@link #relayout relayout()} case.
-     *  This may differ from the surface buffer size in the
-     *  case of {@link LayoutParams#FLAG_SCALED} and {@link #relayout relayout()}
-     *  must be used with requestedWidth/height if this must be changed.
-     *
-     *  @param window The window being modified. Must be attached to a parent window
-     *  or this call will fail.
-     *  @param left The new left position
-     *  @param top The new top position
-     *  @param right The new right position
-     *  @param bottom The new bottom position
-     *  @param deferTransactionUntilFrame Frame number from our parent (attached) to
-     *  defer this action until.
-     *  @param outFrame Rect in which is placed the new position/size on screen.
-     */
-    void repositionChild(IWindow childWindow, int left, int top, int right, int bottom,
-            long deferTransactionUntilFrame, out Rect outFrame);
-
     /*
      * Notify the window manager that an application is relaunching and
      * windows should be prepared for replacement.
@@ -134,12 +109,6 @@
     void prepareToReplaceWindows(IBinder appToken, boolean childrenOnly);
 
     /**
-     * If a call to relayout() asked to have the surface destroy deferred,
-     * it must call this once it is okay to destroy that surface.
-     */
-    void performDeferredDestroy(IWindow window);
-
-    /**
      * Called by a client to report that it ran out of graphics memory.
      */
     boolean outOfMemory(IWindow window);
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index e3ac40c..0e06cd3 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -833,6 +833,7 @@
 
         final int depth = parser.getDepth();
         int type;
+        boolean pendingRequestFocus = false;
 
         while (((type = parser.next()) != XmlPullParser.END_TAG ||
                 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
@@ -844,7 +845,8 @@
             final String name = parser.getName();
 
             if (TAG_REQUEST_FOCUS.equals(name)) {
-                parseRequestFocus(parser, parent);
+                pendingRequestFocus = true;
+                consumeChildElements(parser);
             } else if (TAG_TAG.equals(name)) {
                 parseViewTag(parser, parent, attrs);
             } else if (TAG_INCLUDE.equals(name)) {
@@ -863,23 +865,16 @@
             }
         }
 
+        if (pendingRequestFocus) {
+            parent.restoreDefaultFocus();
+        }
+
         if (finishInflate) {
             parent.onFinishInflate();
         }
     }
 
     /**
-     * Parses a <code>&lt;request-focus&gt;</code> element and requests focus on
-     * the containing View.
-     */
-    private void parseRequestFocus(XmlPullParser parser, View view)
-            throws XmlPullParserException, IOException {
-        view.requestFocus();
-
-        consumeChildElements(parser);
-    }
-
-    /**
      * Parses a <code>&lt;tag&gt;</code> element and sets a keyed tag on the
      * containing View.
      */
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 61b1247..6d320ef 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -500,7 +500,12 @@
                         // While creating the surface, we will set it's initial
                         // geometry. Outside of that though, we should generally
                         // leave it to the RenderThread.
-                        if (creating || !mRtHandlingPositionUpdates) {
+                        //
+                        // 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,
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index aa1cbf2..80f6c32 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -978,142 +978,123 @@
      */
     public static final int AUTOFILL_MODE_MANUAL = 2;
 
-    /** @hide */
-    @IntDef({
-            AUTOFILL_HINT_NONE,
-            AUTOFILL_HINT_EMAIL_ADDRESS,
-            AUTOFILL_HINT_NAME,
-            AUTOFILL_HINT_POSTAL_ADDRESS,
-            AUTOFILL_HINT_PASSWORD,
-            AUTOFILL_HINT_PHONE,
-            AUTOFILL_HINT_USERNAME,
-            AUTOFILL_HINT_POSTAL_CODE,
-            AUTOFILL_HINT_CREDIT_CARD_NUMBER,
-            AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE,
-            AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE,
-            AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH,
-            AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR,
-            AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface AutofillHint {}
-
-    /**
-     * No autofill hint is set.
-     *
-     * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
-     * {@code android:autofillHint}.
-     */
-    public static final int AUTOFILL_HINT_NONE = 0;
-
     /**
      * This view contains an email address.
      *
-     * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
-     * {@code android:autofillHint}.
+     * Use with {@link #setAutofillHint(String[])}, or set "{@value #AUTOFILL_HINT_EMAIL_ADDRESS}"
+     * to <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
      */
-    public static final int AUTOFILL_HINT_EMAIL_ADDRESS = 0x1;
+    public static final String AUTOFILL_HINT_EMAIL_ADDRESS = "emailAddress";
 
     /**
      * The view contains a real name.
      *
-     * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
-     * {@code android:autofillHint}.
+     * Use with {@link #setAutofillHint(String[])}, or set "{@value #AUTOFILL_HINT_NAME}" to
+     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
      */
-    public static final int AUTOFILL_HINT_NAME = 0x2;
+    public static final String AUTOFILL_HINT_NAME = "name";
 
     /**
      * The view contains a user name.
      *
-     * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
-     * {@code android:autofillHint}.
+     * Use with {@link #setAutofillHint(String[])}, or set "{@value #AUTOFILL_HINT_USERNAME}" to
+     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
      */
-    public static final int AUTOFILL_HINT_USERNAME = 0x4;
+    public static final String AUTOFILL_HINT_USERNAME = "username";
 
     /**
      * The view contains a password.
      *
-     * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
-     * {@code android:autofillHint}.
+     * Use with {@link #setAutofillHint(String[])}, or set "{@value #AUTOFILL_HINT_PASSWORD}" to
+     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
      */
-    public static final int AUTOFILL_HINT_PASSWORD = 0x8;
+    public static final String AUTOFILL_HINT_PASSWORD = "password";
 
     /**
      * The view contains a phone number.
      *
-     * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
-     * {@code android:autofillHint}.
+     * Use with {@link #setAutofillHint(String[])}, or set "{@value #AUTOFILL_HINT_PHONE}" to
+     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
      */
-    public static final int AUTOFILL_HINT_PHONE = 0x10;
+    public static final String AUTOFILL_HINT_PHONE = "phone";
 
     /**
      * The view contains a postal address.
      *
-     * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
-     * {@code android:autofillHint}.
+     * Use with {@link #setAutofillHint(String[])}, or set "{@value #AUTOFILL_HINT_POSTAL_ADDRESS}"
+     * to <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
      */
-    public static final int AUTOFILL_HINT_POSTAL_ADDRESS = 0x20;
+    public static final String AUTOFILL_HINT_POSTAL_ADDRESS = "postalAddress";
 
     /**
      * The view contains a postal code.
      *
-     * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
-     * {@code android:autofillHint}.
+     * Use with {@link #setAutofillHint(String[])}, or set "{@value #AUTOFILL_HINT_POSTAL_CODE}" to
+     * <a href="#attr_android:autofillHint"> {@code android:autofillHint}.
      */
-    public static final int AUTOFILL_HINT_POSTAL_CODE = 0x40;
+    public static final String AUTOFILL_HINT_POSTAL_CODE = "postalCode";
 
     /**
      * The view contains a credit card number.
      *
-     * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
-     * {@code android:autofillHint}.
+     * Use with {@link #setAutofillHint(String[])}, or set "{@value
+     * #AUTOFILL_HINT_CREDIT_CARD_NUMBER}" to <a href="#attr_android:autofillHint"> {@code
+     * android:autofillHint}.
      */
-    public static final int AUTOFILL_HINT_CREDIT_CARD_NUMBER = 0x80;
+    public static final String AUTOFILL_HINT_CREDIT_CARD_NUMBER = "creditCardNumber";
 
     /**
      * The view contains a credit card security code.
      *
-     * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
-     * {@code android:autofillHint}.
+     * Use with {@link #setAutofillHint(String[])}, or set "{@value
+     * #AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE}" to <a href="#attr_android:autofillHint"> {@code
+     * android:autofillHint}.
      */
-    public static final int AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = 0x100;
+    public static final String AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE = "creditCardSecurityCode";
 
     /**
      * The view contains a credit card expiration date.
      *
-     * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
-     * {@code android:autofillHint}.
+     * Use with {@link #setAutofillHint(String[])}, or set "{@value
+     * #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE}" to <a href="#attr_android:autofillHint"> {@code
+     * android:autofillHint}.
      */
-    public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE = 0x200;
+    public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE =
+            "creditCardExpirationDate";
 
     /**
      * The view contains the month a credit card expires.
      *
-     * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
-     * {@code android:autofillHint}.
+     * Use with {@link #setAutofillHint(String[])}, or set "{@value
+     * #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH}" to <a href="#attr_android:autofillHint"> {@code
+     * android:autofillHint}.
      */
-    public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH = 0x400;
+    public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH =
+            "creditCardExpirationMonth";
 
     /**
      * The view contains the year a credit card expires.
      *
-     * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
-     * {@code android:autofillHint}.
+     * Use with {@link #setAutofillHint(String[])}, or set "{@value
+     * #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}" to <a href="#attr_android:autofillHint"> {@code
+     * android:autofillHint}.
      */
-    public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR = 0x800;
+    public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR =
+            "creditCardExpirationYear";
 
     /**
      * The view contains the day a credit card expires.
      *
-     * Use with {@link #setAutofillHint(int)} and <a href="#attr_android:autofillHint">
-     * {@code android:autofillHint}.
+     * Use with {@link #setAutofillHint(String[])}, or set "{@value
+     * #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY}" to <a href="#attr_android:autofillHint"> {@code
+     * android:autofillHint}.
      */
-    public static final int AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = 0x1000;
+    public static final String AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY = "creditCardExpirationDay";
 
     /**
-     * Hint for the autofill services that describes the content of the view.
+     * Hintd for the autofill services that describes the content of the view.
      */
-    @AutofillHint private int mAutofillHint;
+    private @Nullable String[] mAutofillHint;
 
     /** @hide */
     @IntDef({
@@ -5049,7 +5030,37 @@
                     break;
                 case R.styleable.View_autofillHint:
                     if (a.peekValue(attr) != null) {
-                        setAutofillHint(a.getInt(attr, AUTOFILL_HINT_NONE));
+                        CharSequence[] rawHints = null;
+                        String rawString = null;
+
+                        if (a.getType(attr) == TypedValue.TYPE_REFERENCE) {
+                            int resId = a.getResourceId(attr, 0);
+
+                            try {
+                                rawHints = a.getTextArray(attr);
+                            } catch (NullPointerException e) {
+                                rawString = getResources().getString(resId);
+                            }
+                        } else {
+                            rawString = a.getString(attr);
+                        }
+
+                        if (rawHints == null) {
+                            if (rawString == null) {
+                                throw new IllegalArgumentException(
+                                        "Could not resolve autofillHint");
+                            } else {
+                                rawHints = rawString.split(",");
+                            }
+                        }
+
+                        String[] hints = new String[rawHints.length];
+
+                        int numHints = rawHints.length;
+                        for (int rawHintNum = 0; rawHintNum < numHints; rawHintNum++) {
+                            hints[rawHintNum] = rawHints[rawHintNum].toString().trim();
+                        }
+                        setAutofillHint(hints);
                     }
                     break;
                 case R.styleable.View_importantForAutofill:
@@ -6132,6 +6143,9 @@
         }
 
         sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+
+        notifyEnterOrExitForAutoFillIfNeeded(true);
+
         return result;
     }
 
@@ -6791,14 +6805,18 @@
             mAttachInfo.mKeyDispatchState.reset(this);
         }
 
+        notifyEnterOrExitForAutoFillIfNeeded(gainFocus);
+    }
+
+    private void notifyEnterOrExitForAutoFillIfNeeded(boolean enter) {
         if (isAutofillable() && isAttachedToWindow()
                 && getResolvedAutofillMode() == AUTOFILL_MODE_AUTO) {
             AutofillManager afm = getAutofillManager();
             if (afm != null) {
-                if (gainFocus) {
-                    afm.startAutofillRequest(this);
-                } else {
-                    afm.stopAutofillRequest(this);
+                if (enter && hasWindowFocus() && isFocused()) {
+                    afm.notifyViewEntered(this);
+                } else if (!hasWindowFocus() || !isFocused()) {
+                    afm.notifyViewExited(this);
                 }
             }
         }
@@ -7250,9 +7268,10 @@
      * Called when assist structure is being retrieved from a view as part of an autofill request.
      *
      * <p>This method already provides most of what's needed for autofill, but should be overridden
+     * when:
      * <ol>
      * <li>The view contents does not include PII (Personally Identifiable Information), so it
-     * can call {@link ViewStructure#setSanitized(boolean)} passing {@code true}.
+     * can call {@link ViewStructure#setDataIsSensitive(boolean)} passing {@code false}.
      * <li>It must set fields such {@link ViewStructure#setText(CharSequence)},
      * {@link ViewStructure#setAutofillOptions(String[])}, or {@link ViewStructure#setUrl(String)}.
      * </ol>
@@ -7368,13 +7387,17 @@
      * <li>Also implement {@link #autofillVirtual(int, AutofillValue)} to autofill the virtual
      * children.
      * <li>Call
-     * {@link android.view.autofill.AutofillManager#startAutofillRequestOnVirtualView} and
-     * {@link android.view.autofill.AutofillManager#stopAutofillRequestOnVirtualView(View, int)}
+     * {@link android.view.autofill.AutofillManager#notifyVirtualViewEntered} and
+     * {@link android.view.autofill.AutofillManager#notifyVirtualViewExited(View, int)}
      * when the focus inside the view changed.
-     * <li>Call {@link android.view.autofill.AutofillManager#virtualValueChanged(View, int,
+     * <li>Call {@link android.view.autofill.AutofillManager#notifyVirtualValueChanged(View, int,
      * AutofillValue)} when the value of a child changed.
-     * <li>Call {@link android.view.autofill.AutofillManager#reset()} when the autofill context
-     * of the view structure changed.
+     * <li>Call {@link AutofillManager#commit()} when the autofill context
+     * of the view structure changed and you want the current autofill interaction if such
+     * to be commited.
+     * <li>Call {@link AutofillManager#cancel()} ()} when the autofill context
+     * of the view structure changed and you want the current autofill interaction if such
+     * to be cancelled.
      * </ol>
      *
      * @param structure Fill in with structured view data.
@@ -7453,12 +7476,12 @@
     /**
      * Describes the content of a view so that a autofill service can fill in the appropriate data.
      *
-     * @return The hint set via the attribute
+     * @return The hint set via the attribute or {@code null} if no hint it set.
      *
      * @attr ref android.R.styleable#View_autofillHint
      */
     @ViewDebug.ExportedProperty()
-    @AutofillHint public int getAutofillHint() {
+    @Nullable public String[] getAutofillHint() {
         return mAutofillHint;
     }
 
@@ -9088,11 +9111,15 @@
      * Sets the a hint that helps the autofill service to select the appropriate data to fill the
      * view.
      *
-     * @param autofillHint The autofill hint to set
+     * @param autofillHint The autofill hint to set. If the array is emtpy, {@code null} is set.
      * @attr ref android.R.styleable#View_autofillHint
      */
-    public void setAutofillHint(@AutofillHint int autofillHint) {
-        mAutofillHint = autofillHint;
+    public void setAutofillHint(@Nullable String... autofillHint) {
+        if (autofillHint == null || autofillHint.length == 0) {
+            mAutofillHint = null;
+        } else {
+            mAutofillHint = autofillHint;
+        }
     }
 
     /**
@@ -11085,6 +11112,7 @@
     @CallSuper
     public void dispatchStartTemporaryDetach() {
         mPrivateFlags3 |= PFLAG3_TEMPORARY_DETACH;
+        notifyEnterOrExitForAutoFillIfNeeded(false);
         onStartTemporaryDetach();
     }
 
@@ -11110,6 +11138,7 @@
         if (hasWindowFocus() && hasFocus()) {
             InputMethodManager.getInstance().focusIn(this);
         }
+        notifyEnterOrExitForAutoFillIfNeeded(true);
     }
 
     /**
@@ -11512,6 +11541,9 @@
         } else if (imm != null && (mPrivateFlags & PFLAG_FOCUSED) != 0) {
             imm.focusIn(this);
         }
+
+        notifyEnterOrExitForAutoFillIfNeeded(hasWindowFocus);
+
         refreshDrawableState();
     }
 
@@ -16879,12 +16911,7 @@
         }
         needGlobalAttributesUpdate(false);
 
-        if (isAutofillable() && isFocused() && getResolvedAutofillMode() == AUTOFILL_MODE_AUTO) {
-            AutofillManager afm = getAutofillManager();
-            if (afm != null) {
-                afm.startAutofillRequest(this);
-            }
-        }
+        notifyEnterOrExitForAutoFillIfNeeded(true);
     }
 
     void dispatchDetachedFromWindow() {
@@ -16932,12 +16959,7 @@
             mOverlay.getOverlayView().dispatchDetachedFromWindow();
         }
 
-        if (isAutofillable() && isFocused() && getResolvedAutofillMode() == AUTOFILL_MODE_AUTO) {
-            AutofillManager afm = getAutofillManager();
-            if (afm != null) {
-                afm.stopAutofillRequest(this);
-            }
-        }
+        notifyEnterOrExitForAutoFillIfNeeded(false);
     }
 
     /**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 9fa9985e..de0ec40 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -46,6 +46,7 @@
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.Pools;
 import android.util.Pools.SynchronizedPool;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -3327,7 +3328,74 @@
     @Override
     public void dispatchProvideStructure(ViewStructure structure) {
         super.dispatchProvideStructure(structure);
-        dispatchProvideStructureForAssistOrAutoFill(structure, false);
+        if (isAssistBlocked() || structure.getChildCount() != 0) {
+            return;
+        }
+        final int childrenCount = mChildrenCount;
+        if (childrenCount <= 0) {
+            return;
+        }
+        structure.setChildCount(childrenCount);
+        ArrayList<View> preorderedList = buildOrderedChildList();
+        boolean customOrder = preorderedList == null
+                && isChildrenDrawingOrderEnabled();
+        for (int i = 0; i < childrenCount; i++) {
+            int childIndex;
+            try {
+                childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
+            } catch (IndexOutOfBoundsException e) {
+                childIndex = i;
+                if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.M) {
+                    Log.w(TAG, "Bad getChildDrawingOrder while collecting assist @ "
+                            + i + " of " + childrenCount, e);
+                    // At least one app is failing when we call getChildDrawingOrder
+                    // at this point, so deal semi-gracefully with it by falling back
+                    // on the basic order.
+                    customOrder = false;
+                    if (i > 0) {
+                        // If we failed at the first index, there really isn't
+                        // anything to do -- we will just proceed with the simple
+                        // sequence order.
+                        // Otherwise, we failed in the middle, so need to come up
+                        // with an order for the remaining indices and use that.
+                        // Failed at the first one, easy peasy.
+                        int[] permutation = new int[childrenCount];
+                        SparseBooleanArray usedIndices = new SparseBooleanArray();
+                        // Go back and collected the indices we have done so far.
+                        for (int j = 0; j < i; j++) {
+                            permutation[j] = getChildDrawingOrder(childrenCount, j);
+                            usedIndices.put(permutation[j], true);
+                        }
+                        // Fill in the remaining indices with indices that have not
+                        // yet been used.
+                        int nextIndex = 0;
+                        for (int j = i; j < childrenCount; j++) {
+                            while (usedIndices.get(nextIndex, false)) {
+                                nextIndex++;
+                            }
+                            permutation[j] = nextIndex;
+                            nextIndex++;
+                        }
+                        // Build the final view list.
+                        preorderedList = new ArrayList<>(childrenCount);
+                        for (int j = 0; j < childrenCount; j++) {
+                            final int index = permutation[j];
+                            final View child = mChildren[index];
+                            preorderedList.add(child);
+                        }
+                    }
+                } else {
+                    throw e;
+                }
+            }
+            final View child = getAndVerifyPreorderedView(preorderedList, mChildren,
+                    childIndex);
+            final ViewStructure cstructure = structure.newChild(i);
+            child.dispatchProvideStructure(cstructure);
+        }
+        if (preorderedList != null) {
+            preorderedList.clear();
+        }
     }
 
     /**
@@ -3339,21 +3407,44 @@
     @Override
     public void dispatchProvideAutofillStructure(ViewStructure structure, int flags) {
         super.dispatchProvideAutofillStructure(structure, flags);
-        dispatchProvideStructureForAssistOrAutoFill(structure, true);
+        if (isAutofillBlocked() || structure.getChildCount() != 0) {
+            return;
+        }
+        final ChildListForAutoFill children = getChildrenForAutofill();
+        final int childrenCount = children.size();
+        structure.setChildCount(childrenCount);
+        for (int i = 0; i < childrenCount; i++) {
+            final View child = children.get(i);
+            final ViewStructure cstructure = structure.newChild(i);
+            child.dispatchProvideAutofillStructure(cstructure, flags);
+        }
+        children.recycle();
     }
 
-    /** @hide */
-    private ArrayList<View> getChildrenForAutofill() {
-        final ArrayList<View> list = new ArrayList<>();
-        populateChildrenForAutofill(list);
-        return list;
+    /**
+     * Gets the children for autofill. Children for autofill are the first
+     * level descendants that are important for autofill. The returned
+     * child list object is pooled and the caller must recycle it once done.
+     * @hide */
+    private @NonNull ChildListForAutoFill getChildrenForAutofill() {
+        final ChildListForAutoFill children = ChildListForAutoFill.obtain();
+        populateChildrenForAutofill(children);
+        return children;
     }
 
     /** @hide */
     private void populateChildrenForAutofill(ArrayList<View> list) {
-        final int count = mChildrenCount;
-        for (int i = 0; i < count; i++) {
-            final View child = mChildren[i];
+        final int childrenCount = mChildrenCount;
+        if (childrenCount <= 0) {
+            return;
+        }
+        final ArrayList<View> preorderedList = buildOrderedChildList();
+        final boolean customOrder = preorderedList == null
+                && isChildrenDrawingOrderEnabled();
+        for (int i = 0; i < childrenCount; i++) {
+            final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
+            final View child = (preorderedList == null)
+                    ? mChildren[childIndex] : preorderedList.get(childIndex);
             if (child.isImportantForAutofill()) {
                 list.add(child);
             } else if (child instanceof ViewGroup) {
@@ -3362,106 +3453,6 @@
         }
     }
 
-    private void dispatchProvideStructureForAssistOrAutoFill(ViewStructure structure,
-            boolean forAutofill) {
-        boolean blocked = forAutofill ? isAutofillBlocked() : isAssistBlocked();
-        if (blocked || structure.getChildCount() != 0) {
-            return;
-        }
-        final View[] childrenArray;
-        final ArrayList<View> childrenList;
-        final int childrenCount;
-
-        if (forAutofill) {
-            childrenArray = null;
-            // TODO(b/33197203): the current algorithm allocates a new list for each children that
-            // is a view group; ideally, we should use mAttachInfo.mTempArrayList instead, but that
-            // would complicated the algorithm a lot...
-            childrenList = getChildrenForAutofill();
-
-            childrenCount = childrenList.size();
-        } else {
-            childrenArray = mChildren;
-            childrenList = null;
-            childrenCount = getChildCount();
-        }
-
-        if (childrenCount > 0) {
-            structure.setChildCount(childrenCount);
-            ArrayList<View> preorderedList = buildOrderedChildList();
-            boolean customOrder = preorderedList == null
-                    && isChildrenDrawingOrderEnabled();
-            for (int i = 0; i < childrenCount; i++) {
-                int childIndex;
-                try {
-                    childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
-                } catch (IndexOutOfBoundsException e) {
-                    childIndex = i;
-                    if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.M) {
-                        Log.w(TAG, "Bad getChildDrawingOrder while collecting assist @ "
-                                + i + " of " + childrenCount, e);
-                        // At least one app is failing when we call getChildDrawingOrder
-                        // at this point, so deal semi-gracefully with it by falling back
-                        // on the basic order.
-                        customOrder = false;
-                        if (i > 0) {
-                            // If we failed at the first index, there really isn't
-                            // anything to do -- we will just proceed with the simple
-                            // sequence order.
-                            // Otherwise, we failed in the middle, so need to come up
-                            // with an order for the remaining indices and use that.
-                            // Failed at the first one, easy peasy.
-                            int[] permutation = new int[childrenCount];
-                            SparseBooleanArray usedIndices = new SparseBooleanArray();
-                            // Go back and collected the indices we have done so far.
-                            for (int j = 0; j < i; j++) {
-                                permutation[j] = getChildDrawingOrder(childrenCount, j);
-                                usedIndices.put(permutation[j], true);
-                            }
-                            // Fill in the remaining indices with indices that have not
-                            // yet been used.
-                            int nextIndex = 0;
-                            for (int j = i; j < childrenCount; j++) {
-                                while (usedIndices.get(nextIndex, false)) {
-                                    nextIndex++;
-                                }
-                                permutation[j] = nextIndex;
-                                nextIndex++;
-                            }
-                            // Build the final view list.
-                            preorderedList = new ArrayList<>(childrenCount);
-                            for (int j = 0; j < childrenCount; j++) {
-                                final int index = permutation[j];
-                                final View child = forAutofill
-                                        ? childrenList.get(index)
-                                        : childrenArray[index];
-                                preorderedList.add(child);
-                            }
-                        }
-                    } else {
-                        throw e;
-                    }
-                }
-
-                final View child = forAutofill
-                        ? getAndVerifyPreorderedView(preorderedList, childrenList, childIndex)
-                        : getAndVerifyPreorderedView(preorderedList, childrenArray, childIndex);
-                final ViewStructure cstructure = structure.newChild(i);
-
-                // Must explicitly check which recursive method to call.
-                if (forAutofill) {
-                    // NOTE: flags are not currently supported, hence 0
-                    child.dispatchProvideAutofillStructure(cstructure, 0);
-                } else {
-                    child.dispatchProvideStructure(cstructure);
-                }
-            }
-            if (preorderedList != null) {
-                preorderedList.clear();
-            }
-        }
-    }
-
     private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList, View[] children,
             int childIndex) {
         final View child;
@@ -3477,21 +3468,6 @@
         return child;
     }
 
-    private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList,
-            ArrayList<View> children, int childIndex) {
-        final View child;
-        if (preorderedList != null) {
-            child = preorderedList.get(childIndex);
-            if (child == null) {
-                throw new RuntimeException("Invalid preorderedList contained null child at index "
-                        + childIndex);
-            }
-        } else {
-            child = children.get(childIndex);
-        }
-        return child;
-    }
-
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
@@ -8192,6 +8168,29 @@
     }
 
     /**
+     * Pooled class that to hold the children for autifill.
+     */
+    static class ChildListForAutoFill extends ArrayList<View> {
+        private static final int MAX_POOL_SIZE = 32;
+
+        private static final Pools.SimplePool<ChildListForAutoFill> sPool =
+                new Pools.SimplePool<>(MAX_POOL_SIZE);
+
+        public static ChildListForAutoFill obtain() {
+            ChildListForAutoFill list = sPool.acquire();
+            if (list == null) {
+                list = new ChildListForAutoFill();
+            }
+            return list;
+        }
+
+        public void recycle() {
+            clear();
+            sPool.release(this);
+        }
+    }
+
+    /**
      * Pooled class that orderes the children of a ViewGroup from start
      * to end based on how they are laid out and the layout direction.
      */
@@ -8228,10 +8227,6 @@
             return mChildren.get(index);
         }
 
-        public int getChildIndex(View child) {
-            return mChildren.indexOf(child);
-        }
-
         private void init(ViewGroup parent, boolean sort) {
             ArrayList<View> children = mChildren;
             final int childCount = parent.getChildCount();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 580888c..ed42385 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -164,6 +164,17 @@
     static final ArrayList<ComponentCallbacks> sConfigCallbacks = new ArrayList();
 
     /**
+     * Signals that compatibility booleans have been initialized according to
+     * target SDK versions.
+     */
+    private static boolean sCompatibilityDone = false;
+
+    /**
+     * Always assign focus if a focusable View is available.
+     */
+    private static boolean sAlwaysAssignFocus;
+
+    /**
      * This list must only be modified by the main thread, so a lock is only needed when changing
      * the list or when accessing the list from a non-main thread.
      */
@@ -451,6 +462,13 @@
         mFallbackEventHandler = new PhoneFallbackEventHandler(context);
         mChoreographer = Choreographer.getInstance();
         mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
+
+        if (!sCompatibilityDone) {
+            sAlwaysAssignFocus = mTargetSdkVersion < Build.VERSION_CODES.O;
+
+            sCompatibilityDone = true;
+        }
+
         loadSystemProperties();
     }
 
@@ -2180,7 +2198,7 @@
             }
         }
 
-        if (mFirst) {
+        if (mFirst && sAlwaysAssignFocus) {
             // handle first focus request
             if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: mView.hasFocus()="
                     + mView.hasFocus());
@@ -3290,7 +3308,9 @@
         checkThread();
         if (mView != null) {
             if (!mView.hasFocus()) {
-                v.requestFocus();
+                if (sAlwaysAssignFocus) {
+                    v.requestFocus();
+                }
             } else {
                 // the one case where will transfer focus away from the current one
                 // is if the current view is a view group that prefers to give focus
@@ -4463,9 +4483,7 @@
                         return true;
                     }
                 } else {
-                    // find the best view to give focus to in this non-touch-mode with no-focus
-                    View v = focusSearch(null, direction);
-                    if (v != null && v.requestFocus(direction)) {
+                    if (mView.restoreDefaultFocus()) {
                         return true;
                     }
                 }
@@ -4475,9 +4493,10 @@
 
         private boolean performKeyboardGroupNavigation(int direction) {
             final View focused = mView.findFocus();
-            View cluster = focused != null
-                    ? focused.keyboardNavigationClusterSearch(null, direction)
-                    : keyboardNavigationClusterSearch(null, direction);
+            if (focused == null && mView.restoreDefaultFocus()) {
+                return true;
+            }
+            View cluster = focused.keyboardNavigationClusterSearch(null, direction);
 
             // Since requestFocus only takes "real" focus directions (and therefore also
             // restoreFocusInCluster), convert forward/backward focus into FOCUS_DOWN.
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index bccaca2..38c7738 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.annotation.Nullable;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -323,7 +324,7 @@
      * Sets the a hint that helps the autofill service to select the appropriate data to fill the
      * view.
      */
-    public abstract void setAutofillHint(@View.AutofillHint int hint);
+    public abstract void setAutofillHint(@Nullable String[] hint);
 
     /**
      * Sets the {@link AutofillValue} representing the current value of this node.
@@ -346,19 +347,27 @@
     public abstract void setInputType(int inputType);
 
     /**
-     * Marks this node as sanitized so its content are sent on {@link
+     * Sets whether the data on this node is sensitive; if it is, then its content (text, autofill
+     * value, etc..) is striped before calls to {@link
      * android.service.autofill.AutofillService#onFillRequest(android.app.assist.AssistStructure,
-     * Bundle, android.os.CancellationSignal, android.service.autofill.FillCallback)}.
+     * Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback)}.
      *
-     * <p>Only nodes that does not have PII (Personally Identifiable Information - sensitive data
-     * such as email addresses, credit card numbers, passwords, etc...) should be marked
-     * as sanitized; a good rule of thumb is to mark as sanitized nodes whose value were statically
-     * set from resources.
+     * <p>By default, all nodes are assumed to be sensitive, and only nodes that does not have PII
+     * (Personally Identifiable Information - sensitive data such as email addresses, credit card
+     * numbers, passwords, etc...) should be marked as non-sensitive; a good rule of thumb is to
+     * mark as non-sensitive nodes whose value were statically set from resources.
+     *
+     * <p>Notice that the content of even sensitive nodes are sent to the service (through the
+     * {@link
+     * android.service.autofill.AutofillService#onSaveRequest(android.app.assist.AssistStructure,
+     * Bundle, android.service.autofill.SaveCallback)} call) when the user consented to save
+     * thedata, so it is important to set the content of sensitive nodes as well, but mark them as
+     * sensitive.
      *
      * <p>Should only be set when the node is used for autofill purposes - it will be ignored
      * when used for Assist.
      */
-    public abstract void setSanitized(boolean sanitized);
+    public abstract void setDataIsSensitive(boolean sensitive);
 
     /**
      * Call when done populating a {@link ViewStructure} returned by
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index a541a4c..6dbc09c 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -168,6 +168,14 @@
     public abstract void setMagnificationSpec(MagnificationSpec spec);
 
     /**
+     * Set by the accessibility framework to indicate whether the magnifiable regions of the display
+     * should be shown.
+     *
+     * @param show {@code true} to show magnifiable region bounds, {@code false} to hide
+     */
+    public abstract void setForceShowMagnifiableBounds(boolean show);
+
+    /**
      * Obtains the magnification regions.
      *
      * @param magnificationRegion the current magnification region
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index fe888ec..c9f9f31 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -40,6 +40,8 @@
 import android.view.IWindow;
 import android.view.View;
 
+import com.android.internal.util.IntPair;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -109,6 +111,8 @@
 
     boolean mIsEnabled;
 
+    int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK;
+
     boolean mIsTouchExplorationEnabled;
 
     boolean mIsHighTextContrastEnabled;
@@ -203,6 +207,11 @@
         public void notifyServicesStateChanged() {
             mHandler.obtainMessage(MyHandler.MSG_NOTIFY_SERVICES_STATE_CHANGED).sendToTarget();
         }
+
+        @Override
+        public void setRelevantEventTypes(int eventTypes) {
+            mRelevantEventTypes = eventTypes;
+        }
     };
 
     /**
@@ -362,6 +371,14 @@
                     return;
                 }
             }
+            if ((event.getEventType() & mRelevantEventTypes) == 0) {
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "Not dispatching irrelevant event: " + event
+                            + " that is not among "
+                            + AccessibilityEvent.eventTypeToString(mRelevantEventTypes));
+                }
+                return;
+            }
             userId = mUserId;
         }
         try {
@@ -865,8 +882,9 @@
         }
 
         try {
-            final int stateFlags = service.addClient(mClient, mUserId);
-            setStateLocked(stateFlags);
+            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);
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 157980f..06cb5dc 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -38,7 +38,7 @@
 
     oneway void sendAccessibilityEvent(in AccessibilityEvent uiEvent, int userId);
 
-    int addClient(IAccessibilityManagerClient client, int userId);
+    long addClient(IAccessibilityManagerClient client, int userId);
 
     List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId);
 
diff --git a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
index 045ac91..9cc0315 100644
--- a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
@@ -25,5 +25,8 @@
 oneway interface IAccessibilityManagerClient {
 
     void setState(int stateFlags);
+
     void notifyServicesStateChanged();
+
+    void setRelevantEventTypes(int eventTypes);
 }
diff --git a/core/java/android/view/autofill/AutoFillValue.java b/core/java/android/view/autofill/AutoFillValue.java
index 5dd17f1..4774d8f 100644
--- a/core/java/android/view/autofill/AutoFillValue.java
+++ b/core/java/android/view/autofill/AutoFillValue.java
@@ -89,11 +89,6 @@
         return mRealValue.equals(other.mRealValue);
     }
 
-    /** @hide */
-    public String coerceToString() {
-        return mRealValue.coerceToString();
-    }
-
     @Override
     public String toString() {
         if (!DEBUG) return super.toString();
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 2a12e4b..f036b9c 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -71,10 +71,17 @@
     public static final String EXTRA_AUTHENTICATION_RESULT =
             "android.view.autofill.extra.AUTHENTICATION_RESULT";
 
-    /** @hide */ public static final int FLAG_START_SESSION = 0x1;
-    /** @hide */ public static final int FLAG_FOCUS_GAINED = 0x2;
-    /** @hide */ public static final int FLAG_FOCUS_LOST = 0x4;
-    /** @hide */ public static final int FLAG_VALUE_CHANGED = 0x8;
+    // Public flags start from the lowest bit
+    /**
+     * Indicates autofill was explicitly requested by the user.
+     */
+    public static final int FLAG_MANUAL_REQUEST = 0x1;
+
+    // Private flags start from the highest bit
+    /** @hide */ public static final int FLAG_START_SESSION = 0x80000000;
+    /** @hide */ public static final int FLAG_VIEW_ENTERED =  0x40000000;
+    /** @hide */ public static final int FLAG_VIEW_EXITED =   0x20000000;
+    /** @hide */ public static final int FLAG_VALUE_CHANGED = 0x10000000;
 
     private final Rect mTempRect = new Rect();
 
@@ -121,11 +128,71 @@
     }
 
     /**
-     * Called when an autofill operation on a {@link View} should start.
+     * Checkes whether autofill is enabled for the current user.
      *
-     * @param view {@link View} that triggered the autofill request.
+     * <p>Typically used to determine whether the option to explicitly request autofill should
+     * be offered - see {@link #requestAutofill(View)}.
+     *
+     * @return whether autofill is enabled for the current user.
      */
-    public void startAutofillRequest(@NonNull View view) {
+    public boolean isEnabled() {
+        ensureServiceClientAddedIfNeeded();
+        return mEnabled;
+    }
+
+    /**
+     * Explicitly requests a new autofill context.
+     *
+     * <p>Normally, the autofill context is automatically started when autofillable views are
+     * focused, but this method should be used in the cases where it must be explicitly requested,
+     * like a view that provides a contextual menu allowing users to autofill the activity.
+     *
+     * @param view view requesting the new autofill context.
+     */
+    public void requestAutofill(@NonNull View view) {
+        ensureServiceClientAddedIfNeeded();
+
+        if (!mEnabled) {
+            return;
+        }
+
+        final Rect bounds = mTempRect;
+        view.getBoundsOnScreen(bounds);
+        final AutofillId id = getAutofillId(view);
+        final AutofillValue value = view.getAutofillValue();
+
+        startSession(id, view.getWindowToken(), bounds, value, FLAG_MANUAL_REQUEST);
+    }
+
+    /**
+     * Explicitly requests a new autofill context for virtual views.
+     *
+     * <p>Normally, the autofill context is automatically started when autofillable views are
+     * focused, but this method should be used in the cases where it must be explicitly requested,
+     * like a virtual view that provides a contextual menu allowing users to autofill the activity.
+     *
+     * @param view the {@link View} whose descendant is the virtual view.
+     * @param childId id identifying the virtual child inside the view.
+     * @param bounds child boundaries, relative to the top window.
+     */
+    public void requestAutofill(@NonNull View view, int childId, @NonNull Rect bounds) {
+        ensureServiceClientAddedIfNeeded();
+
+        if (!mEnabled) {
+            return;
+        }
+
+        final AutofillId id = getAutofillId(view, childId);
+        startSession(id, view.getWindowToken(), bounds, null, FLAG_MANUAL_REQUEST);
+    }
+
+
+    /**
+     * Called when a {@link View} that supports autofill is entered.
+     *
+     * @param view {@link View} that was entered.
+     */
+    public void notifyViewEntered(@NonNull View view) {
         ensureServiceClientAddedIfNeeded();
 
         if (!mEnabled) {
@@ -139,38 +206,37 @@
 
         if (!mHasSession) {
             // Starts new session.
-            startSession(id, view.getWindowToken(), bounds, value);
+            startSession(id, view.getWindowToken(), bounds, value, 0);
         } else {
             // Update focus on existing session.
-            updateSession(id, bounds, value, FLAG_FOCUS_GAINED);
+            updateSession(id, bounds, value, FLAG_VIEW_ENTERED);
         }
     }
 
     /**
-     * Called when an autofill operation on a {@link View} should stop.
+     * Called when a {@link View} that supports autofill is exited.
      *
-     * @param view {@link View} that triggered the autofill request in
-     *             {@link #startAutofillRequest(View)}.
+     * @param view {@link View} that was exited.
      */
-    public void stopAutofillRequest(@NonNull View view) {
+    public void notifyViewExited(@NonNull View view) {
         ensureServiceClientAddedIfNeeded();
 
         if (mEnabled && mHasSession) {
             final AutofillId id = getAutofillId(view);
 
             // Update focus on existing session.
-            updateSession(id, null, null, FLAG_FOCUS_LOST);
+            updateSession(id, null, null, FLAG_VIEW_EXITED);
         }
     }
 
     /**
-     * Called when an autofill operation on a virtual {@link View} should start.
+     * Called when a virtual view that supports autofill is entered.
      *
-     * @param parent parent of the {@link View} that triggered the autofill request.
-     * @param childId id identifying the virtual child inside the parent view.
+     * @param view the {@link View} whose descendant is the virtual view.
+     * @param childId id identifying the virtual child inside the view.
      * @param bounds child boundaries, relative to the top window.
      */
-    public void startAutofillRequestOnVirtualView(@NonNull View parent, int childId,
+    public void notifyVirtualViewEntered(@NonNull View view, int childId,
             @NonNull Rect bounds) {
         ensureServiceClientAddedIfNeeded();
 
@@ -178,32 +244,31 @@
             return;
         }
 
-        final AutofillId id = getAutofillId(parent, childId);
+        final AutofillId id = getAutofillId(view, childId);
 
         if (!mHasSession) {
             // Starts new session.
-            startSession(id, parent.getWindowToken(), bounds, null);
+            startSession(id, view.getWindowToken(), bounds, null, 0);
         } else {
             // Update focus on existing session.
-            updateSession(id, bounds, null, FLAG_FOCUS_GAINED);
+            updateSession(id, bounds, null, FLAG_VIEW_ENTERED);
         }
     }
 
     /**
-     * Called when an autofill operation on a virtual {@link View} should stop.
+     * Called when a virtual view that supports autofill is exited.
      *
-     * @param parent parent of the {@link View} that triggered the autofill request in
-     *               {@link #startAutofillRequestOnVirtualView(View, int, Rect)}.
-     * @param childId id identifying the virtual child inside the parent view.
+     * @param view the {@link View} whose descendant is the virtual view.
+     * @param childId id identifying the virtual child inside the view.
      */
-    public void stopAutofillRequestOnVirtualView(@NonNull View parent, int childId) {
+    public void notifyVirtualViewExited(@NonNull View view, int childId) {
         ensureServiceClientAddedIfNeeded();
 
         if (mEnabled && mHasSession) {
-            final AutofillId id = getAutofillId(parent, childId);
+            final AutofillId id = getAutofillId(view, childId);
 
             // Update focus on existing session.
-            updateSession(id, null, null, FLAG_FOCUS_LOST);
+            updateSession(id, null, null, FLAG_VIEW_EXITED);
         }
     }
 
@@ -212,7 +277,7 @@
      *
      * @param view view whose value changed.
      */
-    public void valueChanged(View view) {
+    public void notifyValueChanged(View view) {
         if (!mEnabled || !mHasSession) {
             return;
         }
@@ -226,26 +291,26 @@
     /**
      * Called to indicate the value of an autofillable virtual {@link View} changed.
      *
-     * @param parent parent view whose value changed.
+     * @param view the {@link View} whose descendant is the virtual view.
      * @param childId id identifying the virtual child inside the parent view.
      * @param value new value of the child.
      */
-    public void virtualValueChanged(View parent, int childId, AutofillValue value) {
+    public void notifyVirtualValueChanged(View view, int childId, AutofillValue value) {
         if (!mEnabled || !mHasSession) {
             return;
         }
 
-        final AutofillId id = getAutofillId(parent, childId);
+        final AutofillId id = getAutofillId(view, childId);
         updateSession(id, null, value, FLAG_VALUE_CHANGED);
     }
 
     /**
-     * Called to indicate the current autofill context should be reset.
+     * Called to indicate the current autofill context should be commited.
      *
      * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should
      * call this method after the form is submitted and another page is rendered.
      */
-    public void reset() {
+    public void commit() {
         if (!mEnabled && !mHasSession) {
             return;
         }
@@ -253,6 +318,20 @@
         finishSession();
     }
 
+    /**
+     * Called to indicate the current autofill context should be cancelled.
+     *
+     * <p>For example, when a virtual view is rendering an {@code HTML} page with a form, it should
+     * call this method if the user does not post the form but moves to another form in this page.
+     */
+    public void cancel() {
+        if (!mEnabled && !mHasSession) {
+            return;
+        }
+
+        cancelSession();
+    }
+
     private AutofillClient getClient() {
         if (mContext instanceof AutofillClient) {
             return (AutofillClient) mContext;
@@ -293,15 +372,16 @@
     }
 
     private void startSession(AutofillId id, IBinder windowToken, Rect bounds,
-            AutofillValue value) {
+            AutofillValue value, int flags) {
         if (DEBUG) {
-            Log.d(TAG, "startSession(): id=" + id + ", bounds=" + bounds + ", value=" + value);
+            Log.d(TAG, "startSession(): id=" + id + ", bounds=" + bounds + ", value=" + value
+                    + ", flags=" + flags);
         }
 
         try {
             mService.startSession(mContext.getActivityToken(), windowToken,
                     mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
-                    mCallback != null);
+                    mCallback != null, flags);
             final AutofillClient client = getClient();
             if (client != null) {
                 client.resetableStateAvailable();
@@ -324,9 +404,21 @@
         }
     }
 
+    private void cancelSession() {
+        if (DEBUG) {
+            Log.d(TAG, "cancelSession()");
+        }
+        mHasSession = false;
+        try {
+            mService.cancelSession(mContext.getActivityToken(), mContext.getUserId());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     private void updateSession(AutofillId id, Rect bounds, AutofillValue value, int flags) {
         if (DEBUG) {
-            if (VERBOSE || (flags & FLAG_FOCUS_LOST) != 0) {
+            if (VERBOSE || (flags & FLAG_VIEW_EXITED) != 0) {
                 Log.d(TAG, "updateSession(): id=" + id + ", bounds=" + bounds + ", value=" + value
                         + ", flags=" + flags);
             }
diff --git a/core/java/android/view/autofill/AutofillValue.java b/core/java/android/view/autofill/AutofillValue.java
index 0c7620e..e2dd7fe 100644
--- a/core/java/android/view/autofill/AutofillValue.java
+++ b/core/java/android/view/autofill/AutofillValue.java
@@ -16,13 +16,23 @@
 
 package android.view.autofill;
 
+import static android.view.View.AUTOFILL_TYPE_DATE;
+import static android.view.View.AUTOFILL_TYPE_LIST;
+import static android.view.View.AUTOFILL_TYPE_TEXT;
+import static android.view.View.AUTOFILL_TYPE_TOGGLE;
 import static android.view.autofill.Helper.DEBUG;
+import static android.view.autofill.Helper.VERBOSE;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.View;
 
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
 /**
  * Abstracts how a {@link View} can be autofilled by an
  * {@link android.service.autofill.AutofillService}.
@@ -31,52 +41,107 @@
  * {@link View#getAutofillType()}.
  */
 public final class AutofillValue implements Parcelable {
-    private final String mText;
-    private final int mListIndex;
-    private final boolean mToggle;
-    private final long mDate;
+    private final @View.AutofillType int mType;
+    private final @NonNull Object mValue;
 
-    private AutofillValue(CharSequence text, int listIndex, boolean toggle, long date) {
-        mText = (text == null) ? null : text.toString();
-        mListIndex = listIndex;
-        mToggle = toggle;
-        mDate = date;
+    private AutofillValue(@View.AutofillType int type, @NonNull Object value) {
+        mType = type;
+        mValue = value;
     }
 
     /**
      * Gets the value to autofill a text field.
      *
-     * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.
+     * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.</p>
+     *
+     * @throws IllegalStateException if the value is not a text value
      */
-    public CharSequence getTextValue() {
-        return mText;
+    @NonNull public CharSequence getTextValue() {
+        Preconditions.checkState(isText(), "value must be a text value, not type=" + mType);
+        return (CharSequence) mValue;
+    }
+
+    /**
+     * Checks is this is a text value.
+     *
+     * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.</p>
+     */
+    public boolean isText() {
+        return mType == AUTOFILL_TYPE_TEXT;
     }
 
     /**
      * Gets the value to autofill a toggable field.
      *
-     * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.
+     * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.</p>
+     *
+     * @throws IllegalStateException if the value is not a toggle value
      */
     public boolean getToggleValue() {
-        return mToggle;
+        Preconditions.checkState(isToggle(), "value must be a toggle value, not type=" + mType);
+        return (Boolean) mValue;
+    }
+
+    /**
+     * Checks is this is a toggle value.
+     *
+     * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.</p>
+     */
+    public boolean isToggle() {
+        return mType == AUTOFILL_TYPE_TOGGLE;
     }
 
     /**
      * Gets the value to autofill a selection list field.
      *
-     * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.
+     * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.</p>
+     *
+     * @throws IllegalStateException if the value is not a list value
      */
     public int getListValue() {
-        return mListIndex;
+        Preconditions.checkState(isList(), "value must be a list value, not type=" + mType);
+        return (Integer) mValue;
+    }
+
+    /**
+     * Checks is this is a list value.
+     *
+     * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.</p>
+     */
+    public boolean isList() {
+        return mType == AUTOFILL_TYPE_LIST;
     }
 
     /**
      * Gets the value to autofill a date field.
      *
-     * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.
+     * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.</p>
+     *
+     * @throws IllegalStateException if the value is not a date value
      */
     public long getDateValue() {
-        return mDate;
+        Preconditions.checkState(isDate(), "value must be a date value, not type=" + mType);
+        return (Long) mValue;
+    }
+
+    /**
+     * Checks is this is a date value.
+     *
+     * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.</p>
+     */
+    public boolean isDate() {
+        return mType == AUTOFILL_TYPE_DATE;
+    }
+
+    /**
+     * Used to define whether a field is empty so it's not sent to service on save.
+     *
+     * <p>Only applies to some types, like text.
+     *
+     * @hide
+     */
+    public boolean isEmpty() {
+        return isText() && ((CharSequence) mValue).length() == 0;
     }
 
     /////////////////////////////////////
@@ -85,13 +150,7 @@
 
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((mText == null) ? 0 : mText.hashCode());
-        result = prime * result + mListIndex;
-        result = prime * result + (mToggle ? 1231 : 1237);
-        result = prime * result + (int) (mDate ^ (mDate >>> 32));
-        return result;
+        return mType + mValue.hashCode();
     }
 
     @Override
@@ -100,32 +159,24 @@
         if (obj == null) return false;
         if (getClass() != obj.getClass()) return false;
         final AutofillValue other = (AutofillValue) obj;
-        if (mText == null) {
-            if (other.mText != null) return false;
-        } else {
-            if (!mText.equals(other.mText)) return false;
-        }
-        if (mListIndex != other.mListIndex) return false;
-        if (mToggle != other.mToggle) return false;
-        if (mDate != other.mDate) return false;
-        return true;
-    }
 
-    /** @hide */
-    public String coerceToString() {
-        // TODO(b/33197203): How can we filter on toggles or list values?
-        return mText;
+        if (mType != other.mType) return false;
+
+        if (isText()) {
+            return mValue.toString().equals(other.mValue.toString());
+        } else {
+            return Objects.equals(mValue, other.mValue);
+        }
     }
 
     @Override
     public String toString() {
         if (!DEBUG) return super.toString();
 
-        if (mText != null) {
-            return mText.length() + "_chars";
-        }
+        final String sanitizedValue = isText() && !VERBOSE
+                ? ((CharSequence) mValue).length() + "_chars" : mValue.toString();
 
-        return "[l=" + mListIndex + ", t=" + mToggle + ", d=" + mDate + "]";
+        return "[type=" + mType + ", value=" + sanitizedValue + "]";
     }
 
     /////////////////////////////////////
@@ -139,17 +190,44 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeString(mText);
-        parcel.writeInt(mListIndex);
-        parcel.writeInt(mToggle ? 1 : 0);
-        parcel.writeLong(mDate);
+        parcel.writeInt(mType);
+
+        switch (mType) {
+            case AUTOFILL_TYPE_TEXT:
+                parcel.writeCharSequence((CharSequence) mValue);
+                break;
+            case AUTOFILL_TYPE_TOGGLE:
+                parcel.writeInt((Boolean) mValue ? 1 : 0);
+                break;
+            case AUTOFILL_TYPE_LIST:
+                parcel.writeInt((Integer) mValue);
+                break;
+            case AUTOFILL_TYPE_DATE:
+                parcel.writeLong((Long) mValue);
+                break;
+        }
     }
 
-    private AutofillValue(Parcel parcel) {
-        mText = parcel.readString();
-        mListIndex = parcel.readInt();
-        mToggle = parcel.readInt() == 1;
-        mDate = parcel.readLong();
+    private AutofillValue(@NonNull Parcel parcel) {
+        mType = parcel.readInt();
+
+        switch (mType) {
+            case AUTOFILL_TYPE_TEXT:
+                mValue = parcel.readCharSequence();
+                break;
+            case AUTOFILL_TYPE_TOGGLE:
+                int rawValue = parcel.readInt();
+                mValue = rawValue != 0;
+                break;
+            case AUTOFILL_TYPE_LIST:
+                mValue = parcel.readInt();
+                break;
+            case AUTOFILL_TYPE_DATE:
+                mValue = parcel.readLong();
+                break;
+            default:
+                throw new IllegalArgumentException("type=" + mType + " not valid");
+        }
     }
 
     public static final Parcelable.Creator<AutofillValue> CREATOR =
@@ -175,9 +253,8 @@
      * <p>See {@link View#AUTOFILL_TYPE_TEXT} for more info.
      */
     // TODO(b/33197203): use cache
-    @Nullable
     public static AutofillValue forText(@Nullable CharSequence value) {
-        return value == null ? null : new AutofillValue(value, 0, false, 0);
+        return value == null ? null : new AutofillValue(AUTOFILL_TYPE_TEXT, value);
     }
 
     /**
@@ -187,7 +264,7 @@
      * <p>See {@link View#AUTOFILL_TYPE_TOGGLE} for more info.
      */
     public static AutofillValue forToggle(boolean value) {
-        return new AutofillValue(null, 0, value, 0);
+        return new AutofillValue(AUTOFILL_TYPE_TOGGLE, value);
     }
 
     /**
@@ -197,7 +274,7 @@
      * <p>See {@link View#AUTOFILL_TYPE_LIST} for more info.
      */
     public static AutofillValue forList(int value) {
-        return new AutofillValue(null, value, false, 0);
+        return new AutofillValue(AUTOFILL_TYPE_LIST, value);
     }
 
     /**
@@ -206,6 +283,6 @@
      * <p>See {@link View#AUTOFILL_TYPE_DATE} for more info.
      */
     public static AutofillValue forDate(long value) {
-        return new AutofillValue(null, 0, false, value);
+        return new AutofillValue(AUTOFILL_TYPE_DATE, value);
     }
 }
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 07d8cab..85b05e5 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -32,10 +32,11 @@
     boolean addClient(in IAutoFillManagerClient client, int userId);
     oneway void startSession(in IBinder activityToken, IBinder windowToken, in IBinder appCallback,
             in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId,
-            boolean hasCallback);
+            boolean hasCallback, int flags);
     oneway void updateSession(in IBinder activityToken, in AutofillId id, in Rect bounds,
             in AutofillValue value, int flags, int userId);
     oneway void finishSession(in IBinder activityToken, int userId);
+    oneway void cancelSession(in IBinder activityToken, int userId);
     oneway void setAuthenticationResult(in Bundle data,
             in IBinder activityToken, int userId);
     oneway void setHasCallback(in IBinder activityToken, int userId, boolean hasIt);
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index f032414..35c9a29 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -63,7 +63,7 @@
             if (mDefault == null) {
                 try {
                     mSmartSelectionFd = ParcelFileDescriptor.open(
-                            new File("/etc/assistant/smart-selection.model"),
+                            new File("/etc/textclassifier/textclassifier.smartselection.en.model"),
                             ParcelFileDescriptor.MODE_READ_ONLY);
                     mDefault = new TextClassifierImpl(mContext, mSmartSelectionFd);
                 } catch (FileNotFoundException e) {
@@ -109,7 +109,7 @@
         synchronized (mLangIdLock) {
             if (mLangId == null) {
                 mLangIdFd = ParcelFileDescriptor.open(
-                        new File("/etc/assistant/lang-id.model"),
+                        new File("/etc/textclassifier/textclassifier.langid.model"),
                         ParcelFileDescriptor.MODE_READ_ONLY);
                 mLangId = new LangId(mLangIdFd.getFd());
             }
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index c95a1fb..06ac869 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -121,8 +121,8 @@
                         .classifyText(text.toString(), startIndex, endIndex);
                 if (results.length > 0) {
                     // TODO: Added this log for debug only. Remove before release.
-                    Log.d(LOG_TAG,
-                            String.format("Classification type: %s", results[0].mCollection));
+                    Log.d(LOG_TAG, String.format(
+                            "Classification type: %s", getHighestScoringType(results)));
                     return createClassificationResult(results, classified);
                 }
             }
@@ -188,7 +188,7 @@
             builder.setEntityType(classifications[i].mCollection, classifications[i].mScore);
         }
 
-        final String type = classifications[0].mCollection;
+        final String type = getHighestScoringType(classifications);
         final Intent intent = IntentFactory.create(mContext, type, text.toString());
         final PackageManager pm;
         final ResolveInfo resolveInfo;
@@ -226,6 +226,23 @@
         return builder.build();
     }
 
+    private static String getHighestScoringType(SmartSelection.ClassificationResult[] types) {
+        if (types.length < 1) {
+            return "";
+        }
+
+        String type = types[0].mCollection;
+        float highestScore = types[0].mScore;
+        final int size = types.length;
+        for (int i = 1; i < size; i++) {
+            if (types[i].mScore > highestScore) {
+                type = types[i].mCollection;
+                highestScore = types[i].mScore;
+            }
+        }
+        return type;
+    }
+
     /**
      * @throws IllegalArgumentException if text is null; startIndex is negative;
      *      endIndex is greater than text.length() or is not greater than startIndex
@@ -265,7 +282,7 @@
                     final SmartSelection.ClassificationResult[] results =
                             smartSelection.classifyText(text, selectionStart, selectionEnd);
                     if (results.length > 0) {
-                        final String type = results[0].mCollection;
+                        final String type = getHighestScoringType(results);
                         if (matches(type, linkMask)) {
                             final Intent intent = IntentFactory.create(
                                     context, type, text.substring(selectionStart, selectionEnd));
diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index 053574f..020e80a 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -23,6 +23,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.SparseArray;
 import android.view.View;
 import android.view.ViewGroup;
@@ -38,6 +39,8 @@
  * @attr ref android.R.styleable#AbsSpinner_entries
  */
 public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> {
+    private static final String LOG_TAG = AbsSpinner.class.getSimpleName();
+
     SpinnerAdapter mAdapter;
 
     int mHeightMeasureSpec;
@@ -514,8 +517,11 @@
     public void autofill(AutofillValue value) {
         if (!isEnabled()) return;
 
-        final int position = value.getListValue();
-        setSelection(position);
+        if (value.isList()) {
+            setSelection(value.getListValue());
+        } else {
+            Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+        }
     }
 
     @Override
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index e5505a6..5725b49 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -918,7 +918,7 @@
         // Always notify AutoFillManager - it will return right away if autofill is disabled.
         final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
         if (afm != null) {
-            afm.valueChanged(this);
+            afm.notifyValueChanged(this);
         }
     }
 
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index 81aec9c..899a824 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -28,6 +28,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.Gravity;
 import android.view.SoundEffectConstants;
 import android.view.ViewDebug;
@@ -55,6 +56,7 @@
  * </p>
  */
 public abstract class CompoundButton extends Button implements Checkable {
+    private static final String LOG_TAG = CompoundButton.class.getSimpleName();
 
     private boolean mChecked;
     private boolean mBroadcasting;
@@ -173,7 +175,7 @@
             }
             final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
             if (afm != null) {
-                afm.valueChanged(this);
+                afm.notifyValueChanged(this);
             }
 
             mBroadcasting = false;
@@ -578,14 +580,18 @@
     public void onProvideAutofillStructure(ViewStructure structure, int flags) {
         super.onProvideAutofillStructure(structure, flags);
 
-        structure.setSanitized(mCheckedFromResource);
+        structure.setDataIsSensitive(!mCheckedFromResource);
     }
 
     @Override
     public void autofill(AutofillValue value) {
         if (!isEnabled()) return;
 
-        setChecked(value.getToggleValue());
+        if (value.isToggle()) {
+            setChecked(value.getToggleValue());
+        } else {
+            Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+        }
     }
 
     @Override
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index fa8316c..f63573f 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -29,6 +29,7 @@
 import android.os.Parcelable;
 import android.text.format.DateUtils;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.SparseArray;
 import android.view.View;
 import android.view.ViewStructure;
@@ -83,6 +84,8 @@
  */
 @Widget
 public class DatePicker extends FrameLayout {
+    private static final String LOG_TAG = DatePicker.class.getSimpleName();
+
     /**
      * Presentation mode for the Holo-style date picker that uses a set of
      * {@link android.widget.NumberPicker}s.
@@ -182,7 +185,7 @@
         mDelegate.setAutoFillChangeListener((v, y, m, d) -> {
             final AutofillManager afm = context.getSystemService(AutofillManager.class);
             if (afm != null) {
-                afm.valueChanged(this);
+                afm.notifyValueChanged(this);
             }
         });
     }
@@ -775,7 +778,11 @@
     public void autofill(AutofillValue value) {
         if (!isEnabled()) return;
 
-        mDelegate.updateDate(value.getDateValue());
+        if (value.isDate()) {
+            mDelegate.updateDate(value.getDateValue());
+        } else {
+            Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+        }
     }
 
     @Override
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index ade03e1..faa2310 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -157,6 +157,7 @@
     private static final int MENU_ITEM_ORDER_SELECT_ALL = 9;
     private static final int MENU_ITEM_ORDER_REPLACE = 10;
     private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 11;
+    private static final int MENU_ITEM_ORDER_AUTOFILL = 12;
 
     // Each Editor manages its own undo stack.
     private final UndoManager mUndoManager = new UndoManager();
@@ -2644,6 +2645,10 @@
                 .setAlphabeticShortcut('a')
                 .setEnabled(mTextView.canSelectAllText())
                 .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
+        menu.add(Menu.NONE, TextView.ID_AUTOFILL, MENU_ITEM_ORDER_AUTOFILL,
+                com.android.internal.R.string.autofill)
+                .setEnabled(mTextView.canRequestAutofill())
+                .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
 
         mPreserveSelection = true;
     }
@@ -3828,6 +3833,12 @@
                         .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
             }
 
+            if (mTextView.canRequestAutofill()) {
+                menu.add(Menu.NONE, TextView.ID_AUTOFILL, MENU_ITEM_ORDER_AUTOFILL,
+                        com.android.internal.R.string.autofill)
+                        .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+            }
+
             updateSelectAllItem(menu);
             updateReplaceItem(menu);
         }
diff --git a/core/java/android/widget/RadioGroup.java b/core/java/android/widget/RadioGroup.java
index bd62d6c..5e8279a 100644
--- a/core/java/android/widget/RadioGroup.java
+++ b/core/java/android/widget/RadioGroup.java
@@ -55,6 +55,7 @@
  *
  */
 public class RadioGroup extends LinearLayout {
+    private static final String LOG_TAG = RadioGroup.class.getSimpleName();
 
     // holds the checked id; the selection is empty by default
     private int mCheckedId = -1;
@@ -188,7 +189,7 @@
         }
         final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
         if (afm != null) {
-            afm.valueChanged(this);
+            afm.notifyValueChanged(this);
         }
     }
 
@@ -421,14 +422,21 @@
     @Override
     public void onProvideAutofillStructure(ViewStructure structure, int flags) {
         super.onProvideAutofillStructure(structure, flags);
-        structure.setSanitized(mCheckedId == mInitialCheckedId);
+        structure.setDataIsSensitive(mCheckedId != mInitialCheckedId);
     }
 
     @Override
     public void autofill(AutofillValue value) {
         if (!isEnabled()) return;
 
-        final int index = value.getListValue();
+        int index;
+        if (value.isList()) {
+            index = value.getListValue();
+        } else {
+            Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+            return;
+        }
+
         final View child = getChildAt(index);
         if (child == null) {
             Log.w(VIEW_LOG_TAG, "RadioGroup.autoFill(): no child with index " + index);
diff --git a/core/java/android/widget/TextClock.java b/core/java/android/widget/TextClock.java
index a6a9db4..59881b5 100644
--- a/core/java/android/widget/TextClock.java
+++ b/core/java/android/widget/TextClock.java
@@ -132,7 +132,7 @@
 
     private CharSequence mDescFormat;
 
-    private boolean mRegistered;
+    private boolean mAttached;
 
     private Calendar mTime;
     private String mTimeZone;
@@ -252,7 +252,7 @@
         }
 
         createTime(mTimeZone);
-        // Wait until registering for events to handle the ticker
+        // Wait until onAttachedToWindow() to handle the ticker
         chooseFormat(false);
     }
 
@@ -503,9 +503,12 @@
         boolean hadSeconds = mHasSeconds;
         mHasSeconds = DateFormat.hasSeconds(mFormat);
 
-        if (handleTicker && mRegistered && hadSeconds != mHasSeconds) {
-            if (hadSeconds) getHandler().removeCallbacks(mTicker);
-            else mTicker.run();
+        if (handleTicker && mAttached && hadSeconds != mHasSeconds) {
+            if (hadSeconds) {
+                getHandler().removeCallbacks(mTicker);
+            } else if (getVisibility() == VISIBLE) {
+                mTicker.run();
+            }
         }
     }
 
@@ -517,27 +520,50 @@
     }
 
     @Override
-    public void onVisibilityAggregated(boolean isVisible) {
-        if (!mRegistered && isVisible) {
-            mRegistered = true;
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        if (!mAttached) {
+            mAttached = true;
 
             registerReceiver();
             registerObserver();
 
             createTime(mTimeZone);
 
-            if (mHasSeconds) {
-                mTicker.run();
-            } else {
-                onTimeChanged();
+            if (getVisibility() == VISIBLE) {
+                if (mHasSeconds) {
+                    mTicker.run();
+                } else {
+                    onTimeChanged();
+                }
             }
-        } else if (mRegistered && !isVisible) {
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+
+        if (mAttached) {
             unregisterReceiver();
             unregisterObserver();
 
             getHandler().removeCallbacks(mTicker);
 
-            mRegistered = false;
+            mAttached = false;
+        }
+    }
+
+    @Override
+    public void onVisibilityAggregated(boolean isVisible) {
+        if (mAttached) {
+            if (isVisible && mHasSeconds) {
+                mTicker.run();
+            } else {
+                getHandler().removeCallbacks(mTicker);
+            }
+            onTimeChanged();
         }
     }
 
@@ -560,7 +586,7 @@
     }
 
     private void registerObserver() {
-        if (mRegistered) {
+        if (mAttached) {
             if (mFormatChangeObserver == null) {
                 mFormatChangeObserver = new FormatChangeObserver(getHandler());
             }
@@ -587,9 +613,11 @@
     }
 
     private void onTimeChanged() {
-        mTime.setTimeInMillis(System.currentTimeMillis());
-        setText(DateFormat.format(mFormat, mTime));
-        setContentDescription(DateFormat.format(mDescFormat, mTime));
+        if (getVisibility() == VISIBLE) {
+            mTime.setTimeInMillis(System.currentTimeMillis());
+            setText(DateFormat.format(mFormat, mTime));
+            setContentDescription(DateFormat.format(mDescFormat, mTime));
+        }
     }
 
     /** @hide */
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index ee70acc..c5c317d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9136,7 +9136,7 @@
             if (DEBUG_AUTOFILL) {
                 Log.v(LOG_TAG, "sendAfterTextChanged(): notify AFM for text=" + mText);
             }
-            afm.valueChanged(TextView.this);
+            afm.notifyValueChanged(TextView.this);
         }
     }
 
@@ -9900,7 +9900,7 @@
         final boolean isPassword = hasPasswordTransformationMethod()
                 || isPasswordInputType(getInputType());
         if (forAutofill) {
-            structure.setSanitized(mTextFromResource);
+            structure.setDataIsSensitive(!mTextFromResource);
         }
 
         if (!isPassword || forAutofill) {
@@ -10012,12 +10012,29 @@
 
     // TODO(b/33197203): add unit/CTS tests for autofill methods
 
+    boolean canRequestAutofill() {
+        final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
+        if (afm != null) {
+            return afm.isEnabled();
+        }
+        return false;
+    }
+
+    private void requestAutofill() {
+        final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
+        if (afm != null) {
+            afm.requestAutofill(this);
+        }
+    }
+
     @Override
     public void autofill(AutofillValue value) {
-        final CharSequence text = value.getTextValue();
-
-        if (text != null && isTextEditable()) {
-            setText(text, mBufferType, true, 0);
+        if (value.isText()) {
+            if (isTextEditable()) {
+                setText(value.getTextValue(), mBufferType, true, 0);
+            }
+        } else {
+            Log.w(LOG_TAG, value + " could not be autofilled into " + this);
         }
     }
 
@@ -10479,6 +10496,7 @@
     static final int ID_PASTE_AS_PLAIN_TEXT = android.R.id.pasteAsPlainText;
     static final int ID_REPLACE = android.R.id.replaceText;
     static final int ID_ASSIST = android.R.id.textAssist;
+    static final int ID_AUTOFILL = android.R.id.autofill;
 
     /**
      * Called when a context menu option for the text view is selected.  Currently
@@ -10543,6 +10561,11 @@
             case ID_SHARE:
                 shareSelectedText();
                 return true;
+
+            case ID_AUTOFILL:
+                requestAutofill();
+                stopTextActionMode();
+                return true;
         }
         return false;
     }
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 1435983..cfa78b5 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -27,6 +27,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.util.MathUtils;
 import android.view.View;
 import android.view.ViewStructure;
@@ -53,6 +54,8 @@
  */
 @Widget
 public class TimePicker extends FrameLayout {
+    private static final String LOG_TAG = TimePicker.class.getSimpleName();
+
     /**
      * Presentation mode for the Holo-style time picker that uses a set of
      * {@link android.widget.NumberPicker}s.
@@ -144,7 +147,7 @@
         mDelegate.setAutoFillChangeListener((v, h, m) -> {
             final AutofillManager afm = context.getSystemService(AutofillManager.class);
             if (afm != null) {
-                afm.valueChanged(this);
+                afm.notifyValueChanged(this);
             }
         });
     }
@@ -530,7 +533,11 @@
     public void autofill(AutofillValue value) {
         if (!isEnabled()) return;
 
-        mDelegate.setDate(value.getDateValue());
+        if (value.isDate()) {
+            mDelegate.setDate(value.getDateValue());
+        } else {
+            Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+        }
     }
 
     @Override
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 3ac5a72..83cc9f0 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -18,6 +18,7 @@
 
 import android.annotation.CallSuper;
 import android.content.ContentResolver;
+import android.content.ContentValues;
 import android.content.Intent;
 import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
@@ -157,10 +158,9 @@
         if (!before.renameTo(after)) {
             throw new IllegalStateException("Failed to rename to " + after);
         }
-        removeFromMediaStore(visibleFileBefore);
 
         final String afterDocId = getDocIdForFile(after);
-        scanFile(getFileForDocId(afterDocId, true));
+        moveInMediaStore(visibleFileBefore, getFileForDocId(afterDocId, true));
 
         if (!TextUtils.equals(docId, afterDocId)) {
             return afterDocId;
@@ -170,22 +170,6 @@
     }
 
     @Override
-    public void deleteDocument(String docId) throws FileNotFoundException {
-        final File file = getFileForDocId(docId);
-        final File visibleFile = getFileForDocId(docId, true);
-
-        final boolean isDirectory = file.isDirectory();
-        if (isDirectory) {
-            FileUtils.deleteContents(file);
-        }
-        if (!file.delete()) {
-            throw new IllegalStateException("Failed to delete " + file);
-        }
-
-        removeFromMediaStore(visibleFile);
-    }
-
-    @Override
     public String moveDocument(String sourceDocumentId, String sourceParentDocumentId,
             String targetParentDocumentId)
             throws FileNotFoundException {
@@ -200,22 +184,56 @@
             throw new IllegalStateException("Failed to move to " + after);
         }
 
-        // Notify media store to update its content
-        removeFromMediaStore(visibleFileBefore);
         final String docId = getDocIdForFile(after);
-        scanFile(getFileForDocId(docId, true));
+        moveInMediaStore(visibleFileBefore, getFileForDocId(docId, true));
 
         return docId;
     }
 
-    private void removeFromMediaStore(File visibleFile) throws FileNotFoundException {
+    private void moveInMediaStore(File oldVisibleFile, File newVisibleFile) {
+        if (newVisibleFile != null) {
+            final ContentResolver resolver = getContext().getContentResolver();
+            final Uri externalUri = MediaStore.Files.getContentUri("external");
+
+            ContentValues values = new ContentValues();
+            values.put(MediaStore.Files.FileColumns.DATA, newVisibleFile.getAbsolutePath());
+
+            // Logic borrowed from MtpDatabase.
+            // note - we are relying on a special case in MediaProvider.update() to update
+            // the paths for all children in the case where this is a directory.
+            final String path = oldVisibleFile.getAbsolutePath();
+            resolver.update(externalUri,
+                    values,
+                    "_data LIKE ? AND lower(_data)=lower(?)",
+                    new String[] { path, path });
+        }
+    }
+
+    @Override
+    public void deleteDocument(String docId) throws FileNotFoundException {
+        final File file = getFileForDocId(docId);
+        final File visibleFile = getFileForDocId(docId, true);
+
+        final boolean isDirectory = file.isDirectory();
+        if (isDirectory) {
+            FileUtils.deleteContents(file);
+        }
+        if (!file.delete()) {
+            throw new IllegalStateException("Failed to delete " + file);
+        }
+
+        removeFromMediaStore(visibleFile, isDirectory);
+    }
+
+    private void removeFromMediaStore(File visibleFile, boolean isFolder)
+            throws FileNotFoundException {
         if (visibleFile != null) {
             final ContentResolver resolver = getContext().getContentResolver();
             final Uri externalUri = MediaStore.Files.getContentUri("external");
 
             // Remove media store entries for any files inside this directory, using
             // path prefix match. Logic borrowed from MtpDatabase.
-            if (visibleFile.isDirectory()) {
+            if (isFolder) {
                 final String path = visibleFile.getAbsolutePath() + "/";
                 resolver.delete(externalUri,
                         "_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)",
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index ec3aac2..c840f26 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -48,12 +48,12 @@
         List<NotificationChannel> channelsList = new ArrayList<NotificationChannel>();
         channelsList.add(new NotificationChannel(
                 VIRTUAL_KEYBOARD,
-                R.string.notification_channel_virtual_keyboard,
+                context.getString(R.string.notification_channel_virtual_keyboard),
                 NotificationManager.IMPORTANCE_LOW));
 
         final NotificationChannel physicalKeyboardChannel = new NotificationChannel(
                 PHYSICAL_KEYBOARD,
-                R.string.notification_channel_physical_keyboard,
+                context.getString(R.string.notification_channel_physical_keyboard),
                 NotificationManager.IMPORTANCE_DEFAULT);
         physicalKeyboardChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
                 Notification.AUDIO_ATTRIBUTES_DEFAULT);
@@ -61,32 +61,32 @@
 
         channelsList.add(new NotificationChannel(
                 SECURITY,
-                R.string.notification_channel_security,
+                context.getString(R.string.notification_channel_security),
                 NotificationManager.IMPORTANCE_LOW));
 
         channelsList.add(new NotificationChannel(
                 CAR_MODE,
-                R.string.notification_channel_car_mode,
+                context.getString(R.string.notification_channel_car_mode),
                 NotificationManager.IMPORTANCE_LOW));
 
         channelsList.add(new NotificationChannel(
                 DEVELOPER,
-                R.string.notification_channel_developer,
+                context.getString(R.string.notification_channel_developer),
                 NotificationManager.IMPORTANCE_LOW));
 
         channelsList.add(new NotificationChannel(
                 UPDATES,
-                R.string.notification_channel_updates,
+                context.getString(R.string.notification_channel_updates),
                 NotificationManager.IMPORTANCE_LOW));
 
         channelsList.add(new NotificationChannel(
                 NETWORK_STATUS,
-                R.string.notification_channel_network_status,
+                context.getString(R.string.notification_channel_network_status),
                 NotificationManager.IMPORTANCE_LOW));
 
         final NotificationChannel networkAlertsChannel = new NotificationChannel(
                 NETWORK_ALERTS,
-                R.string.notification_channel_network_alerts,
+                context.getString(R.string.notification_channel_network_alerts),
                 NotificationManager.IMPORTANCE_HIGH);
         networkAlertsChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
                 Notification.AUDIO_ATTRIBUTES_DEFAULT);
@@ -94,17 +94,17 @@
 
         channelsList.add(new NotificationChannel(
                 VPN,
-                R.string.notification_channel_vpn,
+                context.getString(R.string.notification_channel_vpn),
                 NotificationManager.IMPORTANCE_LOW));
 
         channelsList.add(new NotificationChannel(
                 DEVICE_ADMIN,
-                R.string.notification_channel_device_admin,
+                context.getString(R.string.notification_channel_device_admin),
                 NotificationManager.IMPORTANCE_LOW));
 
         final NotificationChannel alertsChannel = new NotificationChannel(
                 ALERTS,
-                R.string.notification_channel_alerts,
+                context.getString(R.string.notification_channel_alerts),
                 NotificationManager.IMPORTANCE_DEFAULT);
         alertsChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
                 Notification.AUDIO_ATTRIBUTES_DEFAULT);
@@ -112,12 +112,12 @@
 
         channelsList.add(new NotificationChannel(
                 RETAIL_MODE,
-                R.string.notification_channel_retail_mode,
+                context.getString(R.string.notification_channel_retail_mode),
                 NotificationManager.IMPORTANCE_LOW));
 
         channelsList.add(new NotificationChannel(
                 USB,
-                R.string.notification_channel_usb,
+                context.getString(R.string.notification_channel_usb),
                 NotificationManager.IMPORTANCE_MIN));
 
         nm.createNotificationChannels(channelsList);
@@ -128,7 +128,7 @@
         final NotificationManager nm = context.getSystemService(NotificationManager.class);
         nm.createNotificationChannelsForPackage(pkg, Arrays.asList(new NotificationChannel(
                 ACCOUNT,
-                R.string.notification_channel_account,
+                context.getString(R.string.notification_channel_account),
                 NotificationManager.IMPORTANCE_LOW)));
     }
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 2aeddb3..6aa7766 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -6027,7 +6027,8 @@
          * Clear all stats for this uid.  Returns true if the uid is completely
          * inactive so can be dropped.
          */
-        boolean reset() {
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public boolean reset() {
             boolean active = false;
 
             if (mWifiRunningTimer != null) {
@@ -6968,7 +6969,10 @@
 
             boolean reset() {
                 if (mBgCounter != null) {
-                    mBgCounter.reset(true);
+                    mBgCounter.reset(true /*detachIfReset*/);
+                    // If we detach, we must null the mBgCounter reference so that it
+                    // can be recreated and attached.
+                    mBgCounter = null;
                 }
                 if (mTimer.reset(true)) {
                     mTimer = null;
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
index 96468ab..0a9faa1 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -142,6 +142,21 @@
             Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from wrapper");
         }
 
-        RuntimeInit.applicationInit(targetSdkVersion, argv, null);
+        // Check whether the first argument is a "-cp" in argv, and assume the next argument is the
+        // classpath. If found, create a PathClassLoader and use it for applicationInit.
+        ClassLoader classLoader = null;
+        if (argv != null && argv.length > 2 && argv[0].equals("-cp")) {
+            classLoader = ZygoteInit.createPathClassLoader(argv[1], targetSdkVersion);
+
+            // Install this classloader as the context classloader, too.
+            Thread.currentThread().setContextClassLoader(classLoader);
+
+            // Remove the classpath from the arguments.
+            String removedArgs[] = new String[argv.length - 2];
+            System.arraycopy(argv, 2, removedArgs, 0, argv.length - 2);
+            argv = removedArgs;
+        }
+
+        RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
     }
 }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index a7e900a..b2a2fec 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -132,9 +132,6 @@
         Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
         preloadOpenGL();
         Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
-        Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadOpenGL");
-        preloadOpenGL();
-        Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
         preloadSharedLibraries();
         preloadTextResources();
         // Ask the WebViewFactory to do any initialization that must run in the zygote process,
@@ -471,7 +468,8 @@
                 String[] amendedArgs = new String[args.length + 2];
                 amendedArgs[0] = "-cp";
                 amendedArgs[1] = systemServerClasspath;
-                System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
+                System.arraycopy(args, 0, amendedArgs, 2, args.length);
+                args = amendedArgs;
             }
 
             WrapperInit.execApplication(parsedArgs.invokeWith,
@@ -480,8 +478,7 @@
         } else {
             ClassLoader cl = null;
             if (systemServerClasspath != null) {
-                cl = createSystemServerClassLoader(systemServerClasspath,
-                                                   parsedArgs.targetSdkVersion);
+                cl = createPathClassLoader(systemServerClasspath, parsedArgs.targetSdkVersion);
 
                 Thread.currentThread().setContextClassLoader(cl);
             }
@@ -496,15 +493,14 @@
     }
 
     /**
-     * Creates a PathClassLoader for the system server. It also creates
-     * a shared namespace associated with the classloader to let it access
-     * platform-private native libraries.
+     * Creates a PathClassLoader for the given class path that is associated with a shared
+     * namespace, i.e., this classloader can access platform-private native libraries. The
+     * classloader will use java.library.path as the native library path.
      */
-    private static PathClassLoader createSystemServerClassLoader(String systemServerClasspath,
-                                                                 int targetSdkVersion) {
+    static PathClassLoader createPathClassLoader(String classPath, int targetSdkVersion) {
       String libraryPath = System.getProperty("java.library.path");
 
-      return PathClassLoaderFactory.createClassLoader(systemServerClasspath,
+      return PathClassLoaderFactory.createClassLoader(classPath,
                                                       libraryPath,
                                                       libraryPath,
                                                       ClassLoader.getSystemClassLoader(),
@@ -585,7 +581,6 @@
             OsConstants.CAP_SYS_MODULE,
             OsConstants.CAP_SYS_NICE,
             OsConstants.CAP_SYS_PTRACE,
-            OsConstants.CAP_SYS_RESOURCE,
             OsConstants.CAP_SYS_TIME,
             OsConstants.CAP_SYS_TTY_CONFIG,
             OsConstants.CAP_WAKE_ALARM
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 804bd29..6c9280a 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -3585,7 +3585,8 @@
             synchronized (mWindows) {
                 if (!mIsWatching) {
                     try {
-                        WindowManagerHolder.sWindowManager.watchRotation(this);
+                        WindowManagerHolder.sWindowManager.watchRotation(this,
+                                phoneWindow.getContext().getDisplay().getDisplayId());
                         mHandler = new Handler();
                         mIsWatching = true;
                     } catch (RemoteException ex) {
diff --git a/core/java/com/android/internal/policy/PipSnapAlgorithm.java b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
index ae31873..95d714f 100644
--- a/core/java/com/android/internal/policy/PipSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
@@ -321,7 +321,7 @@
                 stackBounds.top));
         boundsOut.set(stackBounds);
         if (mIsMinimized) {
-            boundsOut.offsetTo(boundedLeft, boundsOut.top);
+            boundsOut.offsetTo(boundedLeft, boundedTop);
             return;
         }
 
diff --git a/core/java/com/android/internal/util/IntPair.java b/core/java/com/android/internal/util/IntPair.java
new file mode 100644
index 0000000..7992507
--- /dev/null
+++ b/core/java/com/android/internal/util/IntPair.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+/**
+ * Utilities for treating a {@code long} as a pair of {@code int}s
+ *
+ * @hide
+ */
+public class IntPair {
+    private IntPair() {}
+
+    public static long of(int first, int second) {
+        return (((long)first) << 32) | ((long)second & 0xffffffffL);
+    }
+
+    public static int first(long intPair) {
+        return (int)(intPair >> 32);
+    }
+
+    public static int second(long intPair) {
+        return (int)intPair;
+    }
+}
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 79b0cd1..818cc2c 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -1163,21 +1163,21 @@
                         isLastItem && menuItemButtonWidth <= availableWidth - extraPadding;
                 if (canFitWithOverflow || canFitNoOverflow) {
                     if (isNewGroup) {
-                        final View border = createBorder(mContext);
-                        final int borderWidth = border.getLayoutParams().width;
+                        final View divider = createDivider(mContext);
+                        final int dividerWidth = divider.getLayoutParams().width;
 
                         // Add extra padding to the end of the previous button.
                         // Half of the extra padding (less borderWidth) goes to the previous button.
                         View previousButton = mMainPanel.getChildAt(mMainPanel.getChildCount() - 1);
                         final int prevPaddingEnd = previousButton.getPaddingEnd()
-                                + extraPadding / 2 - borderWidth;
+                                + extraPadding / 2 - dividerWidth;
                         previousButton.setPaddingRelative(
                                 previousButton.getPaddingStart(),
                                 previousButton.getPaddingTop(),
                                 prevPaddingEnd,
                                 previousButton.getPaddingBottom());
                         final ViewGroup.LayoutParams prevParams = previousButton.getLayoutParams();
-                        prevParams.width += extraPadding / 2 - borderWidth;
+                        prevParams.width += extraPadding / 2 - dividerWidth;
                         previousButton.setLayoutParams(prevParams);
 
                         // Add extra padding to the start of this button.
@@ -1190,8 +1190,8 @@
                                 menuItemButton.getPaddingEnd(),
                                 menuItemButton.getPaddingBottom());
 
-                        // Include a border.
-                        mMainPanel.addView(border);
+                        // Include a divider.
+                        mMainPanel.addView(divider);
                     }
 
                     setButtonTagAndClickListener(menuItemButton, menuItem);
@@ -1670,21 +1670,28 @@
         return popupWindow;
     }
 
-    private static View createBorder(Context context) {
+    private static View createDivider(Context context) {
         // TODO: Inflate this instead.
-        View border = new View(context);
+        View divider = new View(context);
+
         int _1dp = (int) TypedValue.applyDimension(
                 TypedValue.COMPLEX_UNIT_DIP, 1, context.getResources().getDisplayMetrics());
         LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                 _1dp, ViewGroup.LayoutParams.MATCH_PARENT);
         params.setMarginsRelative(0, _1dp * 10, 0, _1dp * 10);
-        border.setLayoutParams(params);
-        border.setBackgroundColor(Color.parseColor("#9E9E9E"));
-        border.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-        border.setEnabled(false);
-        border.setFocusable(false);
-        border.setContentDescription(null);
-        return border;
+        divider.setLayoutParams(params);
+
+        TypedArray a = context.obtainStyledAttributes(
+                new TypedValue().data, new int[] { R.attr.floatingToolbarDividerColor });
+        divider.setBackgroundColor(a.getColor(0, 0));
+        a.recycle();
+
+        divider.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+        divider.setEnabled(false);
+        divider.setFocusable(false);
+        divider.setContentDescription(null);
+
+        return divider;
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/ImageFloatingTextView.java b/core/java/com/android/internal/widget/ImageFloatingTextView.java
index 80207ee..e86932c 100644
--- a/core/java/com/android/internal/widget/ImageFloatingTextView.java
+++ b/core/java/com/android/internal/widget/ImageFloatingTextView.java
@@ -115,6 +115,7 @@
         // Lets calculate how many lines the given measurement allows us.
         int availableHeight = height - mPaddingTop - mPaddingBottom;
         int maxLines = availableHeight / getLineHeight();
+        maxLines = Math.max(1, maxLines);
         if (getMaxLines() > 0) {
             maxLines = Math.min(getMaxLines(), maxLines);
         }
diff --git a/core/java/com/android/internal/widget/MessagingLinearLayout.java b/core/java/com/android/internal/widget/MessagingLinearLayout.java
index b259ad1..1104318 100644
--- a/core/java/com/android/internal/widget/MessagingLinearLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLinearLayout.java
@@ -16,8 +16,6 @@
 
 package com.android.internal.widget;
 
-import com.android.internal.R;
-
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -28,6 +26,8 @@
 import android.view.ViewGroup;
 import android.widget.RemoteViews;
 
+import com.android.internal.R;
+
 /**
  * A custom-built layout for the Notification.MessagingStyle.
  *
@@ -119,23 +119,30 @@
                 }
                 final View child = getChildAt(i);
                 LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
-
+                ImageFloatingTextView textChild = null;
                 if (child instanceof ImageFloatingTextView) {
                     // Pretend we need the image padding for all views, we don't know which
                     // one will end up needing to do this (might end up not using all the space,
                     // but calculating this exactly would be more expensive).
-                    ((ImageFloatingTextView) child).setNumIndentLines(
-                            mIndentLines == 2 ? 3 : mIndentLines);
+                    textChild = (ImageFloatingTextView) child;
+                    textChild.setNumIndentLines(mIndentLines == 2 ? 3 : mIndentLines);
                 }
 
-                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
+                int spacing = first ? 0 : mSpacing;
+                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, totalHeight
+                        - mPaddingTop - mPaddingBottom + spacing);
 
                 final int childHeight = child.getMeasuredHeight();
                 int newHeight = Math.max(totalHeight, totalHeight + childHeight + lp.topMargin +
-                        lp.bottomMargin + (first ? 0 : mSpacing));
+                        lp.bottomMargin + spacing);
                 first = false;
+                boolean measuredTooSmall = false;
+                if (textChild != null) {
+                    measuredTooSmall = childHeight < textChild.getLayout().getHeight()
+                            + textChild.getPaddingTop() + textChild.getPaddingBottom();
+                }
 
-                if (newHeight <= targetHeight) {
+                if (newHeight <= targetHeight && !measuredTooSmall) {
                     totalHeight = newHeight;
                     lp.hide = false;
                 } else {
@@ -168,7 +175,15 @@
                 }
                 boolean changed = textChild.setNumIndentLines(Math.max(0, imageLines));
                 if (changed || !recalculateVisibility) {
-                    measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
+                    final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
+                            mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin,
+                            lp.width);
+                    // we want to measure it at most as high as it is currently, otherwise we'll
+                    // drop later lines
+                    final int childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
+                            targetHeight - child.getMeasuredHeight(), lp.height);
+
+                    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);;
                 }
                 imageLines -= textChild.getLineCount();
             }
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index f852194..a8d6830 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -453,7 +453,8 @@
     dst = dstBitmap.getAddr(x, y);
 
     SkColorSpace* colorSpace = dstBitmap.colorSpace();
-    if (GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
+    if (dstBitmap.colorType() == kRGBA_F16_SkColorType ||
+            GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
         // now copy/convert each scanline
         for (int y = 0; y < height; y++) {
             proc(dst, src, width, x, y);
@@ -1267,7 +1268,8 @@
     proc(dst, src, 1, bitmap.getColorTable());
 
     SkColorSpace* colorSpace = bitmap.colorSpace();
-    if (!GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
+    if (bitmap.colorType() != kRGBA_F16_SkColorType &&
+            !GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
         auto sRGB = SkColorSpace::MakeSRGB();
         auto xform = SkColorSpaceXform::New(colorSpace, sRGB.get());
         xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, &dst[0],
@@ -1299,7 +1301,8 @@
     SkColor* d = (SkColor*)dst + offset;
 
     SkColorSpace* colorSpace = bitmap.colorSpace();
-    if (GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
+    if (bitmap.colorType() == kRGBA_F16_SkColorType ||
+            GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
         while (--height >= 0) {
             proc(d, src, width, ctable);
             d += stride;
@@ -1342,7 +1345,8 @@
     }
 
     SkColorSpace* colorSpace = bitmap.colorSpace();
-    if (!GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
+    if (bitmap.colorType() != kRGBA_F16_SkColorType &&
+            !GraphicsJNI::isColorSpaceSRGB(colorSpace)) {
         auto sRGB = SkColorSpace::MakeSRGB();
         auto xform = SkColorSpaceXform::New(sRGB.get(), colorSpace);
         xform->apply(SkColorSpaceXform::kBGRA_8888_ColorFormat, &color,
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index c1bb69d..e64a574 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -417,11 +417,7 @@
     // For wide gamut images, we will leave the color space on the SkBitmap.  Otherwise,
     // use the default.
     SkImageInfo bitmapInfo = decodeInfo;
-    sk_sp<SkColorSpace> srgb =
-            SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
-                                  SkColorSpace::kSRGB_Gamut,
-                                  SkColorSpace::kNonLinearBlending_ColorSpaceFlag);
-    if (decodeInfo.colorSpace() == srgb.get()) {
+    if (decodeInfo.colorSpace() && decodeInfo.colorSpace()->isSRGB()) {
         bitmapInfo = bitmapInfo.makeColorSpace(GraphicsJNI::colorSpaceForType(decodeColorType));
     }
 
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 49024b6..fb7c5c4 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -44,6 +44,7 @@
     uint32_t langId;
     int variant;
     std::vector<minikin::Font> fonts;
+    std::vector<minikin::FontVariation> axes;
 };
 
 static jlong FontFamily_initBuilder(JNIEnv* env, jobject clazz, jstring lang, jint variant) {
@@ -155,32 +156,16 @@
 }
 
 static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong builderPtr,
-        jobject font, jint ttcIndex, jobject listOfAxis, jint weight, jboolean isItalic) {
+        jobject font, jint ttcIndex, jint weight, jboolean isItalic) {
     NPE_CHECK_RETURN_ZERO(env, font);
 
+    NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
+
     // Declare axis native type.
-    std::unique_ptr<SkFontMgr::FontParameters::Axis[]> skiaAxes;
-    int skiaAxesLength = 0;
-    if (listOfAxis) {
-        ListHelper list(env, listOfAxis);
-        jint listSize = list.size();
-
-        skiaAxes.reset(new SkFontMgr::FontParameters::Axis[listSize]);
-        skiaAxesLength = listSize;
-        for (jint i = 0; i < listSize; ++i) {
-            jobject axisObject = list.get(i);
-            if (!axisObject) {
-                skiaAxes[i].fTag = 0;
-                skiaAxes[i].fStyleValue = 0;
-                continue;
-            }
-            AxisHelper axis(env, axisObject);
-
-            jint tag = axis.getTag();
-            jfloat stylevalue = axis.getStyleValue();
-            skiaAxes[i].fTag = tag;
-            skiaAxes[i].fStyleValue = SkFloatToScalar(stylevalue);
-        }
+    std::vector<SkFontMgr::FontParameters::Axis> skiaAxes;
+    skiaAxes.reserve(builder->axes.size());
+    for (const minikin::FontVariation& minikinAxis : builder->axes) {
+        skiaAxes.push_back({minikinAxis.axisTag, minikinAxis.value});
     }
 
     const void* fontPtr = env->GetDirectBufferAddress(font);
@@ -200,7 +185,7 @@
 
     SkFontMgr::FontParameters params;
     params.setCollectionIndex(ttcIndex);
-    params.setAxes(skiaAxes.get(), skiaAxesLength);
+    params.setAxes(skiaAxes.data(), skiaAxes.size());
 
     sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
     sk_sp<SkTypeface> face(fm->createFromStream(fontData.release(), params));
@@ -211,7 +196,6 @@
     std::shared_ptr<minikin::MinikinFont> minikinFont =
             std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, ttcIndex,
                     std::vector<minikin::FontVariation>());
-    NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
     builder->fonts.push_back(minikin::Font(std::move(minikinFont),
             minikin::FontStyle(weight / 100, isItalic)));
     return true;
@@ -270,6 +254,11 @@
     return true;
 }
 
+static void FontFamily_addAxisValue(jlong builderPtr, jint tag, jfloat value) {
+    NativeFamilyBuilder* builder = reinterpret_cast<NativeFamilyBuilder*>(builderPtr);
+    builder->axes.push_back({static_cast<minikin::AxisTag>(tag), value});
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static const JNINativeMethod gFontFamilyMethods[] = {
@@ -278,10 +267,11 @@
     { "nAbort",                "(J)V", (void*)FontFamily_abort },
     { "nUnrefFamily",          "(J)V", (void*)FontFamily_unref },
     { "nAddFont",              "(JLjava/nio/ByteBuffer;I)Z", (void*)FontFamily_addFont },
-    { "nAddFontWeightStyle",   "(JLjava/nio/ByteBuffer;ILjava/util/List;IZ)Z",
+    { "nAddFontWeightStyle",   "(JLjava/nio/ByteBuffer;IIZ)Z",
             (void*)FontFamily_addFontWeightStyle },
     { "nAddFontFromAssetManager",    "(JLandroid/content/res/AssetManager;Ljava/lang/String;IZIZ)Z",
             (void*)FontFamily_addFontFromAssetManager },
+    { "nAddAxisValue",         "(JIF)V", (void*)FontFamily_addAxisValue },
 };
 
 int register_android_graphics_FontFamily(JNIEnv* env)
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 7c56c7b..e66587a 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -461,12 +461,7 @@
 }
 
 bool GraphicsJNI::isColorSpaceSRGB(SkColorSpace* colorSpace) {
-    return colorSpace == nullptr
-            || colorSpace == SkColorSpace::MakeSRGB().get()
-            || colorSpace == SkColorSpace::MakeRGB(
-                  SkColorSpace::kSRGB_RenderTargetGamma,
-                  SkColorSpace::kSRGB_Gamut,
-                  SkColorSpace::kNonLinearBlending_ColorSpaceFlag).get();
+    return colorSpace == nullptr || colorSpace->isSRGB();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 1846237..fa25a8f 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -239,30 +239,38 @@
         return result;
     }
 
-    static jint doTextRunCursor(JNIEnv *env, Paint* paint, const jchar *text, jint start,
-            jint count, jint flags, jint offset, jint opt) {
+    static jint doTextRunCursor(JNIEnv *env, Paint* paint, Typeface* typeface, const jchar *text,
+            jint start, jint count, jint dir, jint offset, jint opt) {
         minikin::GraphemeBreak::MoveOpt moveOpt = minikin::GraphemeBreak::MoveOpt(opt);
-        size_t result = minikin::GraphemeBreak::getTextRunCursor(text, start, count, offset,
-                moveOpt);
+        int bidiFlags = dir == 1 ? minikin::kBidi_Force_RTL : minikin::kBidi_Force_LTR;
+        std::unique_ptr<float[]> advancesArray(new float[count]);
+        MinikinUtils::measureText(paint, bidiFlags, typeface, text, start, count, start + count,
+                advancesArray.get());
+        size_t result = minikin::GraphemeBreak::getTextRunCursor(advancesArray.get(), text,
+                start, count, offset, moveOpt);
         return static_cast<jint>(result);
     }
 
-    static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray text,
-            jint contextStart, jint contextCount, jint dir, jint offset, jint cursorOpt) {
+    static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle,
+            jlong typefaceHandle, jcharArray text, jint contextStart, jint contextCount, jint dir,
+            jint offset, jint cursorOpt) {
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
         jchar* textArray = env->GetCharArrayElements(text, nullptr);
-        jint result = doTextRunCursor(env, paint, textArray, contextStart, contextCount, dir,
-                offset, cursorOpt);
+        jint result = doTextRunCursor(env, paint, typeface, textArray,
+                contextStart, contextCount, dir, offset, cursorOpt);
         env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
         return result;
     }
 
-    static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, jlong paintHandle, jstring text,
-            jint contextStart, jint contextEnd, jint dir, jint offset, jint cursorOpt) {
+    static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, jlong paintHandle,
+            jlong typefaceHandle, jstring text, jint contextStart, jint contextEnd, jint dir,
+            jint offset, jint cursorOpt) {
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
         const jchar* textArray = env->GetStringChars(text, nullptr);
-        jint result = doTextRunCursor(env, paint, textArray, contextStart,
-                contextEnd - contextStart, dir, offset, cursorOpt);
+        jint result = doTextRunCursor(env, paint, typeface, textArray,
+                contextStart, contextEnd - contextStart, dir, offset, cursorOpt);
         env->ReleaseStringChars(text, textArray);
         return result;
     }
@@ -983,8 +991,8 @@
     {"nGetTextAdvances","(JJLjava/lang/String;IIIII[FI)F",
             (void*) PaintGlue::getTextAdvances__StringIIIII_FI},
 
-    {"nGetTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C},
-    {"nGetTextRunCursor", "(JLjava/lang/String;IIIII)I",
+    {"nGetTextRunCursor", "(JJ[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C},
+    {"nGetTextRunCursor", "(JJLjava/lang/String;IIIII)I",
             (void*) PaintGlue::getTextRunCursor__String},
     {"nGetTextPath", "(JJI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C},
     {"nGetTextPath", "(JJILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String},
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index 1bd2333..678041f 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -404,6 +404,11 @@
     signalExceptionForError(env, err);
 }
 
+static void JHwParcel_native_release(
+        JNIEnv *env, jobject thiz) {
+    JHwParcel::GetNativeContext(env, thiz)->setParcel(NULL, false /* assumeOwnership */);
+}
+
 static void JHwParcel_native_releaseTemporaryStorage(
         JNIEnv *env, jobject thiz) {
     JHwParcel::GetNativeContext(env, thiz)->getStorage()->release(env);
@@ -955,6 +960,10 @@
 
     { "writeBuffer", "(L" PACKAGE_PATH "/HwBlob;)V",
         (void *)JHwParcel_native_writeBuffer },
+
+    { "release", "()V",
+        (void *)JHwParcel_native_release },
+
 };
 
 namespace android {
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 7eb0582..98c9e78 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -97,7 +97,7 @@
     SettingProto download_max_bytes_over_mobile = 52;
     SettingProto download_recommended_max_bytes_over_mobile = 53;
     SettingProto hdmi_control_enabled = 54;
-    SettingProto hdmi_system_audio_enabled = 55;
+    SettingProto hdmi_system_audio_control_enabled = 55;
     SettingProto hdmi_control_auto_wakeup_enabled = 56;
     SettingProto hdmi_control_auto_device_off_enabled = 57;
     SettingProto mhl_input_switching_enabled = 58;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c991f22..58e4051 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3593,7 +3593,7 @@
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
-        <service android:name="com.android.server.BackgroundDexOptJobService"
+        <service android:name="com.android.server.pm.BackgroundDexOptService"
                  android:exported="true"
                  android:permission="android.permission.BIND_JOB_SERVICE">
         </service>
diff --git a/core/res/res/layout/autofill_dataset_picker.xml b/core/res/res/layout/autofill_dataset_picker.xml
index 9b90de6..133265b 100644
--- a/core/res/res/layout/autofill_dataset_picker.xml
+++ b/core/res/res/layout/autofill_dataset_picker.xml
@@ -18,6 +18,7 @@
     android:id="@+id/autofill_dataset_picker"
     android:layout_width="wrap_content"
     android:layout_height="fill_parent"
-    android:divider="?android:attr/listDivider"
-    android:background="#ffffffff">
+    android:divider="@null"
+    android:background="#ffffffff"
+    android:elevation="@dimen/floating_window_z">
 </ListView>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 21c8780..4432e3c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -576,6 +576,7 @@
        <attr name="floatingToolbarItemBackgroundDrawable" format="reference" />
        <attr name="floatingToolbarOpenDrawable" format="reference" />
        <attr name="floatingToolbarPopupBackgroundDrawable" format="reference" />
+       <attr name="floatingToolbarDividerColor" format="reference" />
 
         <!-- ============ -->
         <!-- Alert Dialog styles -->
@@ -2307,37 +2308,9 @@
         </attr>
 
         <!-- Describes the content of a view so that a autofill service can fill in the appropriate
-             data. Multiple flags can be combined to mean e.g. emailAddress or postalAddress. -->
-        <attr name="autofillHint">
-            <!-- No hint. -->
-            <flag name="none" value="0" />
-            <!-- The view contains an email address. -->
-            <flag name="emailAddress" value="0x1" />
-            <!-- The view contains a real name. -->
-            <flag name="name" value="0x2" />
-            <!-- The view contains a user name. -->
-            <flag name="username" value="0x4" />
-            <!-- The view contains a password. -->
-            <flag name="password" value="0x8" />
-            <!-- The view contains a phone number. -->
-            <flag name="phone" value="0x10" />
-            <!-- The view contains a postal address. -->
-            <flag name="postalAddress" value="0x20" />
-            <!-- The view contains a postal code. -->
-            <flag name="postalCode" value="0x40" />
-            <!-- The view contains a credit card number. -->
-            <flag name="creditCardNumber" value="0x80" />
-            <!-- The view contains a credit card security code -->
-            <flag name="creditCardSecurityCode" value="0x100" />
-            <!-- The view contains a credit card expiration date -->
-            <flag name="creditCardExpirationDate" value="0x200" />
-            <!-- The view contains the month a credit card expires -->
-            <flag name="creditCardExpirationMonth" value="0x400" />
-            <!-- The view contains the year a credit card expires -->
-            <flag name="creditCardExpirationYear" value="0x800" />
-            <!-- The view contains the day a credit card expires -->
-            <flag name="creditCardExpirationDay" value="0x1000" />
-        </attr>
+             data. Multiple hints can be combined in a comma separated list or an array of strings
+             to mean e.g. emailAddress or postalAddress. -->
+        <attr name="autofillHint" format="string|reference" />
 
         <!-- Hints the Android System whether the view node associated with this View should be
              included in a view structure used for autofill purposes. -->
@@ -7213,7 +7186,7 @@
         <!-- The key of another Preference that this Preference will depend on.  If the other
              Preference is not set or is off, this Preference will be disabled. -->
         <attr name="dependency" format="string" />
-        <!-- Whether the Preference stores its value to the shared preferences. -->
+        <!-- Whether the Preference stores its value to the storage. -->
         <attr name="persistent" />
         <!-- The default value for the preference, which will be set either if persistence
              is off or persistence is on and the preference is not found in the persistent
@@ -7222,6 +7195,9 @@
         <!-- Whether the view of this Preference should be disabled when
              this Preference is disabled. -->
         <attr name="shouldDisableView" format="boolean" />
+        <!-- Whether the preference has enabled to have its view recycled when used in the list
+             view. This is true by default. -->
+        <attr name="recycleEnabled" format="boolean" />
     </declare-styleable>
 
     <!-- Base attributes available to CheckBoxPreference. -->
@@ -8404,7 +8380,9 @@
         <!-- Component name of an activity that allows the user to set up this service. -->
         <attr name="setupActivity" format="string" />
         <!-- Component name of an activity that allows the user to modify the settings for this
-             service. -->
+             service.
+             {@deprecated This value is deprecated and not used by the framework starting from API
+                         level 26. Use setupActivity instead.} -->
         <attr name="settingsActivity" />
         <!-- Attribute whether the TV input service can record programs. This value can be changed
              at runtime by calling
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index bfe666e..67050f7 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2401,6 +2401,9 @@
         <!-- Load order of overlay package. -->
         <attr name="priority" />
 
+        <!-- Whether the given RRO is static or not. -->
+        <attr name="isStatic" format="boolean" />
+
     </declare-styleable>
 
     <!-- Declaration of an {@link android.content.Intent} object in XML.  May
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 6015ed5..f9fd57c 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -194,4 +194,8 @@
 
     <color name="tooltip_background_dark">#e6616161</color>
     <color name="tooltip_background_light">#e6FFFFFF</color>
+
+    <!-- FloatingToolbar -->
+    <color name="floating_popup_divider_dark">#2F2F2F</color>
+    <color name="floating_popup_divider_light">#E9E9E9</color>
 </resources>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index f8a071d..cd3624d 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -138,4 +138,8 @@
 
   <!-- Accessibility action identifier for {@link android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction#ACTION_MOVE_WINDOW}. -->
   <item type="id" name="accessibilityActionMoveWindow" />
+
+  <!-- Action used to manually trigger an autofill request -->
+  <item type="id" name="autofill" />
+
 </resources>
diff --git a/core/res/res/values/locale_config.xml b/core/res/res/values/locale_config.xml
index 483d05b..ba14843 100644
--- a/core/res/res/values/locale_config.xml
+++ b/core/res/res/values/locale_config.xml
@@ -75,7 +75,7 @@
         <item>ce-RU</item> <!-- Chechen (Russia) -->
         <item>cgg-UG</item> <!-- Chiga (Uganda) -->
         <item>chr-US</item> <!-- Cherokee (United States) -->
-        <item>cs-CZ</item> <!-- Czech (Czech Republic) -->
+        <item>cs-CZ</item> <!-- Czech (Czechia) -->
         <item>cy-GB</item> <!-- Welsh (United Kingdom) -->
         <item>da-DK</item> <!-- Danish (Denmark) -->
         <item>da-GL</item> <!-- Danish (Greenland) -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 359fbcb..2897c62 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2805,6 +2805,8 @@
         <public name="autofillHint" />
         <public name="fontProviderPackage" />
         <public name="importantForAutofill" />
+        <public name="recycleEnabled"/>
+        <public name="isStatic" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e0">
@@ -2813,6 +2815,7 @@
     <public-group type="id" first-id="0x01020041">
         <public name="textAssist" />
         <public name="accessibilityActionMoveWindow" />
+        <public name="autofill" />
     </public-group>
 
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d1d406d..1ed069b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2640,6 +2640,9 @@
     <!-- Item on EditText context menu. This action is used to redo a text edit operation. -->
     <string name="redo">Redo</string>
 
+    <!-- Item on EditText context menu. This action is used to request autofill. -->
+    <string name="autofill">Autofill</string>
+
     <!-- Text selection contextual mode title, displayed in the CAB. [CHAR LIMIT=20] -->
     <string name="textSelectionCABTitle">Text selection</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0c318cf..07cecbc 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2425,6 +2425,7 @@
   <java-symbol type="drawable" name="ft_avd_toarrow" />
   <java-symbol type="drawable" name="ft_avd_toarrow_animation" />
   <java-symbol type="drawable" name="ft_avd_tooverflow_animation" />
+  <java-symbol type="attr" name="floatingToolbarDividerColor" />
 
   <java-symbol type="string" name="date_picker_prev_month_button" />
   <java-symbol type="string" name="date_picker_next_month_button" />
@@ -2840,11 +2841,13 @@
   <java-symbol type="dimen" name="autofill_fill_min_margin" />
   <java-symbol type="layout" name="autofill_save"/>
   <java-symbol type="layout" name="autofill_dataset_picker"/>
+  <java-symbol type="id" name="autofill" />
   <java-symbol type="id" name="autofill_save_title" />
   <java-symbol type="id" name="autofill_save_subtitle" />
   <java-symbol type="id" name="autofill_save_no" />
   <java-symbol type="id" name="autofill_save_yes" />
   <java-symbol type="id" name="autofill_save_close" />
+  <java-symbol type="string" name="autofill" />
   <java-symbol type="string" name="autofill_save_title" />
   <java-symbol type="string" name="autofill_save_title_with_type" />
   <java-symbol type="string" name="autofill_save_yes" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index d100c63..a661b07 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -398,6 +398,7 @@
         <item name="floatingToolbarItemBackgroundDrawable">@drawable/item_background_material_dark</item>
         <item name="floatingToolbarOpenDrawable">@drawable/ic_menu_moreoverflow_material_dark</item>
         <item name="floatingToolbarPopupBackgroundDrawable">@drawable/floating_popup_background_dark</item>
+        <item name="floatingToolbarDividerColor">@color/floating_popup_divider_dark</item>
 
         <!-- SearchView attributes -->
         <item name="searchViewStyle">@style/Widget.Holo.SearchView</item>
@@ -559,6 +560,7 @@
         <item name="floatingToolbarItemBackgroundDrawable">@drawable/item_background_material_light</item>
         <item name="floatingToolbarOpenDrawable">@drawable/ic_menu_moreoverflow_material_light</item>
         <item name="floatingToolbarPopupBackgroundDrawable">@drawable/floating_popup_background_light</item>
+        <item name="floatingToolbarDividerColor">@color/floating_popup_divider_light</item>
 
         <!-- Tooltip popup colors -->
         <item name="tooltipForegroundColor">@color/bright_foreground_dark</item>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 29c6b79..1ae922a 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -79,7 +79,7 @@
     <!-- Cyprus: 4-6 digits (not confirmed), known premium codes listed, plus EU -->
     <shortcode country="cy" pattern="\\d{4,6}" premium="7510" free="116\\d{3}" />
 
-    <!-- Czech Republic: 7-8 digits, starting with 9, plus EU:
+    <!-- Czechia: 7-8 digits, starting with 9, plus EU:
          http://www.o2.cz/osobni/en/services-by-alphabet/91670-premium_sms.html -->
     <shortcode country="cz" premium="9\\d{6,7}" free="116\\d{3}" />
 
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index fba8e23..dbc9e5d 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -40,6 +40,7 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt telephony-common org.apache.http.legacy
 LOCAL_PACKAGE_NAME := FrameworksCoreTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
 
 LOCAL_CERTIFICATE := platform
 
diff --git a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
index 23d3aa5..82f4690 100644
--- a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
+++ b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
@@ -20,6 +20,11 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
+import static android.content.res.FontResourcesParser.FamilyResourceEntry;
+import static android.content.res.FontResourcesParser.ProviderResourceEntry;
+import static android.content.res.FontResourcesParser.FontFileResourceEntry;
+import static android.content.res.FontResourcesParser.FontFamilyFilesResourceEntry;
+
 import android.app.Instrumentation;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
@@ -56,44 +61,40 @@
     public void testParse() throws XmlPullParserException, IOException {
         XmlResourceParser parser = mResources.getXml(R.font.samplexmlfont);
 
-        FontConfig result = FontResourcesParser.parse(parser, mResources);
+        FamilyResourceEntry result = FontResourcesParser.parse(parser, mResources);
 
         assertNotNull(result);
-        List<FontConfig.Family> families = result.getFamilies();
-        assertEquals(1, families.size());
-        List<FontConfig.Font> fonts = families.get(0).getFonts();
-        assertEquals(4, fonts.size());
-        FontConfig.Font font1 = fonts.get(0);
+        FontFamilyFilesResourceEntry filesEntry = (FontFamilyFilesResourceEntry) result;
+        FontFileResourceEntry[] fileEntries = filesEntry.getEntries();
+        assertEquals(4, fileEntries.length);
+        FontFileResourceEntry font1 = fileEntries[0];
         assertEquals(400, font1.getWeight());
         assertEquals(false, font1.isItalic());
-        assertEquals("res/font/samplefont.ttf", font1.getFontName());
-        FontConfig.Font font2 = fonts.get(1);
+        assertEquals("res/font/samplefont.ttf", font1.getFileName());
+        FontFileResourceEntry font2 = fileEntries[1];
         assertEquals(400, font2.getWeight());
         assertEquals(true, font2.isItalic());
-        assertEquals("res/font/samplefont2.ttf", font2.getFontName());
-        FontConfig.Font font3 = fonts.get(2);
+        assertEquals("res/font/samplefont2.ttf", font2.getFileName());
+        FontFileResourceEntry font3 = fileEntries[2];
         assertEquals(800, font3.getWeight());
         assertEquals(false, font3.isItalic());
-        assertEquals("res/font/samplefont3.ttf", font3.getFontName());
-        FontConfig.Font font4 = fonts.get(3);
+        assertEquals("res/font/samplefont3.ttf", font3.getFileName());
+        FontFileResourceEntry font4 = fileEntries[3];
         assertEquals(800, font4.getWeight());
         assertEquals(true, font4.isItalic());
-        assertEquals("res/font/samplefont4.ttf", font4.getFontName());
+        assertEquals("res/font/samplefont4.ttf", font4.getFileName());
     }
 
     @Test
     public void testParseDownloadableFont() throws IOException, XmlPullParserException {
         XmlResourceParser parser = mResources.getXml(R.font.samplexmldownloadedfont);
 
-        FontConfig result = FontResourcesParser.parse(parser, mResources);
+        FamilyResourceEntry result = FontResourcesParser.parse(parser, mResources);
 
         assertNotNull(result);
-        List<FontConfig.Family> families = result.getFamilies();
-        assertEquals(1, families.size());
-        FontConfig.Family family = families.get(0);
-        assertEquals("com.example.test.fontprovider.authority", family.getProviderAuthority());
-        assertEquals("com.example.test.fontprovider.package", family.getProviderPackage());
-        assertEquals("MyRequestedFont", family.getQuery());
-        assertNull(family.getFonts());
+        ProviderResourceEntry providerEntry = (ProviderResourceEntry) result;
+        assertEquals("com.example.test.fontprovider.authority", providerEntry.getAuthority());
+        assertEquals("com.example.test.fontprovider.package", providerEntry.getPackage());
+        assertEquals("MyRequestedFont", providerEntry.getQuery());
     }
 }
diff --git a/core/tests/coretests/src/android/database/PageViewCursorTest.java b/core/tests/coretests/src/android/database/PageViewCursorTest.java
new file mode 100644
index 0000000..0be89d5
--- /dev/null
+++ b/core/tests/coretests/src/android/database/PageViewCursorTest.java
@@ -0,0 +1,318 @@
+/*
+ * 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.database;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.os.Bundle;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+import android.util.MathUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+
+@RunWith(AndroidJUnit4.class)
+public class PageViewCursorTest {
+
+    private static final int ITEM_COUNT = 20;
+
+    private static final String NAME_COLUMN = "name";
+    private static final String NUM_COLUMN = "num";
+
+    private static final String[] COLUMNS = new String[]{
+      NAME_COLUMN,
+      NUM_COLUMN
+    };
+
+    private static final String[] NAMES = new String[] {
+            "000",
+            "111",
+            "222",
+            "333",
+            "444",
+            "555",
+            "666",
+            "777",
+            "888",
+            "999",
+            "aaa",
+            "bbb",
+            "ccc",
+            "ddd",
+            "eee",
+            "fff",
+            "ggg",
+            "hhh",
+            "iii",
+            "jjj"
+    };
+
+    private MatrixCursor mDelegate;
+    private PageViewCursor mCursor;
+
+    @Before
+    public void setUp() {
+        Random rand = new Random();
+
+        mDelegate = new MatrixCursor(COLUMNS);
+        for (int i = 0; i < ITEM_COUNT; i++) {
+            MatrixCursor.RowBuilder row = mDelegate.newRow();
+            row.add(NAME_COLUMN, NAMES[i]);
+            row.add(NUM_COLUMN, rand.nextInt());
+        }
+
+        mCursor = new PageViewCursor(mDelegate, 10, 5);
+    }
+
+    @Test
+    public void testPage_Size() {
+        assertEquals(5, mCursor.getCount());
+    }
+
+    @Test
+    public void testPage_TotalSize() {
+        assertEquals(ITEM_COUNT, mCursor.getExtras().getInt(ContentResolver.EXTRA_TOTAL_SIZE));
+    }
+
+    @Test
+    public void testPage_OffsetExceedsCursorCount_EffectivelyEmptyCursor() {
+        mCursor = new PageViewCursor(mDelegate, ITEM_COUNT * 2, 5);
+        assertEquals(0, mCursor.getCount());
+    }
+
+    @Test
+    public void testMoveToPosition() {
+        assertTrue(mCursor.moveToPosition(0));
+        assertEquals(NAMES[10], mCursor.getString(0));
+        assertTrue(mCursor.moveToPosition(1));
+        assertEquals(NAMES[11], mCursor.getString(0));
+        assertTrue(mCursor.moveToPosition(4));
+        assertEquals(NAMES[14], mCursor.getString(0));
+
+        // and then back down again for good measure.
+        assertTrue(mCursor.moveToPosition(1));
+        assertEquals(NAMES[11], mCursor.getString(0));
+        assertTrue(mCursor.moveToPosition(0));
+        assertEquals(NAMES[10], mCursor.getString(0));
+    }
+
+    @Test
+    public void testMoveToPosition_MoveToSamePosition_NoOp() {
+        assertTrue(mCursor.moveToPosition(1));
+        assertEquals(NAMES[11], mCursor.getString(0));
+        assertTrue(mCursor.moveToPosition(1));
+        assertEquals(NAMES[11], mCursor.getString(0));
+    }
+
+    @Test
+    public void testMoveToPosition_PositionOutOfBounds_MovesToBeforeFirst() {
+        assertTrue(mCursor.moveToPosition(0));
+        assertEquals(NAMES[10], mCursor.getString(0));
+
+        // move before
+        assertFalse(mCursor.moveToPosition(-12));
+        assertTrue(mCursor.isBeforeFirst());
+    }
+
+    @Test
+    public void testMoveToPosition_PositionOutOfBounds_MovesToAfterLast() {
+        assertTrue(mCursor.moveToPosition(0));
+        assertEquals(NAMES[10], mCursor.getString(0));
+
+        assertFalse(mCursor.moveToPosition(222));
+        assertTrue(mCursor.isAfterLast());
+    }
+
+    @Test
+    public void testPosition() {
+        assertEquals(-1, mCursor.getPosition());
+    }
+
+    @Test
+    public void testIsBeforeFirst() {
+        assertTrue(mCursor.isBeforeFirst());
+        mCursor.moveToFirst();
+        assertFalse(mCursor.isBeforeFirst());
+    }
+
+    @Test
+    public void testCount_ZeroForEmptyCursor() {
+        mCursor = new PageViewCursor(mDelegate, 0, 0);
+        assertEquals(0, mCursor.getCount());
+    }
+
+    @Test
+    public void testIsBeforeFirst_TrueForEmptyCursor() {
+        mCursor = new PageViewCursor(mDelegate, 0, 0);
+        assertTrue(mCursor.isBeforeFirst());
+    }
+
+    @Test
+    public void testIsAfterLast() {
+        assertFalse(mCursor.isAfterLast());
+        mCursor.moveToLast();
+        mCursor.moveToNext();
+        assertTrue(mCursor.isAfterLast());
+    }
+
+    @Test
+    public void testIsAfterLast_TrueForEmptyCursor() {
+        mCursor = new PageViewCursor(mDelegate, 0, 0);
+        assertTrue(mCursor.isAfterLast());
+    }
+
+    @Test
+    public void testIsFirst() {
+        assertFalse(mCursor.isFirst());
+        mCursor.moveToFirst();
+        assertTrue(mCursor.isFirst());
+    }
+
+    @Test
+    public void testIsLast() {
+        assertFalse(mCursor.isLast());
+        mCursor.moveToLast();
+        assertTrue(mCursor.isLast());
+    }
+
+    @Test
+    public void testMove() {
+        // note that initial position is -1, so moving
+        // 2 will only put as at 1.
+        mCursor.move(2);
+        assertEquals(NAMES[11], mCursor.getString(0));
+        mCursor.move(-1);
+        assertEquals(NAMES[10], mCursor.getString(0));
+    }
+
+    @Test
+    public void testMoveToFist() {
+        mCursor.moveToPosition(3);
+        mCursor.moveToFirst();
+        assertEquals(NAMES[10], mCursor.getString(0));
+    }
+
+    @Test
+    public void testMoveToLast() {
+        mCursor.moveToLast();
+        assertEquals(NAMES[14], mCursor.getString(0));
+    }
+
+    @Test
+    public void testMoveToNext() {
+        // default position is -1, so next is 0.
+        mCursor.moveToNext();
+        assertEquals(NAMES[10], mCursor.getString(0));
+    }
+
+    @Test
+    public void testMoveToNext_AfterLastReturnsFalse() {
+        mCursor.moveToLast();
+        assertFalse(mCursor.moveToNext());
+    }
+
+    @Test
+    public void testMoveToPrevious() {
+        mCursor.moveToPosition(3);
+        mCursor.moveToPrevious();
+        assertEquals(NAMES[12], mCursor.getString(0));
+    }
+
+    @Test
+    public void testMoveToPrevious_BeforeFirstReturnsFalse() {
+        assertFalse(mCursor.moveToPrevious());
+    }
+
+    @Test
+    public void testWindow_ReadPastEnd() {
+        assertFalse(mCursor.moveToPosition(10));
+    }
+
+    @Test
+    public void testOffset_LimitOutOfBounds() {
+        mCursor = new PageViewCursor(mDelegate, 5, 100);
+        assertEquals(15, mCursor.getCount());
+    }
+
+    @Test
+    public void testPagingMarker() {
+        mCursor = new PageViewCursor(mDelegate, 5, 100);
+        assertTrue(mCursor.getExtras().getBoolean(PageViewCursor.EXTRA_AUTO_PAGED));
+    }
+
+    @Test
+    public void testWrap() {
+        Bundle queryArgs = new Bundle();
+        queryArgs.putInt(ContentResolver.QUERY_ARG_OFFSET, 5);
+        queryArgs.putInt(ContentResolver.QUERY_ARG_LIMIT, 5);
+        Cursor wrapped = PageViewCursor.wrap(mDelegate, queryArgs);
+        assertTrue(wrapped instanceof PageViewCursor);
+        assertEquals(5, wrapped.getCount());
+    }
+
+    @Test
+    public void testWrap_NoOpWithoutPagingArgs() {
+        Cursor wrapped = PageViewCursor.wrap(mDelegate, Bundle.EMPTY);
+        assertTrue(mDelegate == wrapped);
+    }
+
+    @Test
+    public void testWrap_NoOpCursorsWithExistingPaging_ByTotalSize() {
+        Bundle extras = new Bundle();
+        extras.putInt(ContentResolver.EXTRA_TOTAL_SIZE, 5);
+        mDelegate.setExtras(extras);
+
+        Bundle queryArgs = new Bundle();
+        queryArgs.putInt(ContentResolver.QUERY_ARG_OFFSET, 5);
+        queryArgs.putInt(ContentResolver.QUERY_ARG_LIMIT, 5);
+        Cursor wrapped = PageViewCursor.wrap(mDelegate, queryArgs);
+        assertTrue(mDelegate == wrapped);
+    }
+
+    @Test
+    public void testWrap_NoOpCursorsWithExistingPaging_ByHonoredArgs() {
+        Bundle extras = new Bundle();
+        extras.putStringArray(
+                ContentResolver.EXTRA_HONORED_ARGS,
+                new String[] {
+                    ContentResolver.QUERY_ARG_OFFSET,
+                    ContentResolver.QUERY_ARG_LIMIT
+                });
+        mDelegate.setExtras(extras);
+
+        Bundle queryArgs = new Bundle();
+        queryArgs.putInt(ContentResolver.QUERY_ARG_OFFSET, 5);
+        queryArgs.putInt(ContentResolver.QUERY_ARG_LIMIT, 5);
+        Cursor wrapped = PageViewCursor.wrap(mDelegate, queryArgs);
+        assertTrue(mDelegate == wrapped);
+    }
+
+    private void assertStringAt(int row, int column, String expected) {
+        mCursor.moveToPosition(row);
+        assertEquals(expected, mCursor.getString(column));
+    }
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 903ef84..0cfdaf5 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -198,7 +198,7 @@
                     Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
                     Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
                     Settings.Global.HDMI_CONTROL_ENABLED,
-                    Settings.Global.HDMI_SYSTEM_AUDIO_ENABLED,
+                    Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
                     Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
                     Settings.Global.HTTP_PROXY,
                     Settings.Global.INET_CONDITION_DEBOUNCE_DOWN_DELAY,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
index e152163..4ec78ff 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
@@ -65,4 +65,49 @@
 
         assertEquals(1, sensorBgCounter.getCountLocked(BatteryStats.STATS_SINCE_CHARGED));
     }
+
+    @SmallTest
+    public void testNestedSensorReset() throws Exception {
+        final MockClocks clocks = new MockClocks();
+        MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+        bi.mForceOnBattery = true;
+        clocks.realtime = 100;
+        clocks.uptime = 100;
+        bi.getOnBatteryTimeBase().setRunning(true, 100, 100);
+        bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_RECEIVER);
+
+        clocks.realtime += 100;
+        clocks.uptime += 100;
+
+        bi.noteStartSensorLocked(UID, SENSOR_ID);
+
+        clocks.realtime += 100;
+        clocks.uptime += 100;
+
+        // The sensor is started and the background counter has been created.
+        final BatteryStats.Uid uid = bi.getUidStats().get(UID);
+        assertNotNull(uid);
+
+        BatteryStats.Uid.Sensor sensor = uid.getSensorStats().get(SENSOR_ID);
+        assertNotNull(sensor);
+        assertNotNull(sensor.getSensorTime());
+        assertNotNull(sensor.getSensorBgCount());
+
+        // Reset the stats. Since the sensor is still running, we should still see the sensor
+        // timer. Background counter should be gone though.
+        bi.getUidStatsLocked(UID).reset();
+
+        sensor = uid.getSensorStats().get(SENSOR_ID);
+        assertNotNull(sensor);
+        assertNotNull(sensor.getSensorTime());
+        assertNull(sensor.getSensorBgCount());
+
+        bi.noteStopSensorLocked(UID, SENSOR_ID);
+
+        // Now the sensor timer has stopped so this reset should also take out the sensor.
+        bi.getUidStatsLocked(UID).reset();
+
+        sensor = uid.getSensorStats().get(SENSOR_ID);
+        assertNull(sensor);
+    }
 }
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 76598a0..38a1a46 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -248,6 +248,9 @@
         <font weight="400" style="normal">NotoSansCham-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansCham-Bold.ttf</font>
     </family>
+    <family lang="und-Avst">
+        <font weight="400" style="normal">NotoSansAvestan-Regular.ttf</font>
+    </family>
     <family lang="und-Bali">
         <font weight="400" style="normal">NotoSansBalinese-Regular.ttf</font>
     </family>
@@ -257,6 +260,9 @@
     <family lang="und-Batk">
         <font weight="400" style="normal">NotoSansBatak-Regular.ttf</font>
     </family>
+    <family lang="und-Brah">
+        <font weight="400" style="normal">NotoSansBrahmi-Regular.ttf</font>
+    </family>
     <family lang="und-Bugi">
         <font weight="400" style="normal">NotoSansBuginese-Regular.ttf</font>
     </family>
@@ -266,33 +272,75 @@
     <family lang="und-Cans">
         <font weight="400" style="normal">NotoSansCanadianAboriginal-Regular.ttf</font>
     </family>
+    <family lang="und-Cari">
+        <font weight="400" style="normal">NotoSansCarian-Regular.ttf</font>
+    </family>
     <family lang="und-Cher">
         <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
     </family>
     <family lang="und-Copt">
         <font weight="400" style="normal">NotoSansCoptic-Regular.ttf</font>
     </family>
+    <family lang="und-Xsux">
+        <font weight="400" style="normal">NotoSansCuneiform-Regular.ttf</font>
+    </family>
+    <family lang="und-Cprt">
+        <font weight="400" style="normal">NotoSansCypriot-Regular.ttf</font>
+    </family>
+    <family lang="und-Dsrt">
+        <font weight="400" style="normal">NotoSansDeseret-Regular.ttf</font>
+    </family>
+    <family lang="und-Egyp">
+        <font weight="400" style="normal">NotoSansEgyptianHieroglyphs-Regular.ttf</font>
+    </family>
     <family lang="und-Glag">
         <font weight="400" style="normal">NotoSansGlagolitic-Regular.ttf</font>
     </family>
+    <family lang="und-Goth">
+        <font weight="400" style="normal">NotoSansGothic-Regular.ttf</font>
+    </family>
     <family lang="und-Hano">
         <font weight="400" style="normal">NotoSansHanunoo-Regular.ttf</font>
     </family>
+    <family lang="und-Armi">
+        <font weight="400" style="normal">NotoSansImperialAramaic-Regular.ttf</font>
+    </family>
+    <family lang="und-Phli">
+        <font weight="400" style="normal">NotoSansInscriptionalPahlavi-Regular.ttf</font>
+    </family>
+    <family lang="und-Prti">
+        <font weight="400" style="normal">NotoSansInscriptionalParthian-Regular.ttf</font>
+    </family>
     <family lang="und-Java">
         <font weight="400" style="normal">NotoSansJavanese-Regular.ttf</font>
     </family>
+    <family lang="und-Kthi">
+        <font weight="400" style="normal">NotoSansKaithi-Regular.ttf</font>
+    </family>
     <family lang="und-Kali">
         <font weight="400" style="normal">NotoSansKayahLi-Regular.ttf</font>
     </family>
+    <family lang="und-Khar">
+        <font weight="400" style="normal">NotoSansKharoshthi-Regular.ttf</font>
+    </family>
     <family lang="und-Lepc">
         <font weight="400" style="normal">NotoSansLepcha-Regular.ttf</font>
     </family>
     <family lang="und-Limb">
         <font weight="400" style="normal">NotoSansLimbu-Regular.ttf</font>
     </family>
+    <family lang="und-Linb">
+        <font weight="400" style="normal">NotoSansLinearB-Regular.ttf</font>
+    </family>
     <family lang="und-Lisu">
         <font weight="400" style="normal">NotoSansLisu-Regular.ttf</font>
     </family>
+    <family lang="und-Lyci">
+        <font weight="400" style="normal">NotoSansLycian-Regular.ttf</font>
+    </family>
+    <family lang="und-Lydi">
+        <font weight="400" style="normal">NotoSansLydian-Regular.ttf</font>
+    </family>
     <family lang="und-Mand">
         <font weight="400" style="normal">NotoSansMandaic-Regular.ttf</font>
     </family>
@@ -305,12 +353,33 @@
     <family lang="und-Nkoo">
         <font weight="400" style="normal">NotoSansNKo-Regular.ttf</font>
     </family>
+    <family lang="und-Ogam">
+        <font weight="400" style="normal">NotoSansOgham-Regular.ttf</font>
+    </family>
     <family lang="und-Olck">
         <font weight="400" style="normal">NotoSansOlChiki-Regular.ttf</font>
     </family>
+    <family lang="und-Ital">
+        <font weight="400" style="normal">NotoSansOldItalic-Regular.ttf</font>
+    </family>
+    <family lang="und-Xpeo">
+        <font weight="400" style="normal">NotoSansOldPersian-Regular.ttf</font>
+    </family>
+    <family lang="und-Sarb">
+        <font weight="400" style="normal">NotoSansOldSouthArabian-Regular.ttf</font>
+    </family>
+    <family lang="und-Orkh">
+        <font weight="400" style="normal">NotoSansOldTurkic-Regular.ttf</font>
+    </family>
+    <family lang="und-Osma">
+        <font weight="400" style="normal">NotoSansOsmanya-Regular.ttf</font>
+    </family>
     <family lang="und-Phag">
         <font weight="400" style="normal">NotoSansPhagsPa-Regular.ttf</font>
     </family>
+    <family lang="und-Phnx">
+        <font weight="400" style="normal">NotoSansPhoenician-Regular.ttf</font>
+    </family>
     <family lang="und-Rjng">
         <font weight="400" style="normal">NotoSansRejang-Regular.ttf</font>
     </family>
@@ -323,6 +392,9 @@
     <family lang="und-Saur">
         <font weight="400" style="normal">NotoSansSaurashtra-Regular.ttf</font>
     </family>
+    <family lang="und-Shaw">
+        <font weight="400" style="normal">NotoSansShavian-Regular.ttf</font>
+    </family>
     <family lang="und-Sund">
         <font weight="400" style="normal">NotoSansSundanese-Regular.ttf</font>
     </family>
@@ -358,6 +430,9 @@
     <family lang="und-Tfng">
         <font weight="400" style="normal">NotoSansTifinagh-Regular.ttf</font>
     </family>
+    <family lang="und-Ugar">
+        <font weight="400" style="normal">NotoSansUgaritic-Regular.ttf</font>
+    </family>
     <family lang="und-Vaii">
         <font weight="400" style="normal">NotoSansVai-Regular.ttf</font>
     </family>
diff --git a/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index 704f0ce..5973d3e 100644
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -275,6 +275,10 @@
      * the {@link DrmManagerClient} object is no longer needed in your
      * application. After this method is called, {@link DrmManagerClient} is no
      * longer usable since it has lost all of its required resource.
+     *
+     * This method was added in API 24. In API versions 16 through 23, release()
+     * should be called instead. There is no need to do anything for API
+     * versions prior to 16.
      */
     @Override
     public void close() {
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 3d5ba79..ed587bb 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -791,12 +791,12 @@
 
         int neww = width;
         int newh = height;
-        Canvas canvas = new Canvas();
         Bitmap bitmap;
         Paint paint;
 
         Rect srcR = new Rect(x, y, x + width, y + height);
         RectF dstR = new RectF(0, 0, width, height);
+        RectF deviceR = new RectF();
 
         Config newConfig = Config.ARGB_8888;
         final Config config = source.getConfig();
@@ -827,7 +827,6 @@
         } else {
             final boolean transformed = !m.rectStaysRect();
 
-            RectF deviceR = new RectF();
             m.mapRect(deviceR, dstR);
 
             neww = Math.round(deviceR.width());
@@ -841,9 +840,6 @@
             }
             bitmap = createBitmap(neww, newh, transformedConfig, transformed || source.hasAlpha());
 
-            canvas.translate(-deviceR.left, -deviceR.top);
-            canvas.concat(m);
-
             paint = new Paint();
             paint.setFilterBitmap(filter);
             if (transformed) {
@@ -857,7 +853,9 @@
         bitmap.setHasAlpha(source.hasAlpha());
         bitmap.setPremultiplied(source.mRequestPremultiplied);
 
-        canvas.setBitmap(bitmap);
+        Canvas canvas = new Canvas(bitmap);
+        canvas.translate(-deviceR.left, -deviceR.top);
+        canvas.concat(m);
         canvas.drawBitmap(source, srcR, dstR, paint);
         canvas.setBitmap(null);
         if (isHardware) {
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 2a2e14b..7289429 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -157,12 +157,10 @@
 
     /**
      * Specify a bitmap for the canvas to draw into. All canvas state such as
-     * layers, filters, and the save/restore stack are reset. Additionally,
+     * layers, filters, and the save/restore stack are reset with the exception
+     * of the current matrix and clip stack. Additionally, as a side-effect
      * the canvas' target density is updated to match that of the bitmap.
      *
-     * Prior to API level {@value Build.VERSION_CODES#O} the current matrix and
-     * clip stack were preserved.
-     *
      * @param bitmap Specifies a mutable bitmap for the canvas to draw into.
      * @see #setDensity(int)
      * @see #getDensity()
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 908ec50..929ac22 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -490,16 +490,16 @@
          *     <tr>
          *         <td>Opto-electronic transfer function (OETF)</td>
          *         <td colspan="4">\(\begin{equation}
-         *             C_{sRGB} = \begin{cases} 12.92 \times C_{linear} & C_{linear} \lt 0.0031308 \\
-         *             1.055 \times C_{linear}^{\frac{1}{2.4}} - 0.055 & C_{linear} \ge 0.0031308 \end{cases}
+         *             C_{DisplayP3} = \begin{cases} 12.92 \times C_{linear} & C_{linear} \lt 0.0030186 \\
+         *             1.055 \times C_{linear}^{\frac{1}{2.4}} - 0.055 & C_{linear} \ge 0.0030186 \end{cases}
          *             \end{equation}\)
          *         </td>
          *     </tr>
          *     <tr>
          *         <td>Electro-optical transfer function (EOTF)</td>
          *         <td colspan="4">\(\begin{equation}
-         *             C_{linear} = \begin{cases}\frac{C_{sRGB}}{12.92} & C_{sRGB} \lt 0.04045 \\
-         *             \left( \frac{C_{sRGB} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \ge 0.04045 \end{cases}
+         *             C_{linear} = \begin{cases}\frac{C_{DisplayP3}}{12.92} & C_{sRGB} \lt 0.039 \\
+         *             \left( \frac{C_{DisplayP3} + 0.055}{1.055} \right) ^{2.4} & C_{sRGB} \ge 0.039 \end{cases}
          *             \end{equation}\)
          *         </td>
          *     </tr>
@@ -1482,7 +1482,7 @@
                 "Display P3",
                 new float[] { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f },
                 ILLUMINANT_D65,
-                new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4),
+                new Rgb.TransferParameters(1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.039, 2.4),
                 Named.DISPLAY_P3.ordinal()
         );
         sNamedColorSpaces[Named.NTSC_1953.ordinal()] = new ColorSpace.Rgb(
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index 317f232..16fc2b1 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -25,7 +25,6 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
-import java.util.List;
 
 /**
  * A family of typefaces with different styles.
@@ -48,14 +47,8 @@
         mBuilderPtr = nInitBuilder(null, 0);
     }
 
-    public FontFamily(String lang, String variant) {
-        int varEnum = 0;
-        if ("compact".equals(variant)) {
-            varEnum = 1;
-        } else if ("elegant".equals(variant)) {
-            varEnum = 2;
-        }
-        mBuilderPtr = nInitBuilder(lang, varEnum);
+    public FontFamily(String lang, int variant) {
+        mBuilderPtr = nInitBuilder(lang, variant);
     }
 
     public void freeze() {
@@ -103,12 +96,15 @@
         }
     }
 
-    public boolean addFontWeightStyle(ByteBuffer font, int ttcIndex, List<FontConfig.Axis> axes,
+    public boolean addFontWeightStyle(ByteBuffer font, int ttcIndex, FontConfig.Axis[] axes,
             int weight, boolean style) {
         if (mBuilderPtr == 0) {
             throw new IllegalStateException("Unable to call addFontWeightStyle after freezing.");
         }
-        return nAddFontWeightStyle(mBuilderPtr, font, ttcIndex, axes, weight, style);
+        for (FontConfig.Axis axis : axes) {
+            nAddAxisValue(mBuilderPtr, axis.getTag(), axis.getStyleValue());
+        }
+        return nAddFontWeightStyle(mBuilderPtr, font, ttcIndex, weight, style);
     }
 
     /**
@@ -143,8 +139,11 @@
     private static native void nUnrefFamily(long nativePtr);
     private static native boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex);
     private static native boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font,
-            int ttcIndex, List<FontConfig.Axis> listOfAxis,
-            int weight, boolean isItalic);
+            int ttcIndex, int weight, boolean isItalic);
     private static native boolean nAddFontFromAssetManager(long builderPtr, AssetManager mgr,
             String path, int cookie, boolean isAsset, int weight, boolean isItalic);
+
+    // The added axis values are only valid for the next nAddFont* method call.
+    @CriticalNative
+    private static native void nAddAxisValue(long builderPtr, int tag, float value);
 }
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index b757842..1b6969f 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -116,20 +116,23 @@
 
     private static FontConfig readFamilies(XmlPullParser parser)
             throws XmlPullParserException, IOException {
-        FontConfig config = new FontConfig();
+        List<FontConfig.Family> families = new ArrayList<>();
+        List<FontConfig.Alias> aliases = new ArrayList<>();
+
         parser.require(XmlPullParser.START_TAG, null, "familyset");
         while (parser.next() != XmlPullParser.END_TAG) {
             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
             String tag = parser.getName();
             if (tag.equals("family")) {
-                config.getFamilies().add(readFamily(parser));
+                families.add(readFamily(parser));
             } else if (tag.equals("alias")) {
-                config.getAliases().add(readAlias(parser));
+                aliases.add(readAlias(parser));
             } else {
                 skip(parser);
             }
         }
-        return config;
+        return new FontConfig(families.toArray(new FontConfig.Family[families.size()]),
+                aliases.toArray(new FontConfig.Alias[aliases.size()]));
     }
 
     private static FontConfig.Family readFamily(XmlPullParser parser)
@@ -147,7 +150,16 @@
                 skip(parser);
             }
         }
-        return new FontConfig.Family(name, fonts, lang, variant);
+        int intVariant = FontConfig.Family.VARIANT_DEFAULT;
+        if (variant != null) {
+            if (variant.equals("compact")) {
+                intVariant = FontConfig.Family.VARIANT_COMPACT;
+            } else if (variant.equals("elegant")) {
+                intVariant = FontConfig.Family.VARIANT_ELEGANT;
+            }
+        }
+        return new FontConfig.Family(name, fonts.toArray(new FontConfig.Font[fonts.size()]), lang,
+                intVariant);
     }
 
     /** Matches leading and trailing XML whitespace. */
@@ -177,7 +189,8 @@
         }
         String fullFilename = "/system/fonts/" +
                 FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
-        return new FontConfig.Font(fullFilename, index, axes, weight, isItalic);
+        return new FontConfig.Font(fullFilename, index,
+                axes.toArray(new FontConfig.Axis[axes.size()]), weight, isItalic);
     }
 
     /** The 'tag' attribute value is read as four character values between U+0020 and U+007E
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index b1d51ec..7ca4615 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -2294,7 +2294,7 @@
             throw new IndexOutOfBoundsException();
         }
 
-        return nGetTextRunCursor(mNativePaint, text,
+        return nGetTextRunCursor(mNativePaint, mNativeTypeface, text,
                 contextStart, contextLength, dir, offset, cursorOpt);
     }
 
@@ -2380,7 +2380,7 @@
             throw new IndexOutOfBoundsException();
         }
 
-        return nGetTextRunCursor(mNativePaint, text,
+        return nGetTextRunCursor(mNativePaint, mNativeTypeface, text,
                 contextStart, contextEnd, dir, offset, cursorOpt);
     }
 
@@ -2686,9 +2686,9 @@
     private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
             String text, int start, int end, int contextStart, int contextEnd,
             int bidiFlags, float[] advances, int advancesIndex);
-    private native int nGetTextRunCursor(long paintPtr, char[] text,
+    private native int nGetTextRunCursor(long paintPtr, long typefacePtr, char[] text,
             int contextStart, int contextLength, int dir, int offset, int cursorOpt);
-    private native int nGetTextRunCursor(long paintPtr, String text,
+    private native int nGetTextRunCursor(long paintPtr, long typefacePtr, String text,
             int contextStart, int contextEnd, int dir, int offset, int cursorOpt);
     private static native void nGetTextPath(long paintPtr, long typefacePtr,
             int bidiFlags, char[] text, int index, int count, float x, float y, long path);
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 6de19cb..3416401 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -16,9 +16,15 @@
 
 package android.graphics;
 
+import static android.content.res.FontResourcesParser.ProviderResourceEntry;
+import static android.content.res.FontResourcesParser.FontFileResourceEntry;
+import static android.content.res.FontResourcesParser.FontFamilyFilesResourceEntry;
+import static android.content.res.FontResourcesParser.FamilyResourceEntry;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.Context;
 import android.content.res.AssetManager;
 import android.graphics.fonts.FontRequest;
 import android.graphics.fonts.FontResult;
@@ -83,7 +89,7 @@
     @GuardedBy("sLock")
     private static FontsContract sFontsContract;
     @GuardedBy("sLock")
-    private static Handler mHandler;
+    private static Handler sHandler;
 
     /**
      * Cache for Typeface objects dynamically loaded from assets. Currently max size is 16.
@@ -161,46 +167,35 @@
      * Used by Resources to load a font resource of type xml.
      */
     @Nullable
-    public static Typeface createFromResources(FontConfig config, AssetManager mgr, String path) {
+    public static Typeface createFromResources(
+            FamilyResourceEntry entry, AssetManager mgr, String path) {
         if (sFallbackFonts != null) {
             Typeface typeface = findFromCache(mgr, path);
             if (typeface != null) return typeface;
 
-            List<FontConfig.Family> families = config.getFamilies();
-            if (families == null || families.isEmpty()) {
-                throw new RuntimeException(
-                        "Font resource " + path + " contained no font families.");
-            }
-            if (families.size() > 1) {
-                throw new RuntimeException(
-                        "Font resource " + path + " contained more than one family.");
-            }
-            FontConfig.Family family = families.get(0);
-            if (family.getProviderAuthority() != null && family.getQuery() != null) {
+            if (entry instanceof ProviderResourceEntry) {
+                final ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry;
                 // Downloadable font
-                typeface = findFromCache(
-                        family.getProviderAuthority(), family.getQuery());
+                typeface = findFromCache(providerEntry.getAuthority(), providerEntry.getQuery());
                 if (typeface != null) {
                     return typeface;
                 }
                 // Downloaded font and it wasn't cached, request it again and return a
                 // default font instead (nothing we can do now).
-                create(new FontRequest(family.getProviderAuthority(), family.getProviderPackage(),
-                        family.getQuery()), NO_OP_REQUEST_CALLBACK);
+                create(new FontRequest(providerEntry.getAuthority(), providerEntry.getPackage(),
+                        providerEntry.getQuery()), NO_OP_REQUEST_CALLBACK);
                 return DEFAULT;
             }
 
+            // family is FontFamilyFilesResourceEntry
+            final FontFamilyFilesResourceEntry filesEntry =
+                    (FontFamilyFilesResourceEntry) entry;
+
             FontFamily fontFamily = new FontFamily();
-            List<FontConfig.Font> fonts = family.getFonts();
-            if (fonts == null || fonts.isEmpty()) {
-                throw new RuntimeException("Font resource " + path + " contained no fonts.");
-            }
-            for (int i = 0; i < fonts.size(); i++) {
-                FontConfig.Font font = fonts.get(i);
-                // TODO: Use style and weight info
-                if (!fontFamily.addFontFromAssetManager(mgr, font.getFontName(),
-                        0 /* resourceCookie */, false /* isAsset */, font.getWeight(),
-                        font.isItalic())) {
+            for (final FontFileResourceEntry fontFile : filesEntry.getEntries()) {
+                if (!fontFamily.addFontFromAssetManager(mgr, fontFile.getFileName(),
+                        0 /* resourceCookie */, false /* isAsset */, fontFile.getWeight(),
+                      fontFile.isItalic())) {
                     return null;
                 }
             }
@@ -232,6 +227,20 @@
     }
 
     /**
+     * Set the application context so we can generate font requests from the provider. This should
+     * be called from ActivityThread when the application binds, as we preload fonts.
+     * @hide
+     */
+    public static void setApplicationContext(Context context) {
+        synchronized (sLock) {
+            if (sFontsContract == null) {
+                sFontsContract = new FontsContract(context);
+                sHandler = new Handler();
+            }
+        }
+    }
+
+    /**
      * Create a typeface object given a font request. The font will be asynchronously fetched,
      * therefore the result is delivered to the given callback. See {@link FontRequest}.
      * Only one of the methods in callback will be invoked, depending on whether the request
@@ -247,18 +256,17 @@
         Typeface cachedTypeface = findFromCache(
                 request.getProviderAuthority(), request.getQuery());
         if (cachedTypeface != null) {
-            mHandler.post(() -> callback.onTypefaceRetrieved(cachedTypeface));
+            sHandler.post(() -> callback.onTypefaceRetrieved(cachedTypeface));
             return;
         }
         synchronized (sLock) {
             if (sFontsContract == null) {
-                sFontsContract = new FontsContract();
-                mHandler = new Handler();
+                throw new RuntimeException("Context not initialized, can't query provider");
             }
             final ResultReceiver receiver = new ResultReceiver(null) {
                 @Override
                 public void onReceiveResult(int resultCode, Bundle resultData) {
-                    mHandler.post(() -> receiveResult(request, callback, resultCode, resultData));
+                    sHandler.post(() -> receiveResult(request, callback, resultCode, resultData));
                 }
             };
             sFontsContract.getFont(request, receiver);
@@ -677,8 +685,8 @@
             List<FontFamily> familyList = new ArrayList<FontFamily>();
             // Note that the default typeface is always present in the fallback list;
             // this is an enhancement from pre-Minikin behavior.
-            for (int i = 0; i < fontConfig.getFamilies().size(); i++) {
-                FontConfig.Family f = fontConfig.getFamilies().get(i);
+            for (int i = 0; i < fontConfig.getFamilies().length; i++) {
+                FontConfig.Family f = fontConfig.getFamilies()[i];
                 if (i == 0 || f.getName() == null) {
                     familyList.add(makeFamilyFromParsed(f, bufferForPath));
                 }
@@ -687,9 +695,9 @@
             setDefault(Typeface.createFromFamilies(sFallbackFonts));
 
             Map<String, Typeface> systemFonts = new HashMap<String, Typeface>();
-            for (int i = 0; i < fontConfig.getFamilies().size(); i++) {
+            for (int i = 0; i < fontConfig.getFamilies().length; i++) {
                 Typeface typeface;
-                FontConfig.Family f = fontConfig.getFamilies().get(i);
+                FontConfig.Family f = fontConfig.getFamilies()[i];
                 if (f.getName() != null) {
                     if (i == 0) {
                         // The first entry is the default typeface; no sense in
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 40ab778..3f842e0 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -190,7 +190,7 @@
 // Dithering must be done in the quantization space
 // When we are writing to an sRGB framebuffer, we must do the following:
 //     EOTF(OETF(color) + dither)
-// The dithering pattern is generated with a triangle noise generator in the range [-0.0,1.0]
+// The dithering pattern is generated with a triangle noise generator in the range [-1.0,1.0]
 // TODO: Handle linear fp16 render targets
 const char* gFS_Gradient_Functions = R"__SHADER__(
         float triangleNoise(const highp vec2 n) {
@@ -203,7 +203,7 @@
 const char* gFS_Gradient_Preamble[2] = {
         // Linear framebuffer
         "\nvec4 dither(const vec4 color) {\n"
-        "    return vec4(color.rgb + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0), color.a);\n"
+        "    return color + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0);\n"
         "}\n"
         "\nvec4 gammaMix(const vec4 a, const vec4 b, float v) {\n"
         "    vec4 c = mix(a, b, v);\n"
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 363aa83..812e4d8 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -73,9 +73,39 @@
 // Canvas state operations: Replace Bitmap
 // ----------------------------------------------------------------------------
 
+class ClipCopier : public SkCanvas::ClipVisitor {
+public:
+    explicit ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {}
+
+    virtual void clipRect(const SkRect& rect, SkClipOp op, bool antialias) {
+        m_dstCanvas->clipRect(rect, op, antialias);
+    }
+    virtual void clipRRect(const SkRRect& rrect, SkClipOp op, bool antialias) {
+        m_dstCanvas->clipRRect(rrect, op, antialias);
+    }
+    virtual void clipPath(const SkPath& path, SkClipOp op, bool antialias) {
+        m_dstCanvas->clipPath(path, op, antialias);
+    }
+
+private:
+    SkCanvas* m_dstCanvas;
+};
+
 void SkiaCanvas::setBitmap(const SkBitmap& bitmap) {
-    mCanvasOwned.reset(new SkCanvas(bitmap));
-    mCanvas = mCanvasOwned.get();
+    SkCanvas* newCanvas = new SkCanvas(bitmap);
+
+    if (!bitmap.isNull()) {
+        // Copy the canvas matrix & clip state.
+        newCanvas->setMatrix(mCanvas->getTotalMatrix());
+
+        ClipCopier copier(newCanvas);
+        mCanvas->replayClips(&copier);
+    }
+
+    // deletes the previously owned canvas (if any)
+    mCanvasOwned = std::unique_ptr<SkCanvas>(newCanvas);
+    mCanvas = newCanvas;
+
     // clean up the old save stack
     mSaveStack.reset(nullptr);
 }
diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp
index d1871ff..415eef7 100644
--- a/libs/hwui/hwui/MinikinUtils.cpp
+++ b/libs/hwui/hwui/MinikinUtils.cpp
@@ -58,8 +58,9 @@
         size_t bufSize) {
     minikin::MinikinPaint minikinPaint;
     minikin::FontStyle minikinStyle = prepareMinikinPaint(&minikinPaint, paint, typeface);
-    minikin::Layout layout(Typeface::resolveDefault(typeface)->fFontCollection);
-    layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinStyle, minikinPaint);
+    minikin::Layout layout;
+    layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinStyle, minikinPaint,
+            Typeface::resolveDefault(typeface)->fFontCollection);
     return layout;
 }
 
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 39184f1..0906ba5 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -677,7 +677,7 @@
              ((audioSource > MediaRecorder.getAudioSourceMax()) &&
               (audioSource != MediaRecorder.AudioSource.RADIO_TUNER) &&
               (audioSource != MediaRecorder.AudioSource.HOTWORD)) )  {
-            throw new IllegalArgumentException("Invalid audio source.");
+            throw new IllegalArgumentException("Invalid audio source " + audioSource);
         }
         mRecordSource = audioSource;
 
@@ -703,8 +703,8 @@
             mAudioFormat = audioFormat;
             break;
         default:
-            throw new IllegalArgumentException("Unsupported sample encoding."
-                    + " Should be ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, or ENCODING_PCM_FLOAT.");
+            throw new IllegalArgumentException("Unsupported sample encoding " + audioFormat
+                    + ". Should be ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, or ENCODING_PCM_FLOAT.");
         }
     }
 
@@ -722,7 +722,8 @@
         int frameSizeInBytes = mChannelCount
             * (AudioFormat.getBytesPerSample(mAudioFormat));
         if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) {
-            throw new IllegalArgumentException("Invalid audio buffer size.");
+            throw new IllegalArgumentException("Invalid audio buffer size " + audioBufferSize
+                    + " (frame size " + frameSizeInBytes + ")");
         }
 
         mNativeBufferSizeInBytes = audioBufferSize;
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index d264127..13a22b4 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -23,6 +23,7 @@
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaMetricsSet;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -3186,59 +3187,22 @@
     public native final String getName();
 
     /**
-     *  Returns Analytics/Metrics data about the current content being
+     *  Return Metrics data about the current codec instance.
      *
-     * @return a Bundle containing the set of attributes and values available
-     * for the media being handled by this instance of MediaCodec
+     * @return a MediaMetricsSet containing the set of attributes and values
+     * available for the media being handled by this instance of MediaCodec
+     * The attributes are descibed in {@link MediaMetricsSet.MediaCodec}.
      *
-     *  <table style="width: 0%">
-     *   <thead>
-     *    <tr>
-     *     <th>Key</th>
-     *     <th>Type</th>
-     *     <th>Description</th>
-     *    </tr>
-     *   </thead>
-     *   <tbody>
-     *    <tr>
-     *     <td>{@code "codec"}</td>
-     *     <td>String</td>
-     *     <td>Identifies the particular codec in use</td>
-     *    </tr><tr>
-     *     <td>{@code "mime"}</td>
-     *     <td>String</td>
-     *     <td>Mime type of the media being encoded/decoded</td>
-     *    </tr><tr>
-     *     <td>{@code "mode"}</td>
-     *     <td>String</td>
-     *     <td>"Audio" or "Video"</td>
-     *    </tr><tr>
-     *     <td>{@code "secure"}</td>
-     *     <td>Integer</td>
-     *     <td>Indicates whether the code is operating on secure content and
-     *         may also use capabilities in android.media.MediaCrypto</td>
-     *    </tr><tr>
-     *     <td>{@code "height"}</td>
-     *     <td>Integer</td>
-     *     <td>Height (pixels); valid only when mode=video</td>
-     *    </tr><tr>
-     *     <td>{@code "width"}</td>
-     *     <td>Integer</td>
-     *     <td>Width (pixels); valid only when mode=video</td>
-     *    </tr><tr>
-     *     <td>{@code "rotation"}</td>
-     *     <td>Integer</td>
-     *     <td>rotation (degrees) to orient the video onto the target surface;
-     *         valid only when mode=video. Note there may be additional
-     *         rotations applied when the surface is mapped to the screen.</td>
-     *    </tr>
-     *   </tbody>
-     *  </table>
-     *
-     *  Additional fields specific to individual codecs will also appear in
+     *  Additional vendor-specific fields may also be present in
      *  the return value.
      */
-    public native Bundle getMetrics();
+    public MediaMetricsSet getMetrics() {
+        Bundle bundle = native_getMetrics();
+	MediaMetricsSet mSet = new MediaMetricsSet(bundle);
+	return mSet;
+    }
+
+    private native Bundle native_getMetrics();
 
     /**
      * Change a video encoder's target bitrate on the fly. The value is an
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index b9e409d..2ed6668 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -25,6 +25,7 @@
 import android.media.MediaCodec;
 import android.media.MediaFormat;
 import android.media.MediaHTTPService;
+import android.media.MediaMetricsSet;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -651,41 +652,24 @@
     public native boolean hasCacheReachedEndOfStream();
 
     /**
-     *  Returns Analytics/Metrics data about the current media container.
+     *  Return Metrics data about the current media container.
      *
-     * @return the set of keys and values available for the media being
-     * handled by this instance of MediaExtractor
+     * @return a MediaMetricsSet containing the set of attributes and values
+     * available for the media container being handled by this instance
+     * of MediaExtractor.
+     * The attributes are descibed in {@link MediaMetricsSet.MediaExtractor}.
      *
-     *  <table style="width: 0%">
-     *   <thead>
-     *    <tr>
-     *     <th>Key</th>
-     *     <th>Type</th>
-     *     <th>Description</th>
-     *    </tr>
-     *   </thead>
-     *   <tbody>
-     *    <tr>
-     *     <td>{@code "fmt"}</td>
-     *     <td>String</td>
-     *     <td>The container format (which determines the handler)</td>
-     *    </tr><tr>
-     *     <td>{@code "mime"}</td>
-     *     <td>String</td>
-     *     <td>Mime type of the container.</td>
-     *    </tr><tr>
-     *     <td>{@code "ntrk"}</td>
-     *     <td>Integer</td>
-     *     <td>Number of tracks in the container</td>
-     *    </tr>
-     *   </tbody>
-     *  </table>
-     *
-     *  Additional fields specific to individual codecs will also appear in
+     *  Additional vendor-specific fields may also be present in
      *  the return value.
      */
-    public native Bundle getMetrics();
 
+    public MediaMetricsSet getMetrics() {
+        Bundle bundle = native_getMetrics();
+	MediaMetricsSet mSet = new MediaMetricsSet(bundle);
+	return mSet;
+    }
+
+    private native Bundle native_getMetrics();
 
     private static native final void native_init();
     private native final void native_setup();
diff --git a/media/java/android/media/MediaMetricsSet.java b/media/java/android/media/MediaMetricsSet.java
new file mode 100644
index 0000000..5ecbee2
--- /dev/null
+++ b/media/java/android/media/MediaMetricsSet.java
@@ -0,0 +1,491 @@
+/*
+ * 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.media;
+
+import android.os.Bundle;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.Runnable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.net.HttpCookie;
+import java.net.HttpURLConnection;
+import java.net.InetSocketAddress;
+import java.net.URL;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.UUID;
+import java.util.Vector;
+
+
+/**
+ * MediaMetricsSet contains the results returned by the getMetrics()
+ * methods defined in other Media classes such as
+ * {@link MediaCodec}, {@link MediaExtractor}, {@link MediaPlayer},
+ * and {@link MediaRecorder}.
+ *
+ * MediaMetricsSet behaves similarly to a {@link Bundle}. It contains
+ * a set of keys and values.
+ * Methods such as {@link #getInt} and {@link #getString} are provided
+ * to extract values of the corresponding types.
+ * The {@link #keySet} method can be used to discover all of the keys
+ * that are present in the particular instance.
+ *
+ */
+public final class MediaMetricsSet
+{
+
+    /**
+     * This MediaCodec class holds the constants defining keys related to
+     * the metrics for a MediaCodec.
+     */
+    public final static class MediaCodec
+    {
+        private MediaCodec() {}
+
+        /**
+         * Key to extract the codec being used
+         * from the {@link MediaCodec#getMetrics} return value.
+         * The value is a String.
+         */
+        public static final String KEY_CODEC = "android.media.mediacodec.codec";
+
+        /**
+         * Key to extract the MIME type
+         * from the {@link MediaCodec#getMetrics} return value.
+         * The value is a String.
+         */
+        public static final String KEY_MIME = "android.media.mediacodec.mime";
+
+        /**
+         * Key to extract what the codec mode
+         * from the {@link MediaCodec#getMetrics} return value.
+         * The value is a String. Values will be one of the constants
+	 * MODE_AUDIO or MODE_VIDEO.
+         */
+        public static final String KEY_MODE = "android.media.mediacodec.mode";
+
+	/**
+	 * The value returned for the key {@link #KEY_MODE} when the
+	 * codec is a audio codec.
+	 */
+        public static final String MODE_AUDIO = "audio";
+
+	/**
+	 * The value returned for the key {@link #KEY_MODE} when the
+	 * codec is a video codec.
+	 */
+        public static final String MODE_VIDEO = "video";
+
+        /**
+         * Key to extract the flag indicating whether the codec is running
+         * as an encoder or decoder from the {@link MediaCodec#getMetrics} return value.
+         * The value is an integer.
+         * A 0 indicates decoder; 1 indicates encoder.
+         */
+        public static final String KEY_ENCODER = "android.media.mediacodec.encoder";
+
+        /**
+         * Key to extract the flag indicating whether the codec is running
+         * in secure (DRM) mode from the {@link MediaCodec#getMetrics} return value.
+         * The value is an integer.
+         */
+        public static final String KEY_SECURE = "android.media.mediacodec.secure";
+
+        /**
+         * Key to extract the width (in pixels) of the video track
+         * from the {@link MediaCodec#getMetrics} return value.
+         * The value is an integer.
+         */
+        public static final String KEY_WIDTH = "android.media.mediacodec.width";
+
+        /**
+         * Key to extract the height (in pixels) of the video track
+         * from the {@link MediaCodec#getMetrics} return value.
+         * The value is an integer.
+         */
+        public static final String KEY_HEIGHT = "android.media.mediacodec.height";
+
+        /**
+         * Key to extract the rotation (in degrees) to properly orient the video
+         * from the {@link MediaCodec#getMetrics} return.
+         * The value is a integer.
+         */
+        public static final String KEY_ROTATION = "android.media.mediacodec.rotation";
+
+    }
+
+    /**
+     * This class holds the constants defining keys related to
+     * the metrics for a MediaExtractor.
+     */
+    public final static class MediaExtractor
+    {
+        private MediaExtractor() {}
+
+        /**
+         * Key to extract the container format
+         * from the {@link MediaExtractor#getMetrics} return value.
+         * The value is a String.
+         */
+        public static final String KEY_FORMAT = "android.media.mediaextractor.fmt";
+
+        /**
+         * Key to extract the container MIME type
+         * from the {@link MediaExtractor#getMetrics} return value.
+         * The value is a String.
+         */
+        public static final String KEY_MIME = "android.media.mediaextractor.mime";
+
+        /**
+         * Key to extract the number of tracks in the container
+         * from the {@link MediaExtractor#getMetrics} return value.
+         * The value is an integer.
+         */
+        public static final String KEY_TRACKS = "android.media.mediaextractor.ntrk";
+
+    }
+
+    /**
+     * This class holds the constants defining keys related to
+     * the metrics for a MediaPlayer.
+     */
+    public final static class MediaPlayer
+    {
+        private MediaPlayer() {}
+
+        /**
+         * Key to extract the MIME type of the video track
+         * from the {@link MediaPlayer#getMetrics} return value.
+         * The value is a String.
+         */
+        public static final String KEY_MIME_VIDEO = "android.media.mediaplayer.video.mime";
+
+        /**
+         * Key to extract the codec being used to decode the video track
+         * from the {@link MediaPlayer#getMetrics} return value.
+         * The value is a String.
+         */
+        public static final String KEY_CODEC_VIDEO = "android.media.mediaplayer.video.codec";
+
+        /**
+         * Key to extract the width (in pixels) of the video track
+         * from the {@link MediaPlayer#getMetrics} return value.
+         * The value is an integer.
+         */
+        public static final String KEY_WIDTH = "android.media.mediaplayer.width";
+
+        /**
+         * Key to extract the height (in pixels) of the video track
+         * from the {@link MediaPlayer#getMetrics} return value.
+         * The value is an integer.
+         */
+        public static final String KEY_HEIGHT = "android.media.mediaplayer.height";
+
+        /**
+         * Key to extract the count of video frames played
+         * from the {@link MediaPlayer#getMetrics} return value.
+         * The value is an integer.
+         */
+        public static final String KEY_FRAMES = "android.media.mediaplayer.frames";
+
+        /**
+         * Key to extract the count of video frames dropped
+         * from the {@link MediaPlayer#getMetrics} return value.
+         * The value is an integer.
+         */
+        public static final String KEY_FRAMES_DROPPED = "android.media.mediaplayer.dropped";
+
+        /**
+         * Key to extract the MIME type of the audio track
+         * from the {@link MediaPlayer#getMetrics} return value.
+         * The value is a String.
+         */
+        public static final String KEY_MIME_AUDIO = "android.media.mediaplayer.audio.mime";
+
+        /**
+         * Key to extract the codec being used to decode the audio track
+         * from the {@link MediaPlayer#getMetrics} return value.
+         * The value is a String.
+         */
+        public static final String KEY_CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
+
+        /**
+         * Key to extract the duration (in milliseconds) of the
+         * media being played
+         * from the {@link MediaPlayer#getMetrics} return value.
+         * The value is a long.
+         */
+        public static final String KEY_DURATION = "android.media.mediaplayer.durationMs";
+
+        /**
+         * Key to extract the playing time (in milliseconds) of the
+         * media being played
+         * from the {@link MediaPlayer#getMetrics} return value.
+         * The value is a long.
+         */
+        public static final String KEY_PLAYING = "android.media.mediaplayer.playingMs";
+
+        /**
+         * Key to extract the count of errors encountered while
+         * playing the media
+         * from the {@link MediaPlayer#getMetrics} return value.
+         * The value is an integer.
+         */
+        public static final String KEY_ERRORS = "android.media.mediaplayer.err";
+
+        /**
+         * Key to extract an (optional) error code detected while
+         * playing the media
+         * from the {@link MediaPlayer#getMetrics} return value.
+         * The value is an integer.
+         */
+        public static final String KEY_ERROR_CODE = "android.media.mediaplayer.errcode";
+
+    }
+
+    /**
+     * This class holds the constants defining keys related to
+     * the metrics for a MediaRecorder.
+     */
+    public final static class MediaRecorder
+    {
+        private MediaRecorder() {}
+
+        /**
+         * Key to extract the audio bitrate
+         * from the {@link MediaRecorder#getMetrics} return.
+         * The value is an integer.
+         */
+        public static final String KEY_AUDIO_BITRATE = "android.media.mediarecorder.audio-bitrate";
+
+        /**
+         * Key to extract the number of audio channels
+         * from the {@link MediaRecorder#getMetrics} return.
+         * The value is an integer.
+         */
+        public static final String KEY_AUDIO_CHANNELS = "android.media.mediarecorder.audio-channels";
+
+        /**
+         * Key to extract the audio samplerate
+         * from the {@link MediaRecorder#getMetrics} return.
+         * The value is an integer.
+         */
+        public static final String KEY_AUDIO_SAMPLERATE = "android.media.mediarecorder.audio-samplerate";
+
+        /**
+         * Key to extract the audio timescale
+         * from the {@link MediaRecorder#getMetrics} return.
+         * The value is an integer.
+         */
+        public static final String KEY_AUDIO_TIMESCALE = "android.media.mediarecorder.audio-timescale";
+
+        /**
+         * Key to extract the video capture frame rate
+         * from the {@link MediaRecorder#getMetrics} return.
+         * The value is a double.
+         */
+        public static final String KEY_CAPTURE_FPS = "android.media.mediarecorder.capture-fps";
+
+        /**
+         * Key to extract the video capture framerate enable value
+         * from the {@link MediaRecorder#getMetrics} return.
+         * The value is an integer.
+         */
+        public static final String KEY_CAPTURE_FPS_ENABLE = "android.media.mediarecorder.capture-fpsenable";
+
+        /**
+         * Key to extract the intended playback frame rate
+         * from the {@link MediaRecorder#getMetrics} return.
+         * The value is an integer.
+         */
+        public static final String KEY_FRAMERATE = "android.media.mediarecorder.frame-rate";
+
+        /**
+         * Key to extract the height (in pixels) of the captured video
+         * from the {@link MediaRecorder#getMetrics} return.
+         * The value is an integer.
+         */
+        public static final String KEY_HEIGHT = "android.media.mediarecorder.height";
+
+        /**
+         * Key to extract the recorded movies time units
+         * from the {@link MediaRecorder#getMetrics} return.
+         * The value is an integer.
+         * A value of 1000 indicates that the movie's timing is in milliseconds.
+         */
+        public static final String KEY_MOVIE_TIMESCALE = "android.media.mediarecorder.movie-timescale";
+
+        /**
+         * Key to extract the rotation (in degrees) to properly orient the video
+         * from the {@link MediaRecorder#getMetrics} return.
+         * The value is an integer.
+         */
+        public static final String KEY_ROTATION = "android.media.mediarecorder.rotation";
+
+        /**
+         * Key to extract the video bitrate from being used
+         * from the {@link MediaRecorder#getMetrics} return.
+         * The value is an integer.
+         */
+        public static final String KEY_VIDEO_BITRATE = "android.media.mediarecorder.video-bitrate";
+
+        /**
+         * Key to extract the value for how often video iframes are generated
+         * from the {@link MediaRecorder#getMetrics} return.
+         * The value is an integer.
+         */
+        public static final String KEY_VIDEO_IFRAME_INTERVAL = "android.media.mediarecorder.video-iframe-interval";
+
+        /**
+         * Key to extract the video encoding level
+         * from the {@link MediaRecorder#getMetrics} return.
+         * The value is an integer.
+         */
+        public static final String KEY_VIDEO_LEVEL = "android.media.mediarecorder.video-encoder-level";
+
+        /**
+         * Key to extract the video encoding profile
+         * from the {@link MediaRecorder#getMetrics} return.
+         * The value is an integer.
+         */
+        public static final String KEY_VIDEO_PROFILE = "android.media.mediarecorder.video-encoder-profile";
+
+        /**
+         * Key to extract the recorded video time units
+         * from the {@link MediaRecorder#getMetrics} return.
+         * The value is an integer.
+         * A value of 1000 indicates that the video's timing is in milliseconds.
+         */
+        public static final String KEY_VIDEO_TIMESCALE = "android.media.mediarecorder.video-timescale";
+
+        /**
+         * Key to extract the width (in pixels) of the captured video
+         * from the {@link MediaRecorder#getMetrics} return.
+         * The value is an integer.
+         */
+        public static final String KEY_WIDTH = "android.media.mediarecorder.width";
+
+    }
+
+    /*
+     * Methods that we want
+     */
+
+    private Bundle mBundle;
+
+    MediaMetricsSet(Bundle bundle) {
+        mBundle = bundle;
+    }
+
+    /**
+     * Returns the number of mappings contained in this Bundle.
+     *
+     * @return the number of mappings as an int.
+     */
+    public int size() {
+        return mBundle.size();
+    }
+
+    /**
+     * Returns true if the mapping of this MediaMetricsSet is empty,
+     * false otherwise.
+     */
+    public boolean isEmpty() {
+        return mBundle.isEmpty();
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a double value
+     */
+    public double getDouble(String key, double defaultValue) {
+        return mBundle.getDouble(key, defaultValue);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return an int value
+     */
+    public int getInt(String key, int defaultValue) {
+        return mBundle.getInt(key, defaultValue);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist
+     * @return a long value
+     */
+    public long getLong(String key, long defaultValue) {
+        return mBundle.getLong(key, defaultValue);
+    }
+
+    /**
+     * Returns the value associated with the given key, or defaultValue if
+     * no mapping of the desired type exists for the given key or if a null
+     * value is explicitly associated with the given key.
+     *
+     * @param key a String
+     * @param defaultValue Value to return if key does not exist or if a null
+     *     value is associated with the given key.
+     * @return the String value associated with the given key, or defaultValue
+     *     if no valid String object is currently mapped to that key.
+     */
+    public String getString(String key, String defaultValue) {
+        return mBundle.getString(key, defaultValue);
+    }
+
+    /**
+     * Returns a Set containing the Strings used as keys in this Bundle.
+     *
+     * @return a Set of String keys
+     */
+    public Set<String> keySet() {
+        return mBundle.keySet();
+    }
+
+
+
+    public String toString() {
+        return mBundle.toString();
+    }
+
+}
+
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index b85c911..1ee05b8 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -49,6 +49,7 @@
 import android.media.BufferingParams;
 import android.media.MediaDrm;
 import android.media.MediaFormat;
+import android.media.MediaMetricsSet;
 import android.media.MediaTimeProvider;
 import android.media.PlaybackParams;
 import android.media.SubtitleController;
@@ -1494,70 +1495,22 @@
     public native int getVideoHeight();
 
     /**
-     *  Returns Analytics/Metrics data about the current video in this player.
+     * Return Metrics data about the current player.
      *
-     * @return the a map of attributes and values available for this video
-     * player or null if no metrics are available.
+     * @return a MediaMetricsSet containing the set of attributes and values
+     * available for the media being handled by this instance of MediaPlayer
+     * The attributes are descibed in {@link MediaMetricsSet.MediaPlayer}.
      *
-     *  <table style="width: 0%">
-     *   <thead>
-     *    <tr>
-     *     <th>Key</th>
-     *     <th>Type</th>
-     *     <th>Description</th>
-     *    </tr>
-     *   </thead>
-     *   <tbody>
-     *    <tr>
-     *     <td>{@code "video/codec"}</td>
-     *     <td>String</td>
-     *     <td>Identifies the video codec in use</td>
-     *    </tr><tr>
-     *     <td>{@code "video/mime"}</td>
-     *     <td>String</td>
-     *     <td>Mime type of the video being encoded/decoded</td>
-     *    </tr><tr>
-     *     <td>{@code "audio/codec"}</td>
-     *     <td>String</td>
-     *     <td>Identifies the audio codec in use</td>
-     *    </tr><tr>
-     *     <td>{@code "audio/mime"}</td>
-     *     <td>String</td>
-     *     <td>Mime type of the audio being encoded/decoded</td>
-     *    </tr><tr>
-     *     <td>{@code "ht"}</td>
-     *     <td>Integer</td>
-     *     <td>Height (pixels); valid only when mode=video</td>
-     *    </tr><tr>
-     *     <td>{@code "wid"}</td>
-     *     <td>Integer</td>
-     *     <td>Width (pixels); valid only when mode=video</td>
-     *    </tr><tr>
-     *     <td>{@code "frame"}</td>
-     *     <td>Integer</td>
-     *     <td>Number of decoded video frames sent to the display</td>
-     *    </tr><tr>
-     *     <td>{@code "dropped"}</td>
-     *     <td>Integer</td>
-     *     <td>Number of decoded video frames that were not sent to display.
-     *         These frames were dropped by the player.</td>
-     *    </tr><tr>
-     *     <td>{@code "durationMs"}</td>
-     *     <td>Integer</td>
-     *     <td>The length of the media being played (in ms), e.g. "This video lasts for 30000 milliseconds". </td>
-     *    </tr><tr>
-     *     <td>{@code "playingMs"}</td>
-     *     <td>Integer</td>
-     *     <td>The time the media has been played (in ms). If you watch a
-     *         30 second twice through, this will report 60000 ms.</td>
-     *    </tr>
-     *   </tbody>
-     *  </table>
-     *
-     *  Additional fields specific to individual codecs will also appear in
+     *  Additional vendor-specific fields may also be present in
      *  the return value.
      */
-    public native Bundle getMetrics();
+    public MediaMetricsSet getMetrics() {
+        Bundle bundle = native_getMetrics();
+	MediaMetricsSet mSet = new MediaMetricsSet(bundle);
+	return mSet;
+    }
+
+    private native Bundle native_getMetrics();
 
     /**
      * Checks whether the MediaPlayer is playing.
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 075a84f..cdc1d60 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -20,6 +20,7 @@
 import android.annotation.SystemApi;
 import android.app.ActivityThread;
 import android.hardware.Camera;
+import android.media.MediaMetricsSet;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -1259,91 +1260,23 @@
     private native void setParameter(String nameValuePair);
 
     /**
-     * Returns Metrics data about the current media container.
+     *  Return Metrics data about the current Mediarecorder instance.
      *
-     * @return the set of keys and values available for the media being
-     * handled by this instance of MediaExtractor. The keys, data types,
-     * and meaning are described in the following table.
+     * @return a MediaMetricsSet containing the set of attributes and values
+     * available for the media being generated by this instance of
+     * MediaRecorder.
+     * The attributes are descibed in {@link MediaMetricsSet.MediaRecorder}.
      *
-     *  <table style="width: 0%">
-     *   <thead>
-     *    <tr>
-     *     <th>Key</th>
-     *     <th>Type</th>
-     *     <th>Description</th>
-     *    </tr>
-     *   </thead>
-     *   <tbody>
-     *    <tr>
-     *     <td>{@code "ht"}</td>
-     *     <td>Integer</td>
-     *     <td>Height of the recorded video (pixels)</td>
-     *    </tr><tr>
-     *     <td>{@code "wid"}</td>
-     *     <td>Integer</td>
-     *     <td>Width of the recorded video (pixels)</td>
-     *    </tr><tr>
-     *     <td>{@code "frame-rate"}</td>
-     *     <td>Integer</td>
-     *     <td>Framerate of captured Video (frames per second)</td>
-     *    </tr><tr>
-     *     <td>{@code "video-bitrate"}</td>
-     *     <td>Integer</td>
-     *     <td>Bit rate of encoded video (bits per second)</td>
-     *    </tr><tr>
-     *     <td>{@code "video-iframe-interval"}</td>
-     *     <td>Integer</td>
-     *     <td>Interval between encoded IFrames (seconds)</td>
-     *    </tr><tr>
-     *     <td>{@code "video-timescale"}</td>
-     *     <td>Integer</td>
-     *     <td></td>
-     *    </tr><tr>
-     *     <td>{@code "video-encoder-profile"}</td>
-     *     <td>Integer</td>
-     *     <td>Video Encoder Profile, as defined in OpenMAX IL</td>
-     *    </tr><tr>
-     *     <td>{@code "video-encoder-level"}</td>
-     *     <td>Integer</td>
-     *     <td>Video Encoder Level, as defined in OpenMAX IL</td>
-     *    </tr><tr>
-     *     <td>{@code "audio-bitrate"}</td>
-     *     <td>Integer</td>
-     *     <td>Bitrate of encoded audio (bits per second)</td>
-     *    </tr><tr>
-     *     <td>{@code "audio-samplerate"}</td>
-     *     <td>Integer</td>
-     *     <td></td>
-     *    </tr><tr>
-     *     <td>{@code "audio-channels"}</td>
-     *     <td>Integer</td>
-     *     <td>Number of Audio Channels Captured</td>
-     *    </tr><tr>
-     *     <td>{@code "audio-timescale"}</td>
-     *     <td>Integer</td>
-     *     <td></td>
-     *    </tr><tr>
-     *     <td>{@code "movie-timescale"}</td>
-     *     <td>Integer</td>
-     *     <td></td>
-     *    </tr><tr>
-     *     <td>{@code "movie-timescale"}</td>
-     *     <td>Integer</td>
-     *     <td></td>
-     *    </tr><tr>
-     *     <td>{@code "capture-fps"}</td>
-     *     <td>Integer</td>
-     *     <td></td>
-     *    </tr><tr>
-     *     <td>{@code "rotation"}</td>
-     *     <td>Integer</td>
-     *     <td>Orientation of the Video (degrees)</td>
-     *    </tr>
-     *   </tbody>
-     *  </table>
+     *  Additional vendor-specific fields may also be present in
+     *  the return value.
      */
+    public MediaMetricsSet getMetrics() {
+        Bundle bundle = native_getMetrics();
+	MediaMetricsSet mSet = new MediaMetricsSet(bundle);
+	return mSet;
+    }
 
-    public native Bundle getMetrics();
+    private native Bundle native_getMetrics();
 
     @Override
     protected void finalize() { native_finalize(); }
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 6e15d8d..62fd395 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -2679,6 +2679,9 @@
         /**
          * The ID of the TV channel that provides this TV program.
          *
+         * <p>This value cannot be changed once it's set. Trying to modify it will make the update
+         * fail.
+         *
          * <p>This is a part of the channel URI and matches to {@link BaseColumns#_ID}.
          *
          * <p>This is a required field.
diff --git a/media/java/android/media/tv/TvInputHardwareInfo.java b/media/java/android/media/tv/TvInputHardwareInfo.java
index 51fa036..957c582 100644
--- a/media/java/android/media/tv/TvInputHardwareInfo.java
+++ b/media/java/android/media/tv/TvInputHardwareInfo.java
@@ -16,11 +16,15 @@
 
 package android.media.tv;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.media.AudioManager;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
+import java.lang.annotation.Retention;
 
 /**
  * Simple container for information about TV input hardware.
@@ -44,6 +48,28 @@
     public static final int TV_INPUT_TYPE_HDMI           = 9;
     public static final int TV_INPUT_TYPE_DISPLAY_PORT   = 10;
 
+    /** @hide */
+    @Retention(SOURCE)
+    @IntDef({CABLE_CONNECTION_STATUS_UNKNOWN, CABLE_CONNECTION_STATUS_CONNECTED,
+        CABLE_CONNECTION_STATUS_DISCONNECTED})
+    public @interface CableConnectionStatus {}
+
+    // Match hardware/interfaces/tv/input/1.0/types.hal
+    /**
+     * The hardware is unsure about the connection status or does not support cable detection.
+     */
+    public static final int CABLE_CONNECTION_STATUS_UNKNOWN = 0;
+
+    /**
+     * Cable is connected to the hardware.
+     */
+    public static final int CABLE_CONNECTION_STATUS_CONNECTED = 1;
+
+    /**
+     * Cable is disconnected to the hardware.
+     */
+    public static final int CABLE_CONNECTION_STATUS_DISCONNECTED = 2;
+
     public static final Parcelable.Creator<TvInputHardwareInfo> CREATOR =
             new Parcelable.Creator<TvInputHardwareInfo>() {
         @Override
@@ -69,6 +95,8 @@
     private int mAudioType;
     private String mAudioAddress;
     private int mHdmiPortId;
+    @CableConnectionStatus
+    private int mCableConnectionStatus;
 
     private TvInputHardwareInfo() {
     }
@@ -96,6 +124,19 @@
         return mHdmiPortId;
     }
 
+    /**
+     * Gets the cable connection status of the hardware.
+     *
+     * @return {@code CABLE_CONNECTION_STATUS_CONNECTED} if cable is connected.
+     *         {@code CABLE_CONNECTION_STATUS_DISCONNECTED} if cable is disconnected.
+     *         {@code CABLE_CONNECTION_STATUS_UNKNOWN} if the hardware is unsure about the
+     *         connection status or does not support cable detection.
+     */
+    @CableConnectionStatus
+    public int getCableConnectionStatus() {
+        return mCableConnectionStatus;
+    }
+
     @Override
     public String toString() {
         StringBuilder b = new StringBuilder(128);
@@ -106,6 +147,7 @@
         if (mType == TV_INPUT_TYPE_HDMI) {
             b.append(", hdmi_port=").append(mHdmiPortId);
         }
+        b.append(", cable_connection_status=").append(mCableConnectionStatus);
         b.append("}");
         return b.toString();
     }
@@ -125,6 +167,7 @@
         if (mType == TV_INPUT_TYPE_HDMI) {
             dest.writeInt(mHdmiPortId);
         }
+        dest.writeInt(mCableConnectionStatus);
     }
 
     public void readFromParcel(Parcel source) {
@@ -135,6 +178,7 @@
         if (mType == TV_INPUT_TYPE_HDMI) {
             mHdmiPortId = source.readInt();
         }
+        mCableConnectionStatus = source.readInt();
     }
 
     public static final class Builder {
@@ -143,6 +187,7 @@
         private int mAudioType = AudioManager.DEVICE_NONE;
         private String mAudioAddress = "";
         private Integer mHdmiPortId = null;
+        private Integer mCableConnectionStatus = CABLE_CONNECTION_STATUS_UNKNOWN;
 
         public Builder() {
         }
@@ -172,6 +217,14 @@
             return this;
         }
 
+        /**
+         * Sets cable connection status.
+         */
+        public Builder cableConnectionStatus(@CableConnectionStatus int cableConnectionStatus) {
+            mCableConnectionStatus = cableConnectionStatus;
+            return this;
+        }
+
         public TvInputHardwareInfo build() {
             if (mDeviceId == null || mType == null) {
                 throw new UnsupportedOperationException();
@@ -191,6 +244,7 @@
             if (mHdmiPortId != null) {
                 info.mHdmiPortId = mHdmiPortId;
             }
+            info.mCableConnectionStatus = mCableConnectionStatus;
             return info;
         }
     }
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 35988d4..a292b8e 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -138,7 +138,6 @@
 
     // Attributes from XML meta data.
     private final String mSetupActivity;
-    private final String mSettingsActivity;
     private final boolean mCanRecord;
     private final int mTunerCount;
 
@@ -259,9 +258,8 @@
 
     private TvInputInfo(ResolveInfo service, String id, int type, boolean isHardwareInput,
             CharSequence label, int labelResId, Icon icon, Icon iconStandby, Icon iconDisconnected,
-            String setupActivity, String settingsActivity, boolean canRecord, int tunerCount,
-            HdmiDeviceInfo hdmiDeviceInfo, boolean isConnectedToHdmiSwitch, String parentId,
-            Bundle extras) {
+            String setupActivity, boolean canRecord, int tunerCount, HdmiDeviceInfo hdmiDeviceInfo,
+            boolean isConnectedToHdmiSwitch, String parentId, Bundle extras) {
         mService = service;
         mId = id;
         mType = type;
@@ -272,7 +270,6 @@
         mIconStandby = iconStandby;
         mIconDisconnected = iconDisconnected;
         mSetupActivity = setupActivity;
-        mSettingsActivity = settingsActivity;
         mCanRecord = canRecord;
         mTunerCount = tunerCount;
         mHdmiDeviceInfo = hdmiDeviceInfo;
@@ -340,14 +337,12 @@
 
     /**
      * Returns an intent to start the settings activity for this TV input.
+     *
+     * @deprecated Use {@link #createSetupIntent()} instead. Settings activity is deprecated.
+     *             Use setup activity instead to provide settings.
      */
+    @Deprecated
     public Intent createSettingsIntent() {
-        if (!TextUtils.isEmpty(mSettingsActivity)) {
-            Intent intent = new Intent(Intent.ACTION_MAIN);
-            intent.setClassName(mService.serviceInfo.packageName, mSettingsActivity);
-            intent.putExtra(EXTRA_INPUT_ID, getId());
-            return intent;
-        }
         return null;
     }
 
@@ -554,7 +549,6 @@
                 && Objects.equals(mIconStandby, obj.mIconStandby)
                 && Objects.equals(mIconDisconnected, obj.mIconDisconnected)
                 && TextUtils.equals(mSetupActivity, obj.mSetupActivity)
-                && TextUtils.equals(mSettingsActivity, obj.mSettingsActivity)
                 && mCanRecord == obj.mCanRecord
                 && mTunerCount == obj.mTunerCount
                 && Objects.equals(mHdmiDeviceInfo, obj.mHdmiDeviceInfo)
@@ -589,7 +583,6 @@
         dest.writeParcelable(mIconStandby, flags);
         dest.writeParcelable(mIconDisconnected, flags);
         dest.writeString(mSetupActivity);
-        dest.writeString(mSettingsActivity);
         dest.writeByte(mCanRecord ? (byte) 1 : 0);
         dest.writeInt(mTunerCount);
         dest.writeParcelable(mHdmiDeviceInfo, flags);
@@ -631,7 +624,6 @@
         mIconStandby = in.readParcelable(null);
         mIconDisconnected = in.readParcelable(null);
         mSetupActivity = in.readString();
-        mSettingsActivity = in.readString();
         mCanRecord = in.readByte() == 1;
         mTunerCount = in.readInt();
         mHdmiDeviceInfo = in.readParcelable(null);
@@ -678,7 +670,6 @@
         private Icon mIconStandby;
         private Icon mIconDisconnected;
         private String mSetupActivity;
-        private String mSettingsActivity;
         private Boolean mCanRecord;
         private Integer mTunerCount;
         private TvInputHardwareInfo mTvInputHardwareInfo;
@@ -906,7 +897,7 @@
             }
             parseServiceMetadata(type);
             return new TvInputInfo(mResolveInfo, id, type, isHardwareInput, mLabel, mLabelResId,
-                    mIcon, mIconStandby, mIconDisconnected, mSetupActivity, mSettingsActivity,
+                    mIcon, mIconStandby, mIconDisconnected, mSetupActivity,
                     mCanRecord == null ? false : mCanRecord, mTunerCount == null ? 0 : mTunerCount,
                     mHdmiDeviceInfo, isConnectedToHdmiSwitch, mParentId, mExtras);
         }
@@ -961,8 +952,6 @@
                 if (inputType == TYPE_TUNER && TextUtils.isEmpty(mSetupActivity)) {
                     throw new IllegalStateException("Setup activity not found for " + si.name);
                 }
-                mSettingsActivity = sa.getString(
-                        com.android.internal.R.styleable.TvInputService_settingsActivity);
                 if (mCanRecord == null) {
                     mCanRecord = sa.getBoolean(
                             com.android.internal.R.styleable.TvInputService_canRecord, false);
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 1eae8db..09b2050 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -224,9 +224,8 @@
      * {@link TvInputCallback#onInputStateChanged(String, int)}: The input source is connected.
      *
      * <p>This state indicates that a source device is connected to the input port and is in the
-     * normal operation mode. It is mostly relevant to hardware inputs such as HDMI input. This is
-     * the default state for any hardware inputs where their states are unknown. Non-hardware inputs
-     * are considered connected all the time.
+     * normal operation mode. It is mostly relevant to hardware inputs such as HDMI input.
+     * Non-hardware inputs are considered connected all the time.
      */
     public static final int INPUT_STATE_CONNECTED = 0;
 
@@ -236,7 +235,8 @@
      * in standby mode.
      *
      * <p>This state indicates that a source device is connected to the input port but is in standby
-     * mode. It is mostly relevant to hardware inputs such as HDMI input.
+     * or low power mode. It is mostly relevant to hardware inputs such as HDMI input and Component
+     * inputs.
      */
     public static final int INPUT_STATE_CONNECTED_STANDBY = 1;
 
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 293e5dd..a8dd313 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -624,7 +624,7 @@
     return OK;
 }
 
-status_t JMediaCodec::getMetrics(JNIEnv *, Parcel *reply) const {
+status_t JMediaCodec::getMetrics(JNIEnv *, MediaAnalyticsItem * &reply) const {
 
     status_t status = mCodec->getMetrics(reply);
     return status;
@@ -1666,9 +1666,9 @@
 }
 
 static jobject
-android_media_MediaCodec_getMetrics(JNIEnv *env, jobject thiz)
+android_media_MediaCodec_native_getMetrics(JNIEnv *env, jobject thiz)
 {
-    ALOGV("android_media_MediaCodec_getMetrics");
+    ALOGV("android_media_MediaCodec_native_getMetrics");
 
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
     if (codec == NULL ) {
@@ -1677,16 +1677,14 @@
     }
 
     // get what we have for the metrics from the codec
-    Parcel reply;
-    status_t err = codec->getMetrics(env, &reply);
+    MediaAnalyticsItem *item = NULL;
+
+    status_t err = codec->getMetrics(env, item);
     if (err != OK) {
         ALOGE("getMetrics failed");
         return (jobject) NULL;
     }
 
-    // build and return the Bundle
-    MediaAnalyticsItem *item = new MediaAnalyticsItem;
-    item->readFromParcel(reply);
     jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL);
 
     // housekeeping
@@ -2004,8 +2002,8 @@
     { "getName", "()Ljava/lang/String;",
       (void *)android_media_MediaCodec_getName },
 
-    { "getMetrics", "()Landroid/os/Bundle;",
-      (void *)android_media_MediaCodec_getMetrics},
+    { "native_getMetrics", "()Landroid/os/Bundle;",
+      (void *)android_media_MediaCodec_native_getMetrics},
 
     { "setParameters", "([Ljava/lang/String;[Ljava/lang/Object;)V",
       (void *)android_media_MediaCodec_setParameters },
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index a8c76c5..c9a1700 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -19,6 +19,7 @@
 
 #include "jni.h"
 
+#include <media/MediaAnalyticsItem.h>
 #include <media/hardware/CryptoAPI.h>
 #include <media/stagefright/foundation/ABase.h>
 #include <media/stagefright/foundation/AHandler.h>
@@ -116,7 +117,7 @@
 
     status_t getName(JNIEnv *env, jstring *name) const;
 
-    status_t getMetrics(JNIEnv *env, Parcel *reply) const;
+    status_t getMetrics(JNIEnv *env, MediaAnalyticsItem * &reply) const;
 
     status_t setParameters(const sp<AMessage> &params);
 
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 3c33493..c2cfed9 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -811,9 +811,9 @@
 }
 
 static jobject
-android_media_MediaExtractor_getMetrics(JNIEnv * env, jobject thiz)
+android_media_MediaExtractor_native_getMetrics(JNIEnv * env, jobject thiz)
 {
-    ALOGV("android_media_MediaExtractor_getMetrics");
+    ALOGV("android_media_MediaExtractor_native_getMetrics");
 
     sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
     if (extractor == NULL ) {
@@ -905,8 +905,8 @@
     { "hasCacheReachedEndOfStream", "()Z",
       (void *)android_media_MediaExtractor_hasCacheReachedEOS },
 
-    {"getMetrics",          "()Landroid/os/Bundle;",
-      (void *)android_media_MediaExtractor_getMetrics},
+    {"native_getMetrics",          "()Landroid/os/Bundle;",
+      (void *)android_media_MediaExtractor_native_getMetrics},
 };
 
 int register_android_media_MediaExtractor(JNIEnv *env) {
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 27724a1..1b52cf5 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -708,7 +708,7 @@
 }
 
 static jobject
-android_media_MediaPlayer_getMetrics(JNIEnv *env, jobject thiz)
+android_media_MediaPlayer_native_getMetrics(JNIEnv *env, jobject thiz)
 {
     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
     if (mp == NULL ) {
@@ -1393,7 +1393,7 @@
     {"_stop",               "()V",                              (void *)android_media_MediaPlayer_stop},
     {"getVideoWidth",       "()I",                              (void *)android_media_MediaPlayer_getVideoWidth},
     {"getVideoHeight",      "()I",                              (void *)android_media_MediaPlayer_getVideoHeight},
-    {"getMetrics",          "()Landroid/os/Bundle;",            (void *)android_media_MediaPlayer_getMetrics},
+    {"native_getMetrics",          "()Landroid/os/Bundle;",            (void *)android_media_MediaPlayer_native_getMetrics},
     {"setPlaybackParams", "(Landroid/media/PlaybackParams;)V", (void *)android_media_MediaPlayer_setPlaybackParams},
     {"getPlaybackParams", "()Landroid/media/PlaybackParams;", (void *)android_media_MediaPlayer_getPlaybackParams},
     {"setSyncParams",     "(Landroid/media/SyncParams;)V",  (void *)android_media_MediaPlayer_setSyncParams},
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 77544eb..7a63e00 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -628,9 +628,9 @@
 }
 
 static jobject
-android_media_MediaRecorder_getMetrics(JNIEnv *env, jobject thiz)
+android_media_MediaRecorder_native_getMetrics(JNIEnv *env, jobject thiz)
 {
-    ALOGV("android_media_MediaRecorder_getMetrics");
+    ALOGV("android_media_MediaRecorder_native_getMetrics");
 
     sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
     if (mr == NULL) {
@@ -688,7 +688,7 @@
     {"native_finalize",      "()V",                             (void *)android_media_MediaRecorder_native_finalize},
     {"native_setInputSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaRecorder_setInputSurface },
 
-    {"getMetrics",          "()Landroid/os/Bundle;",            (void *)android_media_MediaRecorder_getMetrics},
+    {"native_getMetrics",          "()Landroid/os/Bundle;",            (void *)android_media_MediaRecorder_native_getMetrics},
 };
 
 // This function only registers the native methods, and is called from
diff --git a/media/mca/filterfw/jni/jni_util.h b/media/mca/filterfw/jni/jni_util.h
index 803ed29..9b57958 100644
--- a/media/mca/filterfw/jni/jni_util.h
+++ b/media/mca/filterfw/jni/jni_util.h
@@ -198,7 +198,8 @@
     CObjMap objects_;
     FlagMap owns_;
 
-    DISALLOW_COPY_AND_ASSIGN(ObjectPool);
+    ObjectPool(const ObjectPool&) = delete;
+    ObjectPool& operator=(const ObjectPool&) = delete;
 };
 
 template<typename T> ObjectPool<T>* ObjectPool<T>::instance_ = NULL;
diff --git a/media/mca/filterfw/native/Android.mk b/media/mca/filterfw/native/Android.mk
index 2e900fe..feaefcb 100644
--- a/media/mca/filterfw/native/Android.mk
+++ b/media/mca/filterfw/native/Android.mk
@@ -41,7 +41,11 @@
 
 LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
 
-LOCAL_STATIC_LIBRARIES := libarect
+LOCAL_STATIC_LIBRARIES := \
+    libarect \
+
+LOCAL_SHARED_LIBRARIES += \
+    libgui \
 
 # TODO: Build a shared library as well?
 include $(BUILD_STATIC_LIBRARY)
diff --git a/media/mca/filterfw/native/base/utilities.h b/media/mca/filterfw/native/base/utilities.h
index 6bb3b7f..2818f72 100644
--- a/media/mca/filterfw/native/base/utilities.h
+++ b/media/mca/filterfw/native/base/utilities.h
@@ -23,18 +23,6 @@
 namespace android {
 namespace filterfw {
 
-// Convenience Macro to make copy constructor and assignment operator private
-// (thereby disallowing copying and assigning).
-#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
-  TypeName(const TypeName&);               \
-  void operator=(const TypeName&)
-
-// A macro to disallow all the implicit constructors, namely the
-// default constructor, copy constructor and operator= functions.
-#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
-  TypeName();                                    \
-  DISALLOW_COPY_AND_ASSIGN(TypeName)
-
 // STLDeleteContainerPointers()
 //  For a range within a container of pointers, calls delete
 //  (non-array version) on these pointers.
diff --git a/media/mca/filterfw/native/core/gl_env.h b/media/mca/filterfw/native/core/gl_env.h
index 0445301..6af91af 100644
--- a/media/mca/filterfw/native/core/gl_env.h
+++ b/media/mca/filterfw/native/core/gl_env.h
@@ -256,7 +256,8 @@
     std::map<int, ShaderProgram*> attached_shaders_;
     std::map<int, VertexFrame*> attached_vframes_;
 
-    DISALLOW_COPY_AND_ASSIGN(GLEnv);
+    GLEnv(const GLEnv&) = delete;
+    GLEnv& operator=(const GLEnv&) = delete;
 };
 
 } // namespace filterfw
diff --git a/media/mca/filterfw/native/core/native_frame.h b/media/mca/filterfw/native/core/native_frame.h
index 2da557d..fd52165 100644
--- a/media/mca/filterfw/native/core/native_frame.h
+++ b/media/mca/filterfw/native/core/native_frame.h
@@ -76,7 +76,8 @@
     // Capacity of data buffer in bytes.
     int capacity_;
 
-    DISALLOW_COPY_AND_ASSIGN(NativeFrame);
+    NativeFrame(const NativeFrame&) = delete;
+    NativeFrame& operator=(const NativeFrame&) = delete;
 };
 
 } // namespace filterfw
diff --git a/media/mca/filterfw/native/core/native_program.h b/media/mca/filterfw/native/core/native_program.h
index ce704af..e39afc9 100644
--- a/media/mca/filterfw/native/core/native_program.h
+++ b/media/mca/filterfw/native/core/native_program.h
@@ -75,7 +75,8 @@
     // Pointer to user data
     void* user_data_;
 
-    DISALLOW_COPY_AND_ASSIGN(NativeProgram);
+    NativeProgram(const NativeProgram&) = delete;
+    NativeProgram& operator=(const NativeProgram&) = delete;
 };
 
 } // namespace filterfw
diff --git a/packages/CarrierDefaultApp/AndroidManifest.xml b/packages/CarrierDefaultApp/AndroidManifest.xml
index 2e642ec..8df194c 100644
--- a/packages/CarrierDefaultApp/AndroidManifest.xml
+++ b/packages/CarrierDefaultApp/AndroidManifest.xml
@@ -25,7 +25,6 @@
     <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
     <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
     <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" />
-    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
     <uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
 
     <application android:label="@string/app_name" >
@@ -34,10 +33,16 @@
                 <action android:name="com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED" />
             </intent-filter>
         </receiver>
-        <activity android:name="com.android.carrierdefaultapp.CaptivePortalLaunchActivity"
-            android:theme="@android:style/Theme.Translucent.NoTitleBar"
-            android:excludeFromRecents="true"/>
         <service android:name="com.android.carrierdefaultapp.ProvisionObserver"
                  android:permission="android.permission.BIND_JOB_SERVICE"/>
+        <activity
+            android:name="com.android.carrierdefaultapp.CaptivePortalLoginActivity"
+            android:label="@string/action_bar_label"
+            android:theme="@style/AppTheme"
+            android:configChanges="keyboardHidden|orientation|screenSize" >
+            <intent-filter>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/packages/CarrierDefaultApp/assets/quantum_ic_warning_amber_96.png b/packages/CarrierDefaultApp/assets/quantum_ic_warning_amber_96.png
new file mode 100644
index 0000000..08294ce
--- /dev/null
+++ b/packages/CarrierDefaultApp/assets/quantum_ic_warning_amber_96.png
Binary files differ
diff --git a/packages/CarrierDefaultApp/res/drawable/ic_sim_card.xml b/packages/CarrierDefaultApp/res/drawable/ic_sim_card.xml
index dc54fe2..75aa405 100644
--- a/packages/CarrierDefaultApp/res/drawable/ic_sim_card.xml
+++ b/packages/CarrierDefaultApp/res/drawable/ic_sim_card.xml
@@ -22,4 +22,4 @@
 <path
     android:fillColor="#757575"
     android:pathData="M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,4c0,-1.1 -0.9,-2 -2,-2zM13,17h-2v-2h2v2zM13,13h-2L11,8h2v5z"/>
-</vector>
\ No newline at end of file
+</vector>
diff --git a/packages/CarrierDefaultApp/res/layout/activity_captive_portal_login.xml b/packages/CarrierDefaultApp/res/layout/activity_captive_portal_login.xml
new file mode 100644
index 0000000..528576b
--- /dev/null
+++ b/packages/CarrierDefaultApp/res/layout/activity_captive_portal_login.xml
@@ -0,0 +1,34 @@
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="com.android.carrierdefaultapp.CaptivePortalLoginActivity"
+    tools:ignore="MergeRootFrame">
+    <LinearLayout
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <TextView
+        android:id="@+id/url_bar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="20sp"
+        android:singleLine="true" />
+
+    <ProgressBar
+        android:id="@+id/progress_bar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        style="?android:attr/progressBarStyleHorizontal" />
+
+    <WebView
+        android:id="@+id/webview"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_alignParentBottom="false"
+        android:layout_alignParentRight="false" />
+
+</LinearLayout>
+</FrameLayout>
diff --git a/packages/CarrierDefaultApp/res/values/dimens.xml b/packages/CarrierDefaultApp/res/values/dimens.xml
index a3c5049..1ea8c35 100644
--- a/packages/CarrierDefaultApp/res/values/dimens.xml
+++ b/packages/CarrierDefaultApp/res/values/dimens.xml
@@ -1,3 +1,6 @@
 <resources>
     <dimen name="glif_icon_size">32dp</dimen>
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
 </resources>
diff --git a/packages/CarrierDefaultApp/res/values/strings.xml b/packages/CarrierDefaultApp/res/values/strings.xml
index fe5669d..f9342b7 100644
--- a/packages/CarrierDefaultApp/res/values/strings.xml
+++ b/packages/CarrierDefaultApp/res/values/strings.xml
@@ -1,14 +1,13 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <string name="app_name">CarrierDefaultApp</string>
-    <string name="android_system_label">Android System</string>
+    <string name="android_system_label">Mobile Carrier</string>
     <string name="portal_notification_id">Mobile data has run out</string>
-    <string name="no_data_notification_id">No Mobile data service</string>
-    <string name="portal_notification_detail">Tap to add funds to your %s SIM</string>
+    <string name="no_data_notification_id">Your mobile data has been deactivated</string>
+    <string name="portal_notification_detail">Tap to visit the %s website</string>
     <string name="no_data_notification_detail">Please contact your service provider %s</string>
-    <string name="progress_dialogue_network_connection">Connecting to captive portal...</string>
-    <string name="alert_dialogue_network_timeout">Network timeout, would you like to retry?</string>
-    <string name="alert_dialogue_network_timeout_title">Network unavailable</string>
-    <string name="quit">Quit</string>
-    <string name="wait">Wait</string>
+    <string name="action_bar_label">Sign in to mobile network</string>
+    <string name="ssl_error_warning">The network you&#8217;re trying to join has security issues.</string>
+    <string name="ssl_error_example">For example, the login page may not belong to the organization shown.</string>
+    <string name="ssl_error_continue">Continue anyway via browser</string>
 </resources>
diff --git a/packages/CarrierDefaultApp/res/values/styles.xml b/packages/CarrierDefaultApp/res/values/styles.xml
index 3d26915..939c1aa 100644
--- a/packages/CarrierDefaultApp/res/values/styles.xml
+++ b/packages/CarrierDefaultApp/res/values/styles.xml
@@ -1,3 +1,16 @@
 <resources>
-    <style name="AlertDialog" parent="android:Theme.Material.Light.Dialog.Alert"/>
+    <style name="AppBaseTheme" parent="@android:style/Theme.Material.Settings">
+        <!--
+            Theme customizations available in newer API levels can go in
+            res/values-vXX/styles.xml, while customizations related to
+            backward-compatibility can go here.
+        -->
+    </style>
+
+    <!-- Application theme. -->
+    <style name="AppTheme" parent="AppBaseTheme">
+        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+        <!-- Setting's theme's accent color makes ProgressBar useless, reset back. -->
+        <item name="android:colorAccent">@*android:color/material_deep_teal_500</item>
+    </style>
 </resources>
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLaunchActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLaunchActivity.java
deleted file mode 100644
index 28251cb..0000000
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLaunchActivity.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.carrierdefaultapp;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.net.CaptivePortal;
-import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkRequest;
-import android.os.Bundle;
-import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
-import android.telephony.SubscriptionManager;
-import android.text.TextUtils;
-import android.net.ICaptivePortal;
-import android.view.ContextThemeWrapper;
-import android.view.WindowManager;
-import com.android.carrierdefaultapp.R;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.util.ArrayUtils;
-
-import static android.net.CaptivePortal.APP_RETURN_DISMISSED;
-
-/**
- * Activity that launches in response to the captive portal notification
- * @see com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_SHOW_PORTAL_NOTIFICATION
- * This activity requests network connection if there is no available one, launches the
- * {@link com.android.captiveportallogin portalApp} and keeps track of the portal activation result.
- */
-public class CaptivePortalLaunchActivity extends Activity {
-    private static final String TAG = CaptivePortalLaunchActivity.class.getSimpleName();
-    private static final boolean DBG = true;
-    public static final int NETWORK_REQUEST_TIMEOUT_IN_MS = 5 * 1000;
-
-    private ConnectivityManager mCm = null;
-    private ConnectivityManager.NetworkCallback mCb = null;
-    /* Progress dialogue when request network connection for captive portal */
-    private AlertDialog mProgressDialog = null;
-    /* Alert dialogue when network request is timeout */
-    private AlertDialog mAlertDialog = null;
-
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        mCm = ConnectivityManager.from(this);
-        // Check network connection before loading portal
-        Network network = getNetworkForCaptivePortal();
-        NetworkInfo nwInfo = mCm.getNetworkInfo(network);
-        if (nwInfo == null || !nwInfo.isConnected()) {
-            if (DBG) logd("Network unavailable, request restricted connection");
-            requestNetwork(getIntent());
-        } else {
-            launchCaptivePortal(getIntent(), network);
-        }
-    }
-
-    // show progress dialog during network connecting
-    private void showConnectingProgressDialog() {
-        mProgressDialog = new ProgressDialog(getApplicationContext());
-        mProgressDialog.setMessage(getString(R.string.progress_dialogue_network_connection));
-        mProgressDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
-        mProgressDialog.show();
-    }
-
-    // if network request is timeout, show alert dialog with two option: cancel & wait
-    private void showConnectionTimeoutAlertDialog() {
-        mAlertDialog = new AlertDialog.Builder(new ContextThemeWrapper(this, R.style.AlertDialog))
-                .setMessage(getString(R.string.alert_dialogue_network_timeout))
-                .setTitle(getString(R.string.alert_dialogue_network_timeout_title))
-                .setNegativeButton(getString(R.string.quit),
-                        new DialogInterface.OnClickListener() {
-                            @Override
-                            public void onClick(DialogInterface dialog, int which) {
-                                // cancel
-                                dismissDialog(mAlertDialog);
-                                finish();
-                            }
-                        })
-                .setPositiveButton(getString(R.string.wait),
-                        new DialogInterface.OnClickListener() {
-                            @Override
-                            public void onClick(DialogInterface dialog, int which) {
-                                // wait, request network again
-                                dismissDialog(mAlertDialog);
-                                requestNetwork(getIntent());
-                            }
-                        })
-                .create();
-        mAlertDialog.show();
-    }
-
-    private void requestNetwork(final Intent intent) {
-        NetworkRequest request = new NetworkRequest.Builder()
-                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
-                .build();
-
-        mCb = new ConnectivityManager.NetworkCallback() {
-            @Override
-            public void onAvailable(Network network) {
-                if (DBG) logd("Network available: " + network);
-                dismissDialog(mProgressDialog);
-                mCm.bindProcessToNetwork(network);
-                launchCaptivePortal(intent, network);
-            }
-
-            @Override
-            public void onUnavailable() {
-                if (DBG) logd("Network unavailable");
-                dismissDialog(mProgressDialog);
-                showConnectionTimeoutAlertDialog();
-            }
-        };
-        showConnectingProgressDialog();
-        mCm.requestNetwork(request, mCb, NETWORK_REQUEST_TIMEOUT_IN_MS);
-    }
-
-    private void releaseNetworkRequest() {
-        logd("release Network Request");
-        if (mCb != null) {
-            mCm.unregisterNetworkCallback(mCb);
-            mCb = null;
-        }
-    }
-
-    private void dismissDialog(AlertDialog dialog) {
-        if (dialog != null) {
-            dialog.dismiss();
-        }
-    }
-
-    private Network getNetworkForCaptivePortal() {
-        Network[] info = mCm.getAllNetworks();
-        if (!ArrayUtils.isEmpty(info)) {
-            for (Network nw : info) {
-                final NetworkCapabilities nc = mCm.getNetworkCapabilities(nw);
-                if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
-                        && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
-                    return nw;
-                }
-            }
-        }
-        return null;
-    }
-
-    private void launchCaptivePortal(final Intent intent, Network network) {
-        String redirectUrl = intent.getStringExtra(TelephonyIntents.EXTRA_REDIRECTION_URL_KEY);
-        int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
-                SubscriptionManager.getDefaultVoiceSubscriptionId());
-        if (TextUtils.isEmpty(redirectUrl) || !matchUrl(redirectUrl, subId)) {
-            loge("Launch portal fails due to incorrect redirection URL: " +
-                    Rlog.pii(TAG, redirectUrl));
-            return;
-        }
-        final Intent portalIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
-        portalIntent.putExtra(ConnectivityManager.EXTRA_NETWORK, network);
-        portalIntent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
-                new CaptivePortal(new ICaptivePortal.Stub() {
-                    @Override
-                    public void appResponse(int response) {
-                        logd("portal response code: " + response);
-                        releaseNetworkRequest();
-                        if (response == APP_RETURN_DISMISSED) {
-                            // Upon success http response code, trigger re-evaluation
-                            CarrierActionUtils.applyCarrierAction(
-                                    CarrierActionUtils.CARRIER_ACTION_ENABLE_RADIO, intent,
-                                    getApplicationContext());
-                            CarrierActionUtils.applyCarrierAction(
-                                    CarrierActionUtils.CARRIER_ACTION_ENABLE_METERED_APNS, intent,
-                                    getApplicationContext());
-                            CarrierActionUtils.applyCarrierAction(
-                                    CarrierActionUtils.CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS,
-                                    intent, getApplicationContext());
-                        }
-                    }
-                }));
-        portalIntent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL, redirectUrl);
-        portalIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-        if (DBG) logd("launching portal");
-        startActivity(portalIntent);
-        finish();
-    }
-
-    // match configured redirection url
-    private boolean matchUrl(String url, int subId) {
-        CarrierConfigManager configManager = getApplicationContext()
-                .getSystemService(CarrierConfigManager.class);
-        String[] redirectURLs = configManager.getConfigForSubId(subId).getStringArray(
-                CarrierConfigManager.KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY);
-        if (ArrayUtils.isEmpty(redirectURLs)) {
-            if (DBG) logd("match is unnecessary without any configured redirection url");
-            return true;
-        }
-        for (String redirectURL : redirectURLs) {
-            if (url.startsWith(redirectURL)) {
-                return true;
-            }
-        }
-        if (DBG) loge("no match found for configured redirection url");
-        return false;
-    }
-
-    private static void logd(String s) {
-        Rlog.d(TAG, s);
-    }
-
-    private static void loge(String s) {
-        Rlog.d(TAG, s);
-    }
-}
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
new file mode 100644
index 0000000..a5820f2
--- /dev/null
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.carrierdefaultapp;
+
+import android.app.Activity;
+import android.app.LoadedApk;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.Proxy;
+import android.net.TrafficStats;
+import android.net.Uri;
+import android.net.http.SslError;
+import android.os.Bundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.TypedValue;
+import android.webkit.SslErrorHandler;
+import android.webkit.WebChromeClient;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.ArrayUtils;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Random;
+
+/**
+ * Activity that launches in response to the captive portal notification
+ * @see com.android.carrierdefaultapp.CarrierActionUtils#CARRIER_ACTION_SHOW_PORTAL_NOTIFICATION
+ * This activity requests network connection if there is no available one before loading the real
+ * portal page and apply carrier actions on the portal activation result.
+ */
+public class CaptivePortalLoginActivity extends Activity {
+    private static final String TAG = CaptivePortalLoginActivity.class.getSimpleName();
+    private static final boolean DBG = true;
+
+    private static final int SOCKET_TIMEOUT_MS = 10 * 1000;
+    public static final int NETWORK_REQUEST_TIMEOUT_MS = 5 * 1000;
+
+    private URL mUrl;
+    private Network mNetwork;
+    private NetworkCallback mNetworkCallback;
+    private ConnectivityManager mCm;
+    private WebView mWebView;
+    private MyWebViewClient mWebViewClient;
+    private boolean mLaunchBrowser = false;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mCm = ConnectivityManager.from(this);
+        mUrl = getUrlForCaptivePortal();
+        if (mUrl == null) {
+            done(false);
+            return;
+        }
+        if (DBG) logd(String.format("onCreate for %s", mUrl.toString()));
+        setContentView(R.layout.activity_captive_portal_login);
+        getActionBar().setDisplayShowHomeEnabled(false);
+
+        mWebView = (WebView) findViewById(R.id.webview);
+        mWebView.clearCache(true);
+        WebSettings webSettings = mWebView.getSettings();
+        webSettings.setJavaScriptEnabled(true);
+        webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
+        mWebViewClient = new MyWebViewClient();
+        mWebView.setWebViewClient(mWebViewClient);
+        mWebView.setWebChromeClient(new MyWebChromeClient());
+
+        mNetwork = getNetworkForCaptivePortal();
+        if (mNetwork == null) {
+            requestNetworkForCaptivePortal();
+        } else {
+            mCm.bindProcessToNetwork(mNetwork);
+            // Start initial page load so WebView finishes loading proxy settings.
+            // Actual load of mUrl is initiated by MyWebViewClient.
+            mWebView.loadData("", "text/html", null);
+        }
+    }
+
+    @Override
+    public void onBackPressed() {
+        WebView myWebView = (WebView) findViewById(R.id.webview);
+        if (myWebView.canGoBack() && mWebViewClient.allowBack()) {
+            myWebView.goBack();
+        } else {
+            super.onBackPressed();
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        releaseNetworkRequest();
+        if (mLaunchBrowser) {
+            // Give time for this network to become default. After 500ms just proceed.
+            for (int i = 0; i < 5; i++) {
+                // TODO: This misses when mNetwork underlies a VPN.
+                if (mNetwork.equals(mCm.getActiveNetwork())) break;
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                }
+            }
+            final String url = mUrl.toString();
+            if (DBG) logd("starting activity with intent ACTION_VIEW for " + url);
+            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
+        }
+    }
+
+    // Find WebView's proxy BroadcastReceiver and prompt it to read proxy system properties.
+    private void setWebViewProxy() {
+        LoadedApk loadedApk = getApplication().mLoadedApk;
+        try {
+            Field receiversField = LoadedApk.class.getDeclaredField("mReceivers");
+            receiversField.setAccessible(true);
+            ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk);
+            for (Object receiverMap : receivers.values()) {
+                for (Object rec : ((ArrayMap) receiverMap).keySet()) {
+                    Class clazz = rec.getClass();
+                    if (clazz.getName().contains("ProxyChangeListener")) {
+                        Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class,
+                                Intent.class);
+                        Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
+                        onReceiveMethod.invoke(rec, getApplicationContext(), intent);
+                        Log.v(TAG, "Prompting WebView proxy reload.");
+                    }
+                }
+            }
+        } catch (Exception e) {
+            loge("Exception while setting WebView proxy: " + e);
+        }
+    }
+
+    private void done(boolean success) {
+        if (DBG) logd(String.format("Result success %b for %s", success, mUrl.toString()));
+        if (success) {
+            // Trigger re-evaluation upon success http response code
+            CarrierActionUtils.applyCarrierAction(
+                    CarrierActionUtils.CARRIER_ACTION_ENABLE_RADIO, getIntent(),
+                    getApplicationContext());
+            CarrierActionUtils.applyCarrierAction(
+                    CarrierActionUtils.CARRIER_ACTION_ENABLE_METERED_APNS, getIntent(),
+                    getApplicationContext());
+            CarrierActionUtils.applyCarrierAction(
+                    CarrierActionUtils.CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS, getIntent(),
+                    getApplicationContext());
+
+        }
+        finishAndRemoveTask();
+    }
+
+    private URL getUrlForCaptivePortal() {
+        String url = getIntent().getStringExtra(TelephonyIntents.EXTRA_REDIRECTION_URL_KEY);
+        if (url.isEmpty()) {
+            url = mCm.getCaptivePortalServerUrl();
+        }
+        final CarrierConfigManager configManager = getApplicationContext()
+                .getSystemService(CarrierConfigManager.class);
+        final int subId = getIntent().getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+                SubscriptionManager.getDefaultVoiceSubscriptionId());
+        final String[] portalURLs = configManager.getConfigForSubId(subId).getStringArray(
+                CarrierConfigManager.KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY);
+        if (!ArrayUtils.isEmpty(portalURLs)) {
+            for (String portalUrl : portalURLs) {
+                if (url.startsWith(portalUrl)) {
+                    break;
+                }
+            }
+            url = null;
+        }
+        try {
+            return new URL(url);
+        } catch (MalformedURLException e) {
+            loge("Invalid captive portal URL " + url);
+        }
+        return null;
+    }
+
+    private void testForCaptivePortal() {
+        new Thread(new Runnable() {
+            public void run() {
+                // Give time for captive portal to open.
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                }
+                HttpURLConnection urlConnection = null;
+                int httpResponseCode = 500;
+                int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE);
+                try {
+                    urlConnection = (HttpURLConnection) mNetwork.openConnection(mUrl);
+                    urlConnection.setInstanceFollowRedirects(false);
+                    urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
+                    urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
+                    urlConnection.setUseCaches(false);
+                    urlConnection.getInputStream();
+                    httpResponseCode = urlConnection.getResponseCode();
+                } catch (IOException e) {
+                } finally {
+                    if (urlConnection != null) urlConnection.disconnect();
+                    TrafficStats.setThreadStatsTag(oldTag);
+                }
+                if (httpResponseCode == 204) {
+                    done(true);
+                }
+            }
+        }).start();
+    }
+
+    private Network getNetworkForCaptivePortal() {
+        Network[] info = mCm.getAllNetworks();
+        if (!ArrayUtils.isEmpty(info)) {
+            for (Network nw : info) {
+                final NetworkCapabilities nc = mCm.getNetworkCapabilities(nw);
+                if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
+                        && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+                    return nw;
+                }
+            }
+        }
+        return null;
+    }
+
+    private void requestNetworkForCaptivePortal() {
+        NetworkRequest request = new NetworkRequest.Builder()
+                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+                .build();
+
+        mNetworkCallback = new ConnectivityManager.NetworkCallback() {
+            @Override
+            public void onAvailable(Network network) {
+                if (DBG) logd("Network available: " + network);
+                mCm.bindProcessToNetwork(network);
+                mNetwork = network;
+                runOnUiThreadIfNotFinishing(() -> {
+                    // Start initial page load so WebView finishes loading proxy settings.
+                    // Actual load of mUrl is initiated by MyWebViewClient.
+                    mWebView.loadData("", "text/html", null);
+                });
+            }
+
+            @Override
+            public void onUnavailable() {
+                if (DBG) logd("Network unavailable");
+                runOnUiThreadIfNotFinishing(() -> {
+                    // Instead of not loading anything in webview, simply load the page and return
+                    // HTTP error page in the absence of network connection.
+                    mWebView.loadUrl(mUrl.toString());
+                });
+            }
+        };
+        logd("request Network for captive portal");
+        mCm.requestNetwork(request, mNetworkCallback, NETWORK_REQUEST_TIMEOUT_MS);
+    }
+
+    private void releaseNetworkRequest() {
+        logd("release Network for captive portal");
+        if (mNetworkCallback != null) {
+            mCm.unregisterNetworkCallback(mNetworkCallback);
+            mNetworkCallback = null;
+            mNetwork = null;
+        }
+    }
+
+    private class MyWebViewClient extends WebViewClient {
+        private static final String INTERNAL_ASSETS = "file:///android_asset/";
+        private final String mBrowserBailOutToken = Long.toString(new Random().nextLong());
+        // How many Android device-independent-pixels per scaled-pixel
+        // dp/sp = (px/sp) / (px/dp) = (1/sp) / (1/dp)
+        private final float mDpPerSp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 1,
+                    getResources().getDisplayMetrics())
+                / TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
+                    getResources().getDisplayMetrics());
+        private int mPagesLoaded;
+
+        // If we haven't finished cleaning up the history, don't allow going back.
+        public boolean allowBack() {
+            return mPagesLoaded > 1;
+        }
+
+        @Override
+        public void onPageStarted(WebView view, String url, Bitmap favicon) {
+            if (url.contains(mBrowserBailOutToken)) {
+                mLaunchBrowser = true;
+                done(false);
+                return;
+            }
+            // The first page load is used only to cause the WebView to
+            // fetch the proxy settings.  Don't update the URL bar, and
+            // don't check if the captive portal is still there.
+            if (mPagesLoaded == 0) return;
+            // For internally generated pages, leave URL bar listing prior URL as this is the URL
+            // the page refers to.
+            if (!url.startsWith(INTERNAL_ASSETS)) {
+                final TextView myUrlBar = (TextView) findViewById(R.id.url_bar);
+                myUrlBar.setText(url);
+            }
+            if (mNetwork != null) {
+                testForCaptivePortal();
+            }
+        }
+
+        @Override
+        public void onPageFinished(WebView view, String url) {
+            mPagesLoaded++;
+            if (mPagesLoaded == 1) {
+                // Now that WebView has loaded at least one page we know it has read in the proxy
+                // settings.  Now prompt the WebView read the Network-specific proxy settings.
+                setWebViewProxy();
+                // Load the real page.
+                view.loadUrl(mUrl.toString());
+                return;
+            } else if (mPagesLoaded == 2) {
+                // Prevent going back to empty first page.
+                view.clearHistory();
+            }
+            if (mNetwork != null) {
+                testForCaptivePortal();
+            }
+        }
+
+        // Convert Android device-independent-pixels (dp) to HTML size.
+        private String dp(int dp) {
+            // HTML px's are scaled just like dp's, so just add "px" suffix.
+            return Integer.toString(dp) + "px";
+        }
+
+        // Convert Android scaled-pixels (sp) to HTML size.
+        private String sp(int sp) {
+            // Convert sp to dp's.
+            float dp = sp * mDpPerSp;
+            // Apply a scale factor to make things look right.
+            dp *= 1.3;
+            // Convert dp's to HTML size.
+            return dp((int) dp);
+        }
+
+        // A web page consisting of a large broken lock icon to indicate SSL failure.
+        private final String SSL_ERROR_HTML = "<html><head><style>"
+                + "body { margin-left:" + dp(48) + "; margin-right:" + dp(48) + "; "
+                + "margin-top:" + dp(96) + "; background-color:#fafafa; }"
+                + "img { width:" + dp(48) + "; height:" + dp(48) + "; }"
+                + "div.warn { font-size:" + sp(16) + "; margin-top:" + dp(16) + "; "
+                + "           opacity:0.87; line-height:1.28; }"
+                + "div.example { font-size:" + sp(14) + "; margin-top:" + dp(16) + "; "
+                + "              opacity:0.54; line-height:1.21905; }"
+                + "a { font-size:" + sp(14) + "; text-decoration:none; text-transform:uppercase; "
+                + "    margin-top:" + dp(24) + "; display:inline-block; color:#4285F4; "
+                + "    height:" + dp(48) + "; font-weight:bold; }"
+                + "</style></head><body><p><img src=quantum_ic_warning_amber_96.png><br>"
+                + "<div class=warn>%s</div>"
+                + "<div class=example>%s</div>" + "<a href=%s>%s</a></body></html>";
+
+        @Override
+        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
+            Log.w(TAG, "SSL error (error: " + error.getPrimaryError() + " host: "
+                    // Only show host to avoid leaking private info.
+                    + Uri.parse(error.getUrl()).getHost() + " certificate: "
+                    + error.getCertificate() + "); displaying SSL warning.");
+            final String html = String.format(SSL_ERROR_HTML, getString(R.string.ssl_error_warning),
+                    getString(R.string.ssl_error_example), mBrowserBailOutToken,
+                    getString(R.string.ssl_error_continue));
+            view.loadDataWithBaseURL(INTERNAL_ASSETS, html, "text/HTML", "UTF-8", null);
+        }
+
+        @Override
+        public boolean shouldOverrideUrlLoading(WebView view, String url) {
+            if (url.startsWith("tel:")) {
+                startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(url)));
+                return true;
+            }
+            return false;
+        }
+    }
+
+    private class MyWebChromeClient extends WebChromeClient {
+        @Override
+        public void onProgressChanged(WebView view, int newProgress) {
+            final ProgressBar myProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
+            myProgressBar.setProgress(newProgress);
+        }
+    }
+
+    private void runOnUiThreadIfNotFinishing(Runnable r) {
+        if (!isFinishing()) {
+            runOnUiThread(r);
+        }
+    }
+
+    private static void logd(String s) {
+        Rlog.d(TAG, s);
+    }
+
+    private static void loge(String s) {
+        Rlog.d(TAG, s);
+    }
+
+}
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
index d9bd2fc..73ff3a9 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
@@ -112,8 +112,10 @@
         logd("onShowCaptivePortalNotification");
         final NotificationManager notificationMgr = context.getSystemService(
                 NotificationManager.class);
-        Intent portalIntent = new Intent(context, CaptivePortalLaunchActivity.class);
+        Intent portalIntent = new Intent(context, CaptivePortalLoginActivity.class);
         portalIntent.putExtras(intent);
+        portalIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT
+                | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
         PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, portalIntent,
                 PendingIntent.FLAG_UPDATE_CURRENT);
         Notification notification = getNotification(context, R.string.portal_notification_id,
diff --git a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/LaunchCaptivePortalActivityTest.java b/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/LaunchCaptivePortalActivityTest.java
deleted file mode 100644
index 8a18d72..0000000
--- a/packages/CarrierDefaultApp/tests/unit/src/com/android/carrierdefaultapp/LaunchCaptivePortalActivityTest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package com.android.carrierdefaultapp;
-
-import android.annotation.TargetApi;
-import android.content.Intent;
-import android.net.ConnectivityManager;
-import android.net.Network;
-import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkRequest;
-
-import com.android.internal.telephony.TelephonyIntents;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-public class LaunchCaptivePortalActivityTest extends
-        CarrierDefaultActivityTestCase<CaptivePortalLaunchActivity> {
-
-    @Mock
-    private ConnectivityManager mCm;
-    @Mock
-    private NetworkInfo mNetworkInfo;
-    @Mock
-    private Network mNetwork;
-
-    @Captor
-    private ArgumentCaptor<Integer> mInt;
-    @Captor
-    private ArgumentCaptor<NetworkRequest> mNetworkReq;
-
-    private NetworkCapabilities mNetworkCapabilities;
-
-    public LaunchCaptivePortalActivityTest() {
-        super(CaptivePortalLaunchActivity.class);
-    }
-
-    @Before
-    public void setUp() throws Exception {
-        super.setUp();
-        injectSystemService(ConnectivityManager.class, mCm);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        super.tearDown();
-    }
-
-    @Override
-    protected Intent createActivityIntent() {
-        Intent intent = new Intent(getInstrumentation().getTargetContext(),
-                CaptivePortalLaunchActivity.class);
-        intent.putExtra(TelephonyIntents.EXTRA_REDIRECTION_URL_KEY, "url");
-        return intent;
-    }
-
-    @Test
-    public void testWithoutInternetConnection() throws Throwable {
-        startActivity();
-        TestContext.waitForMs(100);
-        verify(mCm, atLeast(1)).requestNetwork(mNetworkReq.capture(), any(), mInt.capture());
-        // verify network request
-        assert(mNetworkReq.getValue().networkCapabilities.hasCapability(
-                NetworkCapabilities.NET_CAPABILITY_INTERNET));
-        assert(mNetworkReq.getValue().networkCapabilities.hasTransport(
-                NetworkCapabilities.TRANSPORT_CELLULAR));
-        assertFalse(mNetworkReq.getValue().networkCapabilities.hasCapability(
-                NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED));
-        assertEquals(CaptivePortalLaunchActivity.NETWORK_REQUEST_TIMEOUT_IN_MS,
-                (int) mInt.getValue());
-        // verify captive portal app is not launched due to unavailable network
-        assertNull(getStartedActivityIntent());
-        stopActivity();
-    }
-
-    @Test
-    public void testWithInternetConnection() throws Throwable {
-        // Mock internet connection
-        mNetworkCapabilities = new NetworkCapabilities()
-                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
-                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
-        doReturn(new Network[]{mNetwork}).when(mCm).getAllNetworks();
-        doReturn(mNetworkCapabilities).when(mCm).getNetworkCapabilities(eq(mNetwork));
-        doReturn(mNetworkInfo).when(mCm).getNetworkInfo(eq(mNetwork));
-        doReturn(true).when(mNetworkInfo).isConnected();
-
-        startActivity();
-        TestContext.waitForMs(100);
-        // verify there is no network request with internet connection
-        verify(mCm, times(0)).requestNetwork(any(), any(), anyInt());
-        // verify captive portal app is launched
-        assertNotNull(getStartedActivityIntent());
-        assertEquals(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN,
-                getStartedActivityIntent().getAction());
-        stopActivity();
-    }
-}
diff --git a/packages/MtpDocumentsProvider/tests/Android.mk b/packages/MtpDocumentsProvider/tests/Android.mk
index e50d6fb..148cd0d 100644
--- a/packages/MtpDocumentsProvider/tests/Android.mk
+++ b/packages/MtpDocumentsProvider/tests/Android.mk
@@ -8,5 +8,6 @@
 LOCAL_PACKAGE_NAME := MtpDocumentsProviderTests
 LOCAL_INSTRUMENTATION_FOR := MtpDocumentsProvider
 LOCAL_CERTIFICATE := media
+LOCAL_COMPATIBILITY_SUITE := device-tests
 
 include $(BUILD_PACKAGE)
diff --git a/packages/PrintSpooler/tests/outofprocess/Android.mk b/packages/PrintSpooler/tests/outofprocess/Android.mk
index d1d0ee4..3c02453 100644
--- a/packages/PrintSpooler/tests/outofprocess/Android.mk
+++ b/packages/PrintSpooler/tests/outofprocess/Android.mk
@@ -24,5 +24,6 @@
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator mockito-target-minus-junit4
 
 LOCAL_PACKAGE_NAME := PrintSpoolerOutOfProcessTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
 
 include $(BUILD_PACKAGE)
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index f77d466..c64574f 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -422,11 +422,11 @@
     <!-- Setting Checkbox title whether to enable WiFi Verbose Logging. [CHAR LIMIT=40] -->
     <string name="wifi_verbose_logging">Enable Wi\u2011Fi Verbose Logging</string>
     <!-- Setting Checkbox title whether to enable WiFi Aggressive Handover. [CHAR LIMIT=40] -->
-    <string name="wifi_aggressive_handover">Aggressive Wi\u2011Fi to Cellular handover</string>
+    <string name="wifi_aggressive_handover">Aggressive Wi\u2011Fi to mobile handover</string>
     <!-- Setting Checkbox title whether to enable WiFi Scanning in the presence of traffic. [CHAR LIMIT=80] -->
     <string name="wifi_allow_scan_with_traffic">Always allow Wi\u2011Fi Roam Scans</string>
-    <!-- Setting Checkbox title whether to always keep cellular data active. [CHAR LIMIT=80] -->
-    <string name="mobile_data_always_on">Cellular data always active</string>
+    <!-- Setting Checkbox title whether to always keep mobile data active. [CHAR LIMIT=80] -->
+    <string name="mobile_data_always_on">Mobile data always active</string>
     <!-- Setting Checkbox title for disabling Bluetooth absolute volume -->
     <string name="bluetooth_disable_absolute_volume">Disable absolute volume</string>
 
@@ -463,7 +463,7 @@
     <!-- Setting Checkbox summary whether to enable Wifi verbose Logging [CHAR LIMIT=80] -->
     <string name="wifi_verbose_logging_summary">Increase Wi\u2011Fi logging level, show per SSID RSSI in Wi\u2011Fi Picker</string>
     <!-- Setting Checkbox summary whether to enable Wifi aggressive handover [CHAR LIMIT=130] -->
-    <string name="wifi_aggressive_handover_summary">When enabled, Wi\u2011Fi will be more aggressive in handing over the data connection to Cellular, when Wi\u2011Fi signal is low</string>
+    <string name="wifi_aggressive_handover_summary">When enabled, Wi\u2011Fi will be more aggressive in handing over the data connection to mobile, when Wi\u2011Fi signal is low</string>
     <!-- Setting Checkbox summary whether to always allow WiFi Roam Scans [CHAR LIMIT=130] -->
     <string name="wifi_allow_scan_with_traffic_summary">Allow/Disallow Wi\u2011Fi Roam Scans based on the amount of data traffic present at the interface</string>
     <!-- UI debug setting: limit size of Android logger buffers -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
index fa5ba73..ee7885d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
@@ -132,7 +132,7 @@
                 mCategoryByKeyMap.put(category.key, category);
             }
             backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
-            normalizePriority(context, mCategoryByKeyMap);
+            sortCategories(context, mCategoryByKeyMap);
             filterDuplicateTiles(mCategoryByKeyMap);
         }
     }
@@ -188,17 +188,17 @@
     }
 
     /**
-     * Normalize priority values on tiles across injected from all apps to make sure they don't set
-     * the same priority value. However internal tiles' priority remains unchanged.
+     * Sort the tiles injected from all apps such that if they have the same priority value,
+     * they wil lbe sorted by package name.
      * <p/>
-     * A list of tiles are considered normalized when their priority value increases in a linear
+     * A list of tiles are considered sorted when their priority value decreases in a linear
      * scan.
      */
     @VisibleForTesting
-    synchronized void normalizePriority(Context context,
+    synchronized void sortCategories(Context context,
             Map<String, DashboardCategory> categoryByKeyMap) {
         for (Entry<String, DashboardCategory> categoryEntry : categoryByKeyMap.entrySet()) {
-            normalizePriorityForExternalTiles(context, categoryEntry.getValue());
+            sortCategoriesForExternalTiles(context, categoryEntry.getValue());
         }
     }
 
@@ -228,39 +228,34 @@
     }
 
     /**
-     * Normalize priority value for tiles within a single {@code DashboardCategory}.
+     * Sort priority value for tiles within a single {@code DashboardCategory}.
      *
-     * @see #normalizePriority(Context, Map)
+     * @see #sortCategories(Context, Map)
      */
-    private synchronized void normalizePriorityForExternalTiles(Context context,
+    private synchronized void sortCategoriesForExternalTiles(Context context,
             DashboardCategory dashboardCategory) {
         final String skipPackageName = context.getPackageName();
 
-        // Sort tiles based on [package, priority within package]
+        // Sort tiles based on [priority, package within priority]
         Collections.sort(dashboardCategory.tiles, (tile1, tile2) -> {
             final String package1 = tile1.intent.getComponent().getPackageName();
             final String package2 = tile2.intent.getComponent().getPackageName();
             final int packageCompare = CASE_INSENSITIVE_ORDER.compare(package1, package2);
-            // First sort by package name
+            // First sort by priority
+            final int priorityCompare = tile2.priority - tile1.priority;
+            if (priorityCompare != 0) {
+                return priorityCompare;
+            }
+            // Then sort by package name, skip package take precedence
             if (packageCompare != 0) {
-                return packageCompare;
-            } else if (TextUtils.equals(package1, skipPackageName)) {
-                return 0;
+                if (TextUtils.equals(package1, skipPackageName)) {
+                    return -1;
+                }
+                if (TextUtils.equals(package2, skipPackageName)) {
+                    return 1;
+                }
             }
-            // Then sort by priority
-            return tile1.priority - tile2.priority;
+            return packageCompare;
         });
-        // Update priority for all items so no package define the same priority value.
-        final int count = dashboardCategory.tiles.size();
-        for (int i = 0; i < count; i++) {
-            final String packageName =
-                    dashboardCategory.tiles.get(i).intent.getComponent().getPackageName();
-            if (TextUtils.equals(packageName, skipPackageName)) {
-                // We skip this tile because it's a intent pointing to our own app. We trust the
-                // priority is set correctly, so don't normalize.
-                continue;
-            }
-            dashboardCategory.tiles.get(i).priority = i;
-        }
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 6fe581e..48f3e2a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -47,6 +47,7 @@
 import android.text.style.TtsSpan;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.R;
 
 import java.util.ArrayList;
@@ -112,9 +113,14 @@
     private static final int PSK_WPA2 = 2;
     private static final int PSK_WPA_WPA2 = 3;
 
-    public static final int SIGNAL_LEVELS = 4;
+    /**
+     * The number of distinct wifi levels.
+     *
+     * <p>Must keep in sync with {@link R.array.wifi_signal} and {@link WifiManager#RSSI_LEVELS}.
+     */
+    public static final int SIGNAL_LEVELS = 5;
 
-    static final int UNREACHABLE_RSSI = Integer.MAX_VALUE;
+    public static final int UNREACHABLE_RSSI = Integer.MIN_VALUE;
 
     private final Context mContext;
 
@@ -170,8 +176,8 @@
             }
         }
         update(mConfig, mInfo, mNetworkInfo);
-        mRssi = getRssi();
-        mSeen = getSeen();
+        updateRssi();
+        updateSeen();
         mId = sLastId.incrementAndGet();
     }
 
@@ -369,27 +375,57 @@
         return mInfo;
     }
 
+    /**
+     * Returns the number of levels to show for a Wifi icon, from 0 to {@link #SIGNAL_LEVELS}-1.
+     *
+     * <p>Use {@#isReachable()} to determine if an AccessPoint is in range, as this method will
+     * always return at least 0.
+     */
     public int getLevel() {
-        if (!isReachable()) {
-            return -1;
-        }
         return WifiManager.calculateSignalLevel(mRssi, SIGNAL_LEVELS);
     }
 
     public int getRssi() {
+        return mRssi;
+    }
+
+    /**
+     * Updates {@link #mRssi}.
+     *
+     * <p>If the given connection is active, the existing value of {@link #mRssi} will be returned.
+     * If the given AccessPoint is not active, a value will be calculated from previous scan
+     * results, returning the best RSSI for all matching AccessPoints. If the access point is not
+     * connected and there are no scan results, the rssi will be set to {@link #UNREACHABLE_RSSI}.
+     *
+     * <p>Old scan results will be evicted from the cache when this method is invoked.
+     */
+    private void updateRssi() {
         evictOldScanResults();
-        int rssi = Integer.MIN_VALUE;
+
+        if (this.isActive()) {
+            return;
+        }
+
+        int rssi = UNREACHABLE_RSSI;
         for (ScanResult result : mScanResultCache.values()) {
             if (result.level > rssi) {
                 rssi = result.level;
             }
         }
 
-        return rssi;
+        mRssi = rssi;
     }
 
-    public long getSeen() {
+    /**
+     * Updates {@link #mSeen} based on the scan result cache.
+     *
+     * <p>Old scan results will be evicted from the cache when this method is invoked.
+     */
+    private void updateSeen() {
         evictOldScanResults();
+
+        // TODO(sghuman): Set to now if connected
+
         long seen = 0;
         for (ScanResult result : mScanResultCache.values()) {
             if (result.timestamp > seen) {
@@ -397,7 +433,7 @@
             }
         }
 
-        return seen;
+        mSeen = seen;
     }
 
     public NetworkInfo getNetworkInfo() {
@@ -822,13 +858,12 @@
 
     boolean update(ScanResult result) {
         if (matches(result)) {
+            int oldLevel = getLevel();
+
             /* Add or update the scan result for the BSSID */
             mScanResultCache.put(result.BSSID, result);
-
-            int oldLevel = getLevel();
-            int oldRssi = getRssi();
-            mSeen = getSeen();
-            mRssi = (getRssi() + oldRssi)/2;
+            updateSeen();
+            updateRssi();
             int newLevel = getLevel();
 
             if (newLevel > 0 && newLevel != oldLevel && mAccessPointListener != null) {
@@ -848,7 +883,7 @@
         return false;
     }
 
-    boolean update(WifiConfiguration config, WifiInfo info, NetworkInfo networkInfo) {
+    public boolean update(WifiConfiguration config, WifiInfo info, NetworkInfo networkInfo) {
         boolean reorder = false;
         if (info != null && isInfoForThisAccessPoint(config, info)) {
             reorder = (mInfo == null);
@@ -877,10 +912,16 @@
         }
     }
 
+    @VisibleForTesting
     void setRssi(int rssi) {
         mRssi = rssi;
     }
 
+    /** Sets the rssi to {@link #UNREACHABLE_RSSI}. */
+    void setUnreachable() {
+        setRssi(AccessPoint.UNREACHABLE_RSSI);
+    }
+
     int getRankingScore() {
         return mRankingScore;
     }
@@ -890,7 +931,7 @@
     }
 
     /** Return true if the current RSSI is reachable, and false otherwise. */
-    boolean isReachable() {
+    public boolean isReachable() {
         return mRssi != UNREACHABLE_RSSI;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index 50972c7..c9fa017 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -223,8 +223,7 @@
         }
 
         final Context context = getContext();
-        int level = WifiManager.calculateSignalLevel(
-                mAccessPoint.getRssi(), WifiManager.RSSI_LEVELS);
+        int level = mAccessPoint.getLevel();
         int wifiBadge = mAccessPoint.getBadge();
         if (level != mLevel || wifiBadge != mWifiBadge) {
             mLevel = level;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 8421c2c..55c886e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -471,7 +471,8 @@
                     accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
                 }
                 if (mIncludeSaved) {
-                    // If saved network not present in scan result then set its Rssi to MAX_VALUE
+                    // If saved network not present in scan result then set its Rssi to
+                    // UNREACHABLE_RSSI
                     boolean apFound = false;
                     for (ScanResult result : results) {
                         if (result.SSID.equals(accessPoint.getSsidStr())) {
@@ -480,7 +481,7 @@
                         }
                     }
                     if (!apFound) {
-                        accessPoint.setRssi(Integer.MAX_VALUE);
+                        accessPoint.setUnreachable();
                     }
                     accessPoints.add(accessPoint);
                     apMap.put(accessPoint.getSsidStr(), accessPoint);
diff --git a/packages/SettingsLib/tests/integ/Android.mk b/packages/SettingsLib/tests/integ/Android.mk
index 60d1c77..7ace048 100644
--- a/packages/SettingsLib/tests/integ/Android.mk
+++ b/packages/SettingsLib/tests/integ/Android.mk
@@ -25,6 +25,7 @@
 LOCAL_JACK_FLAGS := --multi-dex native
 
 LOCAL_PACKAGE_NAME := SettingsLibTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-test \
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index ec0190c..e8a58c1 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -89,21 +89,10 @@
 
     @Test
     public void testThatCopyAccessPoint_scanCacheShouldMatch() {
-        Bundle bundle = new Bundle();
-        ArrayList<ScanResult> scanResults = new ArrayList<>();
-        for (int i = 0; i < 5; i++) {
-            ScanResult scanResult = new ScanResult();
-            scanResult.level = i;
-            scanResult.BSSID = "bssid-" + i;
-            scanResult.timestamp = SystemClock.elapsedRealtime() * 1000;
-            scanResults.add(scanResult);
-        }
-
-        bundle.putParcelableArrayList("key_scanresultcache", scanResults);
-        AccessPoint original = new AccessPoint(mContext, bundle);
+        AccessPoint original = createAccessPointWithScanResultCache();
         assertThat(original.getRssi()).isEqualTo(4);
         AccessPoint copy = new AccessPoint(mContext, createWifiConfiguration());
-        assertThat(copy.getRssi()).isEqualTo(Integer.MIN_VALUE);
+        assertThat(copy.getRssi()).isEqualTo(AccessPoint.UNREACHABLE_RSSI);
         copy.copyFrom(original);
         assertThat(original.getRssi()).isEqualTo(copy.getRssi());
     }
@@ -190,6 +179,37 @@
         assertThat(points.indexOf(firstName)).isLessThan(points.indexOf(lastname));
     }
 
+    @Test
+    public void testRssiIsSetFromScanResults() {
+        AccessPoint ap = createAccessPointWithScanResultCache();
+        int originalRssi = ap.getRssi();
+        assertThat(originalRssi).isNotEqualTo(AccessPoint.UNREACHABLE_RSSI);
+    }
+
+    @Test
+    public void testGetRssiShouldReturnSetRssiValue() {
+        AccessPoint ap = createAccessPointWithScanResultCache();
+        int originalRssi = ap.getRssi();
+        int newRssi = originalRssi - 10;
+        ap.setRssi(newRssi);
+        assertThat(ap.getRssi()).isEqualTo(newRssi);
+    }
+
+    private AccessPoint createAccessPointWithScanResultCache() {
+        Bundle bundle = new Bundle();
+        ArrayList<ScanResult> scanResults = new ArrayList<>();
+        for (int i = 0; i < 5; i++) {
+            ScanResult scanResult = new ScanResult();
+            scanResult.level = i;
+            scanResult.BSSID = "bssid-" + i;
+            scanResult.timestamp = SystemClock.elapsedRealtime() * 1000;
+            scanResults.add(scanResult);
+        }
+
+        bundle.putParcelableArrayList("key_scanresultcache", scanResults);
+        return new AccessPoint(mContext, bundle);
+    }
+
     private WifiConfiguration createWifiConfiguration() {
         WifiConfiguration configuration = new WifiConfiguration();
         configuration.BSSID = "bssid";
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
index 434241d..8d61338 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/CategoryManagerTest.java
@@ -120,7 +120,7 @@
     }
 
     @Test
-    public void normalizePriority_singlePackage_shouldReorderBasedOnPriority() {
+    public void sortCategories_singlePackage_shouldReorderBasedOnPriority() {
         // Create some fake tiles that are not sorted.
         final String testPackage = "com.android.test";
         final DashboardCategory category = new DashboardCategory();
@@ -141,22 +141,18 @@
         category.tiles.add(tile3);
         mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
 
-        // Normalize their priorities
-        mCategoryManager.normalizePriority(ShadowApplication.getInstance().getApplicationContext(),
+        // Sort their priorities
+        mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
                 mCategoryByKeyMap);
 
         // Verify they are now sorted.
-        assertThat(category.tiles.get(0)).isSameAs(tile2);
+        assertThat(category.tiles.get(0)).isSameAs(tile3);
         assertThat(category.tiles.get(1)).isSameAs(tile1);
-        assertThat(category.tiles.get(2)).isSameAs(tile3);
-        // Verify their priority is normalized
-        assertThat(category.tiles.get(0).priority).isEqualTo(0);
-        assertThat(category.tiles.get(1).priority).isEqualTo(1);
-        assertThat(category.tiles.get(2).priority).isEqualTo(2);
+        assertThat(category.tiles.get(2)).isSameAs(tile2);
     }
 
     @Test
-    public void normalizePriority_multiPackage_shouldReorderBasedOnPackageAndPriority() {
+    public void sortCategories_multiPackage_shouldReorderBasedOnPackageAndPriority() {
         // Create some fake tiles that are not sorted.
         final String testPackage1 = "com.android.test1";
         final String testPackage2 = "com.android.test2";
@@ -178,22 +174,18 @@
         category.tiles.add(tile3);
         mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
 
-        // Normalize their priorities
-        mCategoryManager.normalizePriority(ShadowApplication.getInstance().getApplicationContext(),
+        // Sort their priorities
+        mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
                 mCategoryByKeyMap);
 
         // Verify they are now sorted.
-        assertThat(category.tiles.get(0)).isSameAs(tile3);
-        assertThat(category.tiles.get(1)).isSameAs(tile2);
-        assertThat(category.tiles.get(2)).isSameAs(tile1);
-        // Verify their priority is normalized
-        assertThat(category.tiles.get(0).priority).isEqualTo(0);
-        assertThat(category.tiles.get(1).priority).isEqualTo(1);
-        assertThat(category.tiles.get(2).priority).isEqualTo(2);
+        assertThat(category.tiles.get(0)).isSameAs(tile2);
+        assertThat(category.tiles.get(1)).isSameAs(tile1);
+        assertThat(category.tiles.get(2)).isSameAs(tile3);
     }
 
     @Test
-    public void normalizePriority_internalPackageTiles_shouldSkipTileForInternalPackage() {
+    public void sortCategories_internalPackageTiles_shouldSkipTileForInternalPackage() {
         // Create some fake tiles that are not sorted.
         final String testPackage =
                 ShadowApplication.getInstance().getApplicationContext().getPackageName();
@@ -215,18 +207,82 @@
         category.tiles.add(tile3);
         mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
 
-        // Normalize their priorities
-        mCategoryManager.normalizePriority(ShadowApplication.getInstance().getApplicationContext(),
+        // Sort their priorities
+        mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
                 mCategoryByKeyMap);
 
         // Verify the sorting order is not changed
         assertThat(category.tiles.get(0)).isSameAs(tile1);
         assertThat(category.tiles.get(1)).isSameAs(tile2);
         assertThat(category.tiles.get(2)).isSameAs(tile3);
-        // Verify their priorities are not changed.
-        assertThat(category.tiles.get(0).priority).isEqualTo(100);
-        assertThat(category.tiles.get(1).priority).isEqualTo(100);
-        assertThat(category.tiles.get(2).priority).isEqualTo(50);
+    }
+
+    @Test
+    public void sortCategories_internalAndExternalPackageTiles_shouldRetainPriorityOrdering() {
+        // Inject one external tile among internal tiles.
+        final String testPackage =
+            ShadowApplication.getInstance().getApplicationContext().getPackageName();
+        final String testPackage2 = "com.google.test2";
+        final DashboardCategory category = new DashboardCategory();
+        final Tile tile1 = new Tile();
+        tile1.intent = new Intent().setComponent(new ComponentName(testPackage, "class1"));
+        tile1.priority = 2;
+        final Tile tile2 = new Tile();
+        tile2.intent = new Intent().setComponent(new ComponentName(testPackage, "class2"));
+        tile2.priority = 1;
+        final Tile tile3 = new Tile();
+        tile3.intent = new Intent().setComponent(new ComponentName(testPackage2, "class0"));
+        tile3.priority = 0;
+        final Tile tile4 = new Tile();
+        tile4.intent = new Intent().setComponent(new ComponentName(testPackage, "class3"));
+        tile4.priority = -1;
+        category.tiles.add(tile1);
+        category.tiles.add(tile2);
+        category.tiles.add(tile3);
+        category.tiles.add(tile4);
+        mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
+
+        // Sort their priorities
+        mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
+            mCategoryByKeyMap);
+
+        // Verify the sorting order is not changed
+        assertThat(category.tiles.get(0)).isSameAs(tile1);
+        assertThat(category.tiles.get(1)).isSameAs(tile2);
+        assertThat(category.tiles.get(2)).isSameAs(tile3);
+        assertThat(category.tiles.get(3)).isSameAs(tile4);
+    }
+
+    @Test
+    public void sortCategories_samePriority_internalPackageTileShouldTakePrecedence() {
+        // Inject one external tile among internal tiles with same priority.
+        final String testPackage =
+            ShadowApplication.getInstance().getApplicationContext().getPackageName();
+        final String testPackage2 = "com.google.test2";
+        final String testPackage3 = "com.abcde.test3";
+        final DashboardCategory category = new DashboardCategory();
+        final Tile tile1 = new Tile();
+        tile1.intent = new Intent().setComponent(new ComponentName(testPackage2, "class1"));
+        tile1.priority = 1;
+        final Tile tile2 = new Tile();
+        tile2.intent = new Intent().setComponent(new ComponentName(testPackage, "class2"));
+        tile2.priority = 1;
+        final Tile tile3 = new Tile();
+        tile3.intent = new Intent().setComponent(new ComponentName(testPackage3, "class3"));
+        tile3.priority = 1;
+        category.tiles.add(tile1);
+        category.tiles.add(tile2);
+        category.tiles.add(tile3);
+        mCategoryByKeyMap.put(CategoryKey.CATEGORY_HOMEPAGE, category);
+
+        // Sort their priorities
+        mCategoryManager.sortCategories(ShadowApplication.getInstance().getApplicationContext(),
+            mCategoryByKeyMap);
+
+        // Verify the sorting order is internal first, follow by package name ordering
+        assertThat(category.tiles.get(0)).isSameAs(tile2);
+        assertThat(category.tiles.get(1)).isSameAs(tile3);
+        assertThat(category.tiles.get(2)).isSameAs(tile1);
     }
 
     @Test
diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml
index 6c06d05..9374d52 100644
--- a/packages/SettingsProvider/AndroidManifest.xml
+++ b/packages/SettingsProvider/AndroidManifest.xml
@@ -17,6 +17,7 @@
                   android:multiprocess="false"
                   android:exported="true"
                   android:singleUser="true"
-                  android:initOrder="100" />
+                  android:initOrder="100"
+                  android:visibleToInstantApps="true" />
     </application>
 </manifest>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 1fe3c48..4a54c0e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -240,8 +240,8 @@
                 Settings.Global.HDMI_CONTROL_ENABLED,
                 GlobalSettingsProto.HDMI_CONTROL_ENABLED);
         dumpSetting(s, p,
-                Settings.Global.HDMI_SYSTEM_AUDIO_ENABLED,
-                GlobalSettingsProto.HDMI_SYSTEM_AUDIO_ENABLED);
+                Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
+                GlobalSettingsProto.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED);
         dumpSetting(s, p,
                 Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
                 GlobalSettingsProto.HDMI_CONTROL_AUTO_WAKEUP_ENABLED);
diff --git a/packages/Shell/res/drawable/ic_bug_report_black_24dp.xml b/packages/Shell/res/drawable/ic_bug_report_black_24dp.xml
new file mode 100644
index 0000000..a102cee
--- /dev/null
+++ b/packages/Shell/res/drawable/ic_bug_report_black_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8L4,8v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L4,12v2h2v1c0,0.34 0.04,0.67 0.09,1L4,16v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3L20,18v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1L20,10L20,8zM14,16h-4v-2h4v2zM14,12h-4v-2h4v2z"
+        android:fillColor="#000000"/>
+</vector>
diff --git a/packages/Shell/res/values/strings.xml b/packages/Shell/res/values/strings.xml
index 2a5703a..1c49a55d 100644
--- a/packages/Shell/res/values/strings.xml
+++ b/packages/Shell/res/values/strings.xml
@@ -4,9 +4,9 @@
      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.
@@ -16,6 +16,9 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label">Shell</string>
 
+    <!-- Title of notification channel for bug report related notifications. [CHAR LIMIT=50] -->
+    <string name="bugreport_notification_channel">Bug reports</string>
+
     <!-- Title of notification indicating a bugreport is being generated. [CHAR LIMIT=50] -->
     <string name="bugreport_in_progress_title">Bug report <xliff:g id="id">#%d</xliff:g> is being generated</string>
     <!-- Title of notification indicating a bugreport has been successfully captured. [CHAR LIMIT=50] -->
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 1df626f..bf5e6f8 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -57,6 +57,7 @@
 import android.app.AlertDialog;
 import android.app.Notification;
 import android.app.Notification.Action;
+import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
@@ -64,6 +65,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.net.Uri;
@@ -196,6 +198,8 @@
      */
     private static final String SCREENSHOT_DIR = "bugreports";
 
+    private static final String NOTIFICATION_CHANNEL_ID = "bugreports";
+
     /** Managed dumpstate processes (keyed by id) */
     private final SparseArray<DumpstateListener> mProcesses = new SparseArray<>();
 
@@ -240,6 +244,12 @@
         final Configuration conf = mContext.getResources().getConfiguration();
         mIsWatch = (conf.uiMode & Configuration.UI_MODE_TYPE_MASK) ==
                 Configuration.UI_MODE_TYPE_WATCH;
+        NotificationManager nm = NotificationManager.from(mContext);
+        nm.createNotificationChannel(
+                new NotificationChannel(NOTIFICATION_CHANNEL_ID,
+                        mContext.getString(R.string.bugreport_notification_channel),
+                        isTv(this) ? NotificationManager.IMPORTANCE_DEFAULT
+                                : NotificationManager.IMPORTANCE_LOW));
     }
 
     @Override
@@ -1008,13 +1018,16 @@
             sNotificationBundle.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
                     context.getString(com.android.internal.R.string.android_system_label));
         }
-        return new Notification.Builder(context)
+        return new Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
                 .addExtras(sNotificationBundle)
                 .setCategory(Notification.CATEGORY_SYSTEM)
-                .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+                .setSmallIcon(
+                        isTv(context) ? R.drawable.ic_bug_report_black_24dp
+                                : com.android.internal.R.drawable.stat_sys_adb)
                 .setLocalOnly(true)
                 .setColor(context.getColor(
-                        com.android.internal.R.color.system_notification_accent_color));
+                        com.android.internal.R.color.system_notification_accent_color))
+                .extend(new Notification.TvExtender());
     }
 
     /**
@@ -1333,6 +1346,10 @@
         return false;
     }
 
+    private static boolean isTv(Context context) {
+        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+    }
+
     /**
      * Checks whether a character is valid on bugreport names.
      */
diff --git a/packages/Shell/tests/Android.mk b/packages/Shell/tests/Android.mk
index acd552d..48b757c 100644
--- a/packages/Shell/tests/Android.mk
+++ b/packages/Shell/tests/Android.mk
@@ -16,6 +16,7 @@
     legacy-android-test \
 
 LOCAL_PACKAGE_NAME := ShellTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
 LOCAL_INSTRUMENTATION_FOR := Shell
 
 LOCAL_CERTIFICATE := platform
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 428b7b8..4b932a3 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -180,6 +180,9 @@
     <!-- accessibility -->
     <uses-permission android:name="android.permission.MODIFY_ACCESSIBILITY_DATA" />
 
+    <!-- to control accessibility volume -->
+    <uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
+
     <application
         android:name=".SystemUIApplication"
         android:persistent="true"
diff --git a/packages/SystemUI/res/layout/pip_menu_activity.xml b/packages/SystemUI/res/layout/pip_menu_activity.xml
index 5e49d05..c6837fa 100644
--- a/packages/SystemUI/res/layout/pip_menu_activity.xml
+++ b/packages/SystemUI/res/layout/pip_menu_activity.xml
@@ -15,50 +15,55 @@
 -->
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/menu"
+    android:id="@+id/background"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:background="#4D000000">
-    <!-- The above background is only for the dismiss button ripple to show. -->
+    android:layout_height="match_parent">
 
-    <ImageView
-        android:id="@+id/dismiss"
-        android:layout_width="@dimen/pip_action_size"
-        android:layout_height="@dimen/pip_action_size"
-        android:layout_gravity="top|end"
-        android:padding="@dimen/pip_action_padding"
-        android:contentDescription="@string/pip_phone_close"
-        android:src="@drawable/ic_close_white"
-        android:background="?android:selectableItemBackgroundBorderless" />
+  <!-- Menu layout -->
+  <FrameLayout
+      android:id="@+id/menu_container"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent">
 
-    <!-- The margins for this container is calculated in the code depending on whether the
-         actions_container is visible. -->
-    <FrameLayout
-        android:id="@+id/expand_container"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
-        <ImageView
-            android:id="@+id/expand_button"
-            android:layout_width="60dp"
-            android:layout_height="60dp"
-            android:layout_gravity="center"
-            android:contentDescription="@string/pip_phone_expand"
-            android:background="?android:selectableItemBackgroundBorderless" />
-    </FrameLayout>
+      <ImageView
+          android:id="@+id/dismiss"
+          android:layout_width="@dimen/pip_action_size"
+          android:layout_height="@dimen/pip_action_size"
+          android:layout_gravity="top|end"
+          android:padding="@dimen/pip_action_padding"
+          android:contentDescription="@string/pip_phone_close"
+          android:src="@drawable/ic_close_white"
+          android:background="?android:selectableItemBackgroundBorderless" />
 
-    <FrameLayout
-        android:id="@+id/actions_container"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/pip_action_size"
-        android:layout_gravity="bottom"
-        android:visibility="invisible">
-        <LinearLayout
-            android:id="@+id/actions_group"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_gravity="center_horizontal"
-            android:orientation="horizontal"
-            android:divider="@android:color/transparent"
-            android:showDividers="middle" />
-    </FrameLayout>
+      <!-- The margins for this container is calculated in the code depending on whether the
+           actions_container is visible. -->
+      <FrameLayout
+          android:id="@+id/expand_container"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent">
+          <ImageView
+              android:id="@+id/expand_button"
+              android:layout_width="60dp"
+              android:layout_height="60dp"
+              android:layout_gravity="center"
+              android:contentDescription="@string/pip_phone_expand"
+              android:background="?android:selectableItemBackgroundBorderless" />
+      </FrameLayout>
+
+      <FrameLayout
+          android:id="@+id/actions_container"
+          android:layout_width="match_parent"
+          android:layout_height="@dimen/pip_action_size"
+          android:layout_gravity="bottom"
+          android:visibility="invisible">
+          <LinearLayout
+              android:id="@+id/actions_group"
+              android:layout_width="wrap_content"
+              android:layout_height="match_parent"
+              android:layout_gravity="center_horizontal"
+              android:orientation="horizontal"
+              android:divider="@android:color/transparent"
+              android:showDividers="middle" />
+      </FrameLayout>
+  </FrameLayout>
 </FrameLayout>
diff --git a/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml b/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
index 2ba04fd..397fbf1 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
@@ -88,7 +88,6 @@
                 android:scaleType="fitCenter"
                 android:src="@drawable/ic_qs_network_logging"
                 android:tint="?android:attr/textColorPrimaryInverse"
-                android:alpha="@dimen/qs_footer_dialog_network_logging_icon_alpha"
                 android:adjustViewBounds="true"/>
             <LinearLayout
                 android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ffaa7ba..72bdbf1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -279,8 +279,6 @@
     <dimen name="qs_footer_dialog_icon_size">24sp</dimen>
     <!-- Left and right margin of the icons -->
     <dimen name="qs_footer_dialog_icon_margin">8sp</dimen>
-    <!-- Alpha value of network logging icon -->
-    <dimen name="qs_footer_dialog_network_logging_icon_alpha">0.3</dimen>
 
     <!-- Zen mode panel: condition item button padding -->
     <dimen name="zen_mode_condition_detail_button_padding">8dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index bb7e19d..4dfaf45 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -30,6 +30,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.PluginDependencyProvider;
 import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.plugins.PluginManagerImpl;
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
 import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
@@ -73,6 +74,7 @@
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
 import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerServiceImpl;
 import com.android.systemui.util.leak.GarbageMonitor;
 import com.android.systemui.util.leak.LeakDetector;
 import com.android.systemui.util.leak.LeakReporter;
@@ -200,7 +202,7 @@
                 new DeviceProvisionedControllerImpl(mContext));
 
         mProviders.put(PluginManager.class, () ->
-                new PluginManager(mContext));
+                new PluginManagerImpl(mContext));
 
         mProviders.put(AssistManager.class, () ->
                 new AssistManager(getDependency(DeviceProvisionedController.class), mContext));
@@ -223,7 +225,7 @@
                 getDependency(LeakReporter.class)));
 
         mProviders.put(TunerService.class, () ->
-                new TunerService(mContext));
+                new TunerServiceImpl(mContext));
 
         mProviders.put(StatusBarWindowManager.class, () ->
                 new StatusBarWindowManager(mContext));
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index cbe822f..a549c73 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1160,7 +1160,8 @@
 
             if (mOccluded != isOccluded) {
                 mOccluded = isOccluded;
-                mStatusBarKeyguardViewManager.setOccluded(isOccluded, animate);
+                mStatusBarKeyguardViewManager.setOccluded(isOccluded, animate
+                        && mDeviceInteractive);
                 adjustStatusBarLocked();
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 86e2c49..0547cbb 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -20,10 +20,12 @@
 import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_CONTROLLER_MESSENGER;
 import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_MOVEMENT_BOUNDS;
 import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_STACK_BOUNDS;
+import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_SHOW_MENU;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -35,6 +37,8 @@
 import android.graphics.Color;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.Handler;
@@ -71,13 +75,19 @@
     public static final int MESSAGE_POKE_MENU = 2;
     public static final int MESSAGE_HIDE_MENU = 3;
     public static final int MESSAGE_UPDATE_ACTIONS = 4;
+    public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5;
 
     private static final long INITIAL_DISMISS_DELAY = 2000;
     private static final long POST_INTERACTION_DISMISS_DELAY = 1500;
     private static final long MENU_FADE_DURATION = 125;
 
+    private static final float MENU_BACKGROUND_ALPHA = 0.3f;
+    private static final float DISMISS_BACKGROUND_ALPHA = 0.8f;
+
     private boolean mMenuVisible;
     private final List<RemoteAction> mActions = new ArrayList<>();
+    private View mViewRoot;
+    private Drawable mBackgroundDrawable;
     private View mMenuContainer;
     private LinearLayout mActionsGroup;
     private View mDismissButton;
@@ -85,6 +95,14 @@
     private int mBetweenActionPaddingLand;
 
     private ObjectAnimator mMenuContainerAnimator;
+    private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
+            new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    final float alpha = (float) animation.getAnimatedValue();
+                    mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA*alpha*255));
+                }
+            };
 
     private PointF mDownPosition = new PointF();
     private PointF mDownDelta = new PointF();
@@ -109,6 +127,10 @@
                     Pair<Rect, ParceledListSlice> data = (Pair<Rect, ParceledListSlice>) msg.obj;
                     setActions(data.first, data.second.getList());
                     break;
+                case MESSAGE_UPDATE_DISMISS_FRACTION:
+                    float fraction = (float) msg.obj;
+                    updateDismissFraction(fraction);
+                    break;
             }
         }
     });
@@ -130,7 +152,12 @@
         super.onCreate(savedInstanceState);
         setContentView(R.layout.pip_menu_activity);
 
-        mMenuContainer = findViewById(R.id.menu);
+        mBackgroundDrawable = new ColorDrawable(Color.BLACK);
+        mBackgroundDrawable.setAlpha(0);
+        mViewRoot = findViewById(R.id.background);
+        mViewRoot.setBackground(mBackgroundDrawable);
+        mMenuContainer = findViewById(R.id.menu_container);
+        mMenuContainer.setAlpha(0);
         mMenuContainer.setOnClickListener((v) -> {
             expandPip();
         });
@@ -222,10 +249,11 @@
 
     private void showMenu(Rect stackBounds, Rect movementBounds) {
         if (!mMenuVisible) {
+            setVisible(true);
+            updateActionViews(stackBounds);
             if (mMenuContainerAnimator != null) {
                 mMenuContainerAnimator.cancel();
             }
-
             notifyMenuVisibility(true);
             updateExpandButtonFromBounds(stackBounds, movementBounds);
             mMenuContainerAnimator = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
@@ -238,9 +266,13 @@
                     repostDelayedFinish(INITIAL_DISMISS_DELAY);
                 }
             });
+            mMenuContainerAnimator.addUpdateListener(mMenuBgUpdateListener);
             mMenuContainerAnimator.start();
         } else {
+            // If we are already visible, then just start the delayed dismiss and unregister any
+            // existing input consumers from the previous drag
             repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
+            notifyUnregisterInputConsumer();
         }
     }
 
@@ -264,25 +296,27 @@
                     if (animationFinishedRunnable != null) {
                         animationFinishedRunnable.run();
                     }
-                    if (getSystemService(AccessibilityManager.class).isEnabled()) {
-                        finish();
-                    }
+                    setVisible(false);
                 }
             });
+            mMenuContainerAnimator.addUpdateListener(mMenuBgUpdateListener);
             mMenuContainerAnimator.start();
         }
     }
 
     private void updateFromIntent(Intent intent) {
-        Rect stackBounds = Rect.unflattenFromString(intent.getStringExtra(EXTRA_STACK_BOUNDS));
-        Rect movementBounds = Rect.unflattenFromString(intent.getStringExtra(
-                EXTRA_MOVEMENT_BOUNDS));
         mToControllerMessenger = intent.getParcelableExtra(EXTRA_CONTROLLER_MESSENGER);
         ParceledListSlice actions = intent.getParcelableExtra(EXTRA_ACTIONS);
         if (actions != null) {
-            setActions(stackBounds, actions.getList());
+            mActions.clear();
+            mActions.addAll(actions.getList());
         }
-        showMenu(stackBounds, movementBounds);
+        if (intent.getBooleanExtra(EXTRA_SHOW_MENU, false)) {
+            Rect stackBounds = Rect.unflattenFromString(intent.getStringExtra(EXTRA_STACK_BOUNDS));
+            Rect movementBounds = Rect.unflattenFromString(intent.getStringExtra(
+                    EXTRA_MOVEMENT_BOUNDS));
+            showMenu(stackBounds, movementBounds);
+        }
     }
 
     private void updateExpandButtonFromBounds(Rect stackBounds, Rect movementBounds) {
@@ -365,12 +399,32 @@
         }
     }
 
+    private void updateDismissFraction(float fraction) {
+        setVisible(true);
+        int alpha;
+        if (mMenuVisible) {
+            mMenuContainer.setAlpha(1-fraction);
+            final float interpolatedAlpha =
+                    MENU_BACKGROUND_ALPHA * (1.0f - fraction) + DISMISS_BACKGROUND_ALPHA * fraction;
+            alpha = (int) (interpolatedAlpha*255);
+        } else {
+            alpha = (int) (fraction*DISMISS_BACKGROUND_ALPHA*255);
+        }
+        mBackgroundDrawable.setAlpha(alpha);
+    }
+
     private void notifyRegisterInputConsumer() {
         Message m = Message.obtain();
         m.what = PipMenuActivityController.MESSAGE_REGISTER_INPUT_CONSUMER;
         sendMessage(m, "Could not notify controller to register input consumer");
     }
 
+    private void notifyUnregisterInputConsumer() {
+        Message m = Message.obtain();
+        m.what = PipMenuActivityController.MESSAGE_UNREGISTER_INPUT_CONSUMER;
+        sendMessage(m, "Could not notify controller to unregister input consumer");
+    }
+
     private void notifyMenuVisibility(boolean visible) {
         mMenuVisible = visible;
         Message m = Message.obtain();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index badf64b..724f453 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -42,7 +42,7 @@
 import java.util.List;
 
 /**
- * Manages the PiP menu activity.
+ * Manages the PiP menu activity which can show menu options or a scrim.
  *
  * The current media session provides actions whenever there are no valid actions provided by the
  * current PiP activity. Otherwise, those actions always take precedence.
@@ -55,6 +55,7 @@
     public static final String EXTRA_ACTIONS = "actions";
     public static final String EXTRA_STACK_BOUNDS = "stack_bounds";
     public static final String EXTRA_MOVEMENT_BOUNDS = "movement_bounds";
+    public static final String EXTRA_SHOW_MENU = "show_menu";
 
     public static final int MESSAGE_MENU_VISIBILITY_CHANGED = 100;
     public static final int MESSAGE_EXPAND_PIP = 101;
@@ -62,6 +63,7 @@
     public static final int MESSAGE_DISMISS_PIP = 103;
     public static final int MESSAGE_UPDATE_ACTIVITY_CALLBACK = 104;
     public static final int MESSAGE_REGISTER_INPUT_CONSUMER = 105;
+    public static final int MESSAGE_UNREGISTER_INPUT_CONSUMER = 106;
 
     /**
      * A listener interface to receive notification on changes in PIP.
@@ -101,6 +103,7 @@
     private ParceledListSlice mMediaActions;
     private boolean mMenuVisible;
 
+    private boolean mStartActivityRequested;
     private Messenger mToActivityMessenger;
     private Messenger mMessenger = new Messenger(new Handler() {
         @Override
@@ -133,8 +136,13 @@
                     mInputConsumerController.registerInputConsumer();
                     break;
                 }
+                case MESSAGE_UNREGISTER_INPUT_CONSUMER: {
+                    mInputConsumerController.unregisterInputConsumer();
+                    break;
+                }
                 case MESSAGE_UPDATE_ACTIVITY_CALLBACK: {
                     mToActivityMessenger = msg.replyTo;
+                    mStartActivityRequested = false;
                     // Mark the menu as invisible once the activity finishes as well
                     if (mToActivityMessenger == null) {
                         onMenuVisibilityChanged(false, true /* resize */);
@@ -179,6 +187,25 @@
     }
 
     /**
+     * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
+     */
+    public void setDismissFraction(float fraction) {
+        if (mToActivityMessenger != null) {
+            Message m = Message.obtain();
+            m.what = PipMenuActivity.MESSAGE_UPDATE_DISMISS_FRACTION;
+            m.obj = fraction;
+            try {
+                mToActivityMessenger.send(m);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Could not notify menu to show", e);
+            }
+        } else if (!mStartActivityRequested) {
+            startMenuActivity(null /* stackBounds */, null /* movementBounds */,
+                    false /* showMenu */);
+        }
+    }
+
+    /**
      * Shows the menu activity.
      */
     public void showMenu(Rect stackBounds, Rect movementBounds) {
@@ -191,28 +218,8 @@
             } catch (RemoteException e) {
                 Log.e(TAG, "Could not notify menu to show", e);
             }
-        } else {
-            // Start the menu activity on the top task of the pinned stack
-            try {
-                StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
-                if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
-                        pinnedStackInfo.taskIds.length > 0) {
-                    Intent intent = new Intent(mContext, PipMenuActivity.class);
-                    intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
-                    intent.putExtra(EXTRA_ACTIONS, resolveMenuActions());
-                    intent.putExtra(EXTRA_STACK_BOUNDS, stackBounds.flattenToString());
-                    intent.putExtra(EXTRA_MOVEMENT_BOUNDS, movementBounds.flattenToString());
-                    ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
-                    options.setLaunchTaskId(
-                            pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
-                    options.setTaskOverlay(true, true /* canResume */);
-                    mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
-                } else {
-                    Log.e(TAG, "No PIP tasks found");
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error showing PIP menu activity", e);
-            }
+        } else if (!mStartActivityRequested) {
+            startMenuActivity(stackBounds, movementBounds, true /* showMenu */);
         }
     }
 
@@ -272,6 +279,39 @@
     }
 
     /**
+     * Starts the menu activity on the top task of the pinned stack.
+     */
+    private void startMenuActivity(Rect stackBounds, Rect movementBounds, boolean showMenu) {
+        try {
+            StackInfo pinnedStackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
+            if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
+                    pinnedStackInfo.taskIds.length > 0) {
+                Intent intent = new Intent(mContext, PipMenuActivity.class);
+                intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
+                intent.putExtra(EXTRA_ACTIONS, resolveMenuActions());
+                if (stackBounds != null) {
+                    intent.putExtra(EXTRA_STACK_BOUNDS, stackBounds.flattenToString());
+                }
+                if (movementBounds != null) {
+                    intent.putExtra(EXTRA_MOVEMENT_BOUNDS, movementBounds.flattenToString());
+                }
+                intent.putExtra(EXTRA_SHOW_MENU, showMenu);
+                ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
+                options.setLaunchTaskId(
+                        pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
+                options.setTaskOverlay(true, true /* canResume */);
+                mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
+                mStartActivityRequested = true;
+            } else {
+                Log.e(TAG, "No PIP tasks found");
+            }
+        } catch (RemoteException e) {
+            mStartActivityRequested = false;
+            Log.e(TAG, "Error showing PIP menu activity", e);
+        }
+    }
+
+    /**
      * Updates the PiP menu activity with the best set of actions provided.
      */
     private void updateMenuActions() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 49d89a2..c4cf28c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -65,9 +65,9 @@
     private static final int IME_SHIFT_DURATION = 300;
 
     // The fraction of the stack width that the user has to drag offscreen to minimize the PiP
-    private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.2f;
-    // The fraction of the stack height that the user has to drag offscreen to minimize the PiP
-    private static final float DISMISS_OFFSCREEN_FRACTION = 0.35f;
+    private static final float MINIMIZE_OFFSCREEN_FRACTION = 0.3f;
+    // The fraction of the stack height that the user has to drag offscreen to dismiss the PiP
+    private static final float DISMISS_OFFSCREEN_FRACTION = 0.15f;
 
     private Context mContext;
     private IActivityManager mActivityManager;
@@ -234,12 +234,16 @@
     /**
      * Animates the PiP to the minimized state, slightly offscreen.
      */
-    Rect animateToClosestMinimizedState(Rect movementBounds) {
+    Rect animateToClosestMinimizedState(Rect movementBounds,
+            AnimatorUpdateListener updateListener) {
         cancelAnimations();
         Rect toBounds = getClosestMinimizedBounds(mBounds, movementBounds);
         if (!mBounds.equals(toBounds)) {
             mBoundsAnimator = createAnimationToBounds(mBounds, toBounds,
                     MINIMIZE_STACK_MAX_DURATION, LINEAR_OUT_SLOW_IN, mUpdateBoundsListener);
+            if (updateListener != null) {
+                mBoundsAnimator.addUpdateListener(updateListener);
+            }
             mBoundsAnimator.start();
         }
         return toBounds;
@@ -248,7 +252,8 @@
     /**
      * Flings the PiP to the closest snap target.
      */
-    Rect flingToSnapTarget(float velocity, float velocityX, float velocityY, Rect movementBounds) {
+    Rect flingToSnapTarget(float velocity, float velocityX, float velocityY, Rect movementBounds,
+            AnimatorUpdateListener listener) {
         cancelAnimations();
         Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds,
                 velocityX, velocityY);
@@ -258,6 +263,9 @@
             mFlingAnimationUtils.apply(mBoundsAnimator, 0,
                     distanceBetweenRectOffsets(mBounds, toBounds),
                     velocity);
+            if (listener != null) {
+                mBoundsAnimator.addUpdateListener(listener);
+            }
             mBoundsAnimator.start();
         }
         return toBounds;
@@ -266,12 +274,15 @@
     /**
      * Animates the PiP to the closest snap target.
      */
-    Rect animateToClosestSnapTarget(Rect movementBounds) {
+    Rect animateToClosestSnapTarget(Rect movementBounds, AnimatorUpdateListener listener) {
         cancelAnimations();
         Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds);
         if (!mBounds.equals(toBounds)) {
             mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, SNAP_STACK_DURATION,
                     FAST_OUT_SLOW_IN, mUpdateBoundsListener);
+            if (listener != null) {
+                mBoundsAnimator.addUpdateListener(listener);
+            }
             mBoundsAnimator.start();
         }
         return toBounds;
@@ -316,7 +327,7 @@
     /**
      * Animates the dismissal of the PiP off the edge of the screen.
      */
-    Rect animateDragToEdgeDismiss(Rect pipBounds) {
+    Rect animateDragToEdgeDismiss(Rect pipBounds, AnimatorUpdateListener listener) {
         cancelAnimations();
         Point displaySize = new Point();
         mContext.getDisplay().getRealSize(displaySize);
@@ -330,6 +341,9 @@
                 dismissPip();
             }
         });
+        if (listener != null) {
+            mBoundsAnimator.addUpdateListener(listener);
+        }
         mBoundsAnimator.start();
         return toBounds;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 80231a9..f70d5b4 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.pip.phone;
 
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.app.IActivityManager;
 import android.content.Context;
 import android.graphics.Point;
@@ -55,7 +57,7 @@
 
     // Allow dragging the PIP to a location to close it
     private static final boolean ENABLE_DISMISS_DRAG_TO_TARGET = false;
-    private static final boolean ENABLE_DISMISS_DRAG_TO_EDGE = false;
+    private static final boolean ENABLE_DISMISS_DRAG_TO_EDGE = true;
 
     private final Context mContext;
     private final IActivityManager mActivityManager;
@@ -87,6 +89,13 @@
             }
         }
     };
+    private ValueAnimator.AnimatorUpdateListener mUpdateScrimListener =
+            new AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    updateDismissFraction();
+                }
+            };
 
     // Behaviour states
     private boolean mIsMenuVisible;
@@ -124,7 +133,7 @@
         @Override
         public void onPipMinimize() {
             setMinimizedStateInternal(true);
-            mMotionHelper.animateToClosestMinimizedState(mMovementBounds);
+            mMotionHelper.animateToClosestMinimizedState(mMovementBounds, null /* updateListener */);
         }
 
         @Override
@@ -331,6 +340,22 @@
     }
 
     /**
+     * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
+     */
+    void updateDismissFraction() {
+        if (mMenuController != null) {
+            Rect bounds = mMotionHelper.getBounds();
+            final float target = mMovementBounds.bottom + bounds.height();
+            float fraction = 0f;
+            if (bounds.bottom > target) {
+                final float distance = bounds.bottom - target;
+                fraction = Math.min(distance / bounds.height(), 1f);
+            }
+            mMenuController.setDismissFraction(fraction);
+        }
+    }
+
+    /**
      * Sets the controller to update the system of changes from user interaction.
      */
     void setPinnedStackController(IPinnedStackController controller) {
@@ -465,6 +490,9 @@
                 if (ENABLE_DISMISS_DRAG_TO_TARGET) {
                     mDismissViewController.updateDismissTarget(mTmpBounds);
                 }
+                if (ENABLE_DISMISS_DRAG_TO_EDGE) {
+                    updateDismissFraction();
+                }
                 return true;
             }
             return false;
@@ -476,8 +504,8 @@
                 return false;
             }
 
-            try {
-                if (ENABLE_DISMISS_DRAG_TO_TARGET) {
+            if (ENABLE_DISMISS_DRAG_TO_TARGET) {
+                try {
                     mHandler.removeCallbacks(mShowDismissAffordance);
                     PointF vel = mTouchState.getVelocity();
                     final float velocity = PointF.length(vel.x, vel.y);
@@ -492,9 +520,9 @@
                             return true;
                         }
                     }
+                } finally {
+                    mDismissViewController.destroyDismissTarget();
                 }
-            } finally {
-                mDismissViewController.destroyDismissTarget();
             }
 
             if (touchState.isDragging()) {
@@ -502,7 +530,8 @@
                 boolean isFlingToBot = isFlingTowardsEdge(touchState, 4 /* bottom */);
                 if (ENABLE_DISMISS_DRAG_TO_EDGE
                         && (mMotionHelper.shouldDismissPip() || isFlingToBot)) {
-                    mMotionHelper.animateDragToEdgeDismiss(mMotionHelper.getBounds());
+                    mMotionHelper.animateDragToEdgeDismiss(mMotionHelper.getBounds(),
+                            mUpdateScrimListener);
                     MetricsLogger.action(mContext,
                             MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
                             METRIC_VALUE_DISMISSED_BY_DRAG);
@@ -517,7 +546,8 @@
                         // minimize offset adjusted
                         mMenuController.hideMenu();
                     } else {
-                        mMotionHelper.animateToClosestMinimizedState(mMovementBounds);
+                        mMotionHelper.animateToClosestMinimizedState(mMovementBounds,
+                                mUpdateScrimListener);
                     }
                     return true;
                 }
@@ -536,13 +566,14 @@
                 final PointF vel = mTouchState.getVelocity();
                 final float velocity = PointF.length(vel.x, vel.y);
                 if (velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
-                    mMotionHelper.flingToSnapTarget(velocity, vel.x, vel.y, mMovementBounds);
+                    mMotionHelper.flingToSnapTarget(velocity, vel.x, vel.y, mMovementBounds,
+                            mUpdateScrimListener);
                 } else {
-                    mMotionHelper.animateToClosestSnapTarget(mMovementBounds);
+                    mMotionHelper.animateToClosestSnapTarget(mMovementBounds, mUpdateScrimListener);
                 }
             } else if (mIsMinimized) {
                 // This was a tap, so no longer minimized
-                mMotionHelper.animateToClosestSnapTarget(mMovementBounds);
+                mMotionHelper.animateToClosestSnapTarget(mMovementBounds, null /* listener */);
                 setMinimizedStateInternal(false);
             } else if (!mIsMenuVisible) {
                 mMenuController.showMenu(mMotionHelper.getBounds(), mMovementBounds);
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
index 79f78c9..07ac52d 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java
@@ -62,10 +62,10 @@
     final PluginHandler mPluginHandler;
     private final boolean isDebuggable;
     private final PackageManager mPm;
-    private final PluginManager mManager;
+    private final PluginManagerImpl mManager;
 
     PluginInstanceManager(Context context, String action, PluginListener<T> listener,
-            boolean allowMultiple, Looper looper, VersionInfo version, PluginManager manager) {
+            boolean allowMultiple, Looper looper, VersionInfo version, PluginManagerImpl manager) {
         this(context, context.getPackageManager(), action, listener, allowMultiple, looper, version,
                 manager, Build.IS_DEBUGGABLE);
     }
@@ -73,7 +73,7 @@
     @VisibleForTesting
     PluginInstanceManager(Context context, PackageManager pm, String action,
             PluginListener<T> listener, boolean allowMultiple, Looper looper, VersionInfo version,
-            PluginManager manager, boolean debuggable) {
+            PluginManagerImpl manager, boolean debuggable) {
         mMainHandler = new MainHandler(Looper.getMainLooper());
         mPluginHandler = new PluginHandler(looper);
         mManager = manager;
@@ -346,7 +346,7 @@
                                 .setContentText("Check to see if an OTA is available.\n"
                                         + e.getMessage());
                     }
-                    Intent i = new Intent(PluginManager.DISABLE_PLUGIN).setData(
+                    Intent i = new Intent(PluginManagerImpl.DISABLE_PLUGIN).setData(
                             Uri.parse("package://" + component.flattenToString()));
                     PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, 0);
                     nb.addAction(new Action.Builder(null, "Disable plugin", pi).build());
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java
index 9ad862d..298eaf1 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
  * except in compliance with the License. You may obtain a copy of the License at
@@ -14,276 +14,33 @@
 
 package com.android.systemui.plugins;
 
-import android.app.Notification;
-import android.app.Notification.Action;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.SystemProperties;
-import android.os.UserHandle;
 import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.systemui.Dependency;
-import com.android.systemui.plugins.PluginInstanceManager.PluginContextWrapper;
-import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 
-import dalvik.system.PathClassLoader;
+public interface PluginManager {
 
-import java.lang.Thread.UncaughtExceptionHandler;
-import java.util.Map;
-
-/**
- * @see Plugin
- */
-public class PluginManager extends BroadcastReceiver {
-
-    public static final String PLUGIN_CHANGED = "com.android.systemui.action.PLUGIN_CHANGED";
-
-    static final String DISABLE_PLUGIN = "com.android.systemui.action.DISABLE_PLUGIN";
+    String PLUGIN_CHANGED = "com.android.systemui.action.PLUGIN_CHANGED";
 
     // must be one of the channels created in NotificationChannels.java
-    static final String NOTIFICATION_CHANNEL_ID = "ALR";
+    String NOTIFICATION_CHANNEL_ID = "ALR";
 
-    private static PluginManager sInstance;
+    <T extends Plugin> T getOneShotPlugin(Class<T> cls);
+    <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls);
 
-    private final HandlerThread mBackgroundThread;
-    private final ArrayMap<PluginListener<?>, PluginInstanceManager> mPluginMap
-            = new ArrayMap<>();
-    private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
-    private final ArraySet<String> mOneShotPackages = new ArraySet<>();
-    private final Context mContext;
-    private final PluginInstanceManagerFactory mFactory;
-    private final boolean isDebuggable;
-    private final PluginPrefs mPluginPrefs;
-    private ClassLoaderFilter mParentClassLoader;
-    private boolean mListening;
-    private boolean mHasOneShot;
+    <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls);
+    <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
+            boolean allowMultiple);
+    <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+            Class<?> cls);
+    <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+            Class cls, boolean allowMultiple);
 
-    public PluginManager(Context context) {
-        this(context, new PluginInstanceManagerFactory(),
-                Build.IS_DEBUGGABLE, Thread.getDefaultUncaughtExceptionHandler());
-    }
+    void removePluginListener(PluginListener<?> listener);
 
-    @VisibleForTesting
-    PluginManager(Context context, PluginInstanceManagerFactory factory, boolean debuggable,
-            UncaughtExceptionHandler defaultHandler) {
-        mContext = context;
-        mFactory = factory;
-        mBackgroundThread = new HandlerThread("Plugins");
-        mBackgroundThread.start();
-        isDebuggable = debuggable;
-        mPluginPrefs = new PluginPrefs(mContext);
+    <T> boolean dependsOn(Plugin p, Class<T> cls);
 
-        PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
-                defaultHandler);
-        Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
-        if (isDebuggable) {
-            new Handler(mBackgroundThread.getLooper()).post(() -> {
-                // Plugin dependencies that don't have another good home can go here, but
-                // dependencies that have better places to init can happen elsewhere.
-                Dependency.get(PluginDependencyProvider.class)
-                        .allowPluginDependency(ActivityStarter.class);
-            });
-        }
-    }
-
-    public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
-        ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
-        if (info == null) {
-            throw new RuntimeException(cls + " doesn't provide an interface");
-        }
-        if (TextUtils.isEmpty(info.action())) {
-            throw new RuntimeException(cls + " doesn't provide an action");
-        }
-        return getOneShotPlugin(info.action(), cls);
-    }
-
-    public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) {
-        if (!isDebuggable) {
-            // Never ever ever allow these on production builds, they are only for prototyping.
-            return null;
-        }
-        if (Looper.myLooper() != Looper.getMainLooper()) {
-            throw new RuntimeException("Must be called from UI thread");
-        }
-        PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, null,
-                false, mBackgroundThread.getLooper(), cls, this);
-        mPluginPrefs.addAction(action);
-        PluginInfo<T> info = p.getPlugin();
-        if (info != null) {
-            mOneShotPackages.add(info.mPackage);
-            mHasOneShot = true;
-            startListening();
-            return info.mPlugin;
-        }
-        return null;
-    }
-
-    public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls) {
-        addPluginListener(listener, cls, false);
-    }
-
-    public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
-            boolean allowMultiple) {
-        addPluginListener(getAction(cls), listener, cls, allowMultiple);
-    }
-
-    public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
-            Class<?> cls) {
-        addPluginListener(action, listener, cls, false);
-    }
-
-    public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
-            Class cls, boolean allowMultiple) {
-        if (!isDebuggable) {
-            // Never ever ever allow these on production builds, they are only for prototyping.
-            return;
-        }
-        mPluginPrefs.addAction(action);
-        PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
-                allowMultiple, mBackgroundThread.getLooper(), cls, this);
-        p.loadAll();
-        mPluginMap.put(listener, p);
-        startListening();
-    }
-
-    public void removePluginListener(PluginListener<?> listener) {
-        if (!isDebuggable) {
-            // Never ever ever allow these on production builds, they are only for prototyping.
-            return;
-        }
-        if (!mPluginMap.containsKey(listener)) return;
-        mPluginMap.remove(listener).destroy();
-        stopListening();
-    }
-
-    private void startListening() {
-        if (mListening) return;
-        mListening = true;
-        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
-        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addAction(PLUGIN_CHANGED);
-        filter.addAction(DISABLE_PLUGIN);
-        filter.addDataScheme("package");
-        mContext.registerReceiver(this, filter);
-        filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
-        mContext.registerReceiver(this, filter);
-    }
-
-    private void stopListening() {
-        // Never stop listening if a one-shot is present.
-        if (!mListening || mHasOneShot) return;
-        mListening = false;
-        mContext.unregisterReceiver(this);
-    }
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
-            for (PluginInstanceManager manager : mPluginMap.values()) {
-                manager.loadAll();
-            }
-        } else if (DISABLE_PLUGIN.equals(intent.getAction())) {
-            Uri uri = intent.getData();
-            ComponentName component = ComponentName.unflattenFromString(
-                    uri.toString().substring(10));
-            mContext.getPackageManager().setComponentEnabledSetting(component,
-                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
-                    PackageManager.DONT_KILL_APP);
-            mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(),
-                    SystemMessage.NOTE_PLUGIN);
-        } else {
-            Uri data = intent.getData();
-            String pkg = data.getEncodedSchemeSpecificPart();
-            if (mOneShotPackages.contains(pkg)) {
-                int icon = mContext.getResources().getIdentifier("tuner", "drawable",
-                        mContext.getPackageName());
-                int color = Resources.getSystem().getIdentifier(
-                        "system_notification_accent_color", "color", "android");
-                String label = pkg;
-                try {
-                    PackageManager pm = mContext.getPackageManager();
-                    label = pm.getApplicationInfo(pkg, 0).loadLabel(pm).toString();
-                } catch (NameNotFoundException e) {
-                }
-                // Localization not required as this will never ever appear in a user build.
-                final Notification.Builder nb =
-                        new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
-                                .setSmallIcon(icon)
-                                .setWhen(0)
-                                .setShowWhen(false)
-                                .setPriority(Notification.PRIORITY_MAX)
-                                .setVisibility(Notification.VISIBILITY_PUBLIC)
-                                .setColor(mContext.getColor(color))
-                                .setContentTitle("Plugin \"" + label + "\" has updated")
-                                .setContentText("Restart SysUI for changes to take effect.");
-                Intent i = new Intent("com.android.systemui.action.RESTART").setData(
-                            Uri.parse("package://" + pkg));
-                PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, 0);
-                nb.addAction(new Action.Builder(null, "Restart SysUI", pi).build());
-                mContext.getSystemService(NotificationManager.class).notifyAsUser(pkg,
-                        SystemMessage.NOTE_PLUGIN, nb.build(), UserHandle.ALL);
-            }
-            clearClassLoader(pkg);
-            if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
-                for (PluginInstanceManager manager : mPluginMap.values()) {
-                    manager.onPackageChange(pkg);
-                }
-            } else {
-                for (PluginInstanceManager manager : mPluginMap.values()) {
-                    manager.onPackageRemoved(pkg);
-                }
-            }
-        }
-    }
-
-    public ClassLoader getClassLoader(String sourceDir, String pkg) {
-        if (mClassLoaders.containsKey(pkg)) {
-            return mClassLoaders.get(pkg);
-        }
-        ClassLoader classLoader = new PathClassLoader(sourceDir, getParentClassLoader());
-        mClassLoaders.put(pkg, classLoader);
-        return classLoader;
-    }
-
-    private void clearClassLoader(String pkg) {
-        mClassLoaders.remove(pkg);
-    }
-
-    ClassLoader getParentClassLoader() {
-        if (mParentClassLoader == null) {
-            // Lazily load this so it doesn't have any effect on devices without plugins.
-            mParentClassLoader = new ClassLoaderFilter(getClass().getClassLoader(),
-                    "com.android.systemui.plugin");
-        }
-        return mParentClassLoader;
-    }
-
-    public Context getContext(ApplicationInfo info, String pkg) throws NameNotFoundException {
-        ClassLoader classLoader = getClassLoader(info.sourceDir, pkg);
-        return new PluginContextWrapper(mContext.createApplicationContext(info, 0), classLoader);
-    }
-
-    public static <P> String getAction(Class<P> cls) {
+    static <P> String getAction(Class<P> cls) {
         ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
         if (info == null) {
             throw new RuntimeException(cls + " doesn't provide an interface");
@@ -293,82 +50,4 @@
         }
         return info.action();
     }
-
-    public <T> boolean dependsOn(Plugin p, Class<T> cls) {
-        for (int i = 0; i < mPluginMap.size(); i++) {
-            if (mPluginMap.valueAt(i).dependsOn(p, cls)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @VisibleForTesting
-    public static class PluginInstanceManagerFactory {
-        public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
-                String action, PluginListener<T> listener, boolean allowMultiple, Looper looper,
-                Class<?> cls, PluginManager manager) {
-            return new PluginInstanceManager(context, action, listener, allowMultiple, looper,
-                    new VersionInfo().addClass(cls), manager);
-        }
-    }
-
-    // This allows plugins to include any libraries or copied code they want by only including
-    // classes from the plugin library.
-    private static class ClassLoaderFilter extends ClassLoader {
-        private final String mPackage;
-        private final ClassLoader mBase;
-
-        public ClassLoaderFilter(ClassLoader base, String pkg) {
-            super(ClassLoader.getSystemClassLoader());
-            mBase = base;
-            mPackage = pkg;
-        }
-
-        @Override
-        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
-            if (!name.startsWith(mPackage)) super.loadClass(name, resolve);
-            return mBase.loadClass(name);
-        }
-    }
-
-    private class PluginExceptionHandler implements UncaughtExceptionHandler {
-        private final UncaughtExceptionHandler mHandler;
-
-        private PluginExceptionHandler(UncaughtExceptionHandler handler) {
-            mHandler = handler;
-        }
-
-        @Override
-        public void uncaughtException(Thread thread, Throwable throwable) {
-            if (SystemProperties.getBoolean("plugin.debugging", false)) {
-                mHandler.uncaughtException(thread, throwable);
-                return;
-            }
-            // Search for and disable plugins that may have been involved in this crash.
-            boolean disabledAny = checkStack(throwable);
-            if (!disabledAny) {
-                // We couldn't find any plugins involved in this crash, just to be safe
-                // disable all the plugins, so we can be sure that SysUI is running as
-                // best as possible.
-                for (PluginInstanceManager manager : mPluginMap.values()) {
-                    manager.disableAll();
-                }
-            }
-
-            // Run the normal exception handler so we can crash and cleanup our state.
-            mHandler.uncaughtException(thread, throwable);
-        }
-
-        private boolean checkStack(Throwable throwable) {
-            if (throwable == null) return false;
-            boolean disabledAny = false;
-            for (StackTraceElement element : throwable.getStackTrace()) {
-                for (PluginInstanceManager manager : mPluginMap.values()) {
-                    disabledAny |= manager.checkAndDisable(element.getClassName());
-                }
-            }
-            return disabledAny | checkStack(throwable.getCause());
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
new file mode 100644
index 0000000..1fb6c87
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.plugins;
+
+import android.app.Notification;
+import android.app.Notification.Action;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.systemui.Dependency;
+import com.android.systemui.plugins.PluginInstanceManager.PluginContextWrapper;
+import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+import dalvik.system.PathClassLoader;
+
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.Map;
+
+/**
+ * @see Plugin
+ */
+public class PluginManagerImpl extends BroadcastReceiver implements PluginManager {
+
+    static final String DISABLE_PLUGIN = "com.android.systemui.action.DISABLE_PLUGIN";
+
+    private static PluginManager sInstance;
+
+    private final HandlerThread mBackgroundThread;
+    private final ArrayMap<PluginListener<?>, PluginInstanceManager> mPluginMap
+            = new ArrayMap<>();
+    private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
+    private final ArraySet<String> mOneShotPackages = new ArraySet<>();
+    private final Context mContext;
+    private final PluginInstanceManagerFactory mFactory;
+    private final boolean isDebuggable;
+    private final PluginPrefs mPluginPrefs;
+    private ClassLoaderFilter mParentClassLoader;
+    private boolean mListening;
+    private boolean mHasOneShot;
+
+    public PluginManagerImpl(Context context) {
+        this(context, new PluginInstanceManagerFactory(),
+                Build.IS_DEBUGGABLE, Thread.getDefaultUncaughtExceptionHandler());
+    }
+
+    @VisibleForTesting
+    PluginManagerImpl(Context context, PluginInstanceManagerFactory factory, boolean debuggable,
+            UncaughtExceptionHandler defaultHandler) {
+        mContext = context;
+        mFactory = factory;
+        mBackgroundThread = new HandlerThread("Plugins");
+        mBackgroundThread.start();
+        isDebuggable = debuggable;
+        mPluginPrefs = new PluginPrefs(mContext);
+
+        PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler(
+                defaultHandler);
+        Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
+        if (isDebuggable) {
+            new Handler(mBackgroundThread.getLooper()).post(() -> {
+                // Plugin dependencies that don't have another good home can go here, but
+                // dependencies that have better places to init can happen elsewhere.
+                Dependency.get(PluginDependencyProvider.class)
+                        .allowPluginDependency(ActivityStarter.class);
+            });
+        }
+    }
+
+    public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
+        ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
+        if (info == null) {
+            throw new RuntimeException(cls + " doesn't provide an interface");
+        }
+        if (TextUtils.isEmpty(info.action())) {
+            throw new RuntimeException(cls + " doesn't provide an action");
+        }
+        return getOneShotPlugin(info.action(), cls);
+    }
+
+    public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) {
+        if (!isDebuggable) {
+            // Never ever ever allow these on production builds, they are only for prototyping.
+            return null;
+        }
+        if (Looper.myLooper() != Looper.getMainLooper()) {
+            throw new RuntimeException("Must be called from UI thread");
+        }
+        PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, null,
+                false, mBackgroundThread.getLooper(), cls, this);
+        mPluginPrefs.addAction(action);
+        PluginInfo<T> info = p.getPlugin();
+        if (info != null) {
+            mOneShotPackages.add(info.mPackage);
+            mHasOneShot = true;
+            startListening();
+            return info.mPlugin;
+        }
+        return null;
+    }
+
+    public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls) {
+        addPluginListener(listener, cls, false);
+    }
+
+    public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
+            boolean allowMultiple) {
+        addPluginListener(PluginManager.getAction(cls), listener, cls, allowMultiple);
+    }
+
+    public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+            Class<?> cls) {
+        addPluginListener(action, listener, cls, false);
+    }
+
+    public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+            Class cls, boolean allowMultiple) {
+        if (!isDebuggable) {
+            // Never ever ever allow these on production builds, they are only for prototyping.
+            return;
+        }
+        mPluginPrefs.addAction(action);
+        PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
+                allowMultiple, mBackgroundThread.getLooper(), cls, this);
+        p.loadAll();
+        mPluginMap.put(listener, p);
+        startListening();
+    }
+
+    public void removePluginListener(PluginListener<?> listener) {
+        if (!isDebuggable) {
+            // Never ever ever allow these on production builds, they are only for prototyping.
+            return;
+        }
+        if (!mPluginMap.containsKey(listener)) return;
+        mPluginMap.remove(listener).destroy();
+        stopListening();
+    }
+
+    private void startListening() {
+        if (mListening) return;
+        mListening = true;
+        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addAction(PLUGIN_CHANGED);
+        filter.addAction(DISABLE_PLUGIN);
+        filter.addDataScheme("package");
+        mContext.registerReceiver(this, filter);
+        filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
+        mContext.registerReceiver(this, filter);
+    }
+
+    private void stopListening() {
+        // Never stop listening if a one-shot is present.
+        if (!mListening || mHasOneShot) return;
+        mListening = false;
+        mContext.unregisterReceiver(this);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
+            for (PluginInstanceManager manager : mPluginMap.values()) {
+                manager.loadAll();
+            }
+        } else if (DISABLE_PLUGIN.equals(intent.getAction())) {
+            Uri uri = intent.getData();
+            ComponentName component = ComponentName.unflattenFromString(
+                    uri.toString().substring(10));
+            mContext.getPackageManager().setComponentEnabledSetting(component,
+                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                    PackageManager.DONT_KILL_APP);
+            mContext.getSystemService(NotificationManager.class).cancel(component.getClassName(),
+                    SystemMessage.NOTE_PLUGIN);
+        } else {
+            Uri data = intent.getData();
+            String pkg = data.getEncodedSchemeSpecificPart();
+            if (mOneShotPackages.contains(pkg)) {
+                int icon = mContext.getResources().getIdentifier("tuner", "drawable",
+                        mContext.getPackageName());
+                int color = Resources.getSystem().getIdentifier(
+                        "system_notification_accent_color", "color", "android");
+                String label = pkg;
+                try {
+                    PackageManager pm = mContext.getPackageManager();
+                    label = pm.getApplicationInfo(pkg, 0).loadLabel(pm).toString();
+                } catch (NameNotFoundException e) {
+                }
+                // Localization not required as this will never ever appear in a user build.
+                final Notification.Builder nb =
+                        new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+                                .setSmallIcon(icon)
+                                .setWhen(0)
+                                .setShowWhen(false)
+                                .setPriority(Notification.PRIORITY_MAX)
+                                .setVisibility(Notification.VISIBILITY_PUBLIC)
+                                .setColor(mContext.getColor(color))
+                                .setContentTitle("Plugin \"" + label + "\" has updated")
+                                .setContentText("Restart SysUI for changes to take effect.");
+                Intent i = new Intent("com.android.systemui.action.RESTART").setData(
+                            Uri.parse("package://" + pkg));
+                PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, i, 0);
+                nb.addAction(new Action.Builder(null, "Restart SysUI", pi).build());
+                mContext.getSystemService(NotificationManager.class).notifyAsUser(pkg,
+                        SystemMessage.NOTE_PLUGIN, nb.build(), UserHandle.ALL);
+            }
+            clearClassLoader(pkg);
+            if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+                for (PluginInstanceManager manager : mPluginMap.values()) {
+                    manager.onPackageChange(pkg);
+                }
+            } else {
+                for (PluginInstanceManager manager : mPluginMap.values()) {
+                    manager.onPackageRemoved(pkg);
+                }
+            }
+        }
+    }
+
+    public ClassLoader getClassLoader(String sourceDir, String pkg) {
+        if (mClassLoaders.containsKey(pkg)) {
+            return mClassLoaders.get(pkg);
+        }
+        ClassLoader classLoader = new PathClassLoader(sourceDir, getParentClassLoader());
+        mClassLoaders.put(pkg, classLoader);
+        return classLoader;
+    }
+
+    private void clearClassLoader(String pkg) {
+        mClassLoaders.remove(pkg);
+    }
+
+    ClassLoader getParentClassLoader() {
+        if (mParentClassLoader == null) {
+            // Lazily load this so it doesn't have any effect on devices without plugins.
+            mParentClassLoader = new ClassLoaderFilter(getClass().getClassLoader(),
+                    "com.android.systemui.plugin");
+        }
+        return mParentClassLoader;
+    }
+
+    public Context getContext(ApplicationInfo info, String pkg) throws NameNotFoundException {
+        ClassLoader classLoader = getClassLoader(info.sourceDir, pkg);
+        return new PluginContextWrapper(mContext.createApplicationContext(info, 0), classLoader);
+    }
+
+    public <T> boolean dependsOn(Plugin p, Class<T> cls) {
+        for (int i = 0; i < mPluginMap.size(); i++) {
+            if (mPluginMap.valueAt(i).dependsOn(p, cls)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @VisibleForTesting
+    public static class PluginInstanceManagerFactory {
+        public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
+                String action, PluginListener<T> listener, boolean allowMultiple, Looper looper,
+                Class<?> cls, PluginManagerImpl manager) {
+            return new PluginInstanceManager(context, action, listener, allowMultiple, looper,
+                    new VersionInfo().addClass(cls), manager);
+        }
+    }
+
+    // This allows plugins to include any libraries or copied code they want by only including
+    // classes from the plugin library.
+    private static class ClassLoaderFilter extends ClassLoader {
+        private final String mPackage;
+        private final ClassLoader mBase;
+
+        public ClassLoaderFilter(ClassLoader base, String pkg) {
+            super(ClassLoader.getSystemClassLoader());
+            mBase = base;
+            mPackage = pkg;
+        }
+
+        @Override
+        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+            if (!name.startsWith(mPackage)) super.loadClass(name, resolve);
+            return mBase.loadClass(name);
+        }
+    }
+
+    private class PluginExceptionHandler implements UncaughtExceptionHandler {
+        private final UncaughtExceptionHandler mHandler;
+
+        private PluginExceptionHandler(UncaughtExceptionHandler handler) {
+            mHandler = handler;
+        }
+
+        @Override
+        public void uncaughtException(Thread thread, Throwable throwable) {
+            if (SystemProperties.getBoolean("plugin.debugging", false)) {
+                mHandler.uncaughtException(thread, throwable);
+                return;
+            }
+            // Search for and disable plugins that may have been involved in this crash.
+            boolean disabledAny = checkStack(throwable);
+            if (!disabledAny) {
+                // We couldn't find any plugins involved in this crash, just to be safe
+                // disable all the plugins, so we can be sure that SysUI is running as
+                // best as possible.
+                for (PluginInstanceManager manager : mPluginMap.values()) {
+                    manager.disableAll();
+                }
+            }
+
+            // Run the normal exception handler so we can crash and cleanup our state.
+            mHandler.uncaughtException(thread, throwable);
+        }
+
+        private boolean checkStack(Throwable throwable) {
+            if (throwable == null) return false;
+            boolean disabledAny = false;
+            for (StackTraceElement element : throwable.getStackTrace()) {
+                for (PluginInstanceManager manager : mPluginMap.values()) {
+                    disabledAny |= manager.checkAndDisable(element.getClassName());
+                }
+            }
+            return disabledAny | checkStack(throwable.getCause());
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index c0fb4d5..198cb9e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -41,10 +41,10 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QS;
-import com.android.systemui.qs.QSDetailClipper;
 import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
+import com.android.systemui.qs.QSDetailClipper;
 import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.KeyguardMonitor.Callback;
 
@@ -74,6 +74,7 @@
     private boolean mFinishedFetchingTiles = false;
     private int mX;
     private int mY;
+    private boolean mOpening;
 
     public QSCustomizer(Context context, AttributeSet attrs) {
         super(new ContextThemeWrapper(context, R.style.edit_theme), attrs);
@@ -140,6 +141,7 @@
             mY = y;
             MetricsLogger.visible(getContext(), MetricsProto.MetricsEvent.QS_EDIT);
             isShown = true;
+            mOpening = true;
             setTileSpecs();
             setVisibility(View.VISIBLE);
             mClipper.animateCircularClip(x, y, true, mExpandAnimationListener);
@@ -226,7 +228,7 @@
     }
 
     private final Callback mKeyguardCallback = () -> {
-        if (Dependency.get(KeyguardMonitor.class).isShowing()) {
+        if (Dependency.get(KeyguardMonitor.class).isShowing() && !mOpening) {
             hide(0, 0);
         }
     };
@@ -237,11 +239,13 @@
             if (isShown) {
                 setCustomizing(true);
             }
+            mOpening = false;
             mNotifQsContainer.setCustomizerAnimating(false);
         }
 
         @Override
         public void onAnimationCancel(Animator animation) {
+            mOpening = false;
             mNotifQsContainer.setCustomizerAnimating(false);
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index 6a66fca7..8882cab 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents.views;
 
+import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
@@ -303,7 +304,7 @@
         // TODO: Sometimes targetStackId is not initialized after reboot, so we also have to
         // check for INVALID_STACK_ID
         if (targetStackId == FULLSCREEN_WORKSPACE_STACK_ID || targetStackId == DOCKED_STACK_ID
-                || targetStackId == INVALID_STACK_ID) {
+                || targetStackId == ASSISTANT_STACK_ID || targetStackId == INVALID_STACK_ID) {
             if (taskView == null) {
                 specs.add(composeOffscreenAnimationSpec(task, offscreenTaskRect));
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index dceeb74..0924089 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -29,7 +29,6 @@
 import android.os.BatteryStats;
 import android.os.Handler;
 import android.os.Message;
-import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -95,12 +94,20 @@
     private final DevicePolicyManager mDevicePolicyManager;
     private boolean mDozing;
 
+    /**
+     * Creates a new KeyguardIndicationController and registers callbacks.
+     */
     public KeyguardIndicationController(Context context, ViewGroup indicationArea,
             LockIcon lockIcon) {
         this(context, indicationArea, lockIcon,
                 WakeLock.createPartial(context, "Doze:KeyguardIndication"));
+
+        registerCallbacks(KeyguardUpdateMonitor.getInstance(context));
     }
 
+    /**
+     * Creates a new KeyguardIndicationController for testing. Does *not* register callbacks.
+     */
     @VisibleForTesting
     KeyguardIndicationController(Context context, ViewGroup indicationArea, LockIcon lockIcon,
                 WakeLock wakeLock) {
@@ -124,12 +131,15 @@
         mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
 
-        KeyguardUpdateMonitor.getInstance(context).registerCallback(getKeyguardCallback());
-        context.registerReceiverAsUser(mTickReceiver, UserHandle.SYSTEM,
+        updateDisclosure();
+    }
+
+    private void registerCallbacks(KeyguardUpdateMonitor monitor) {
+        monitor.registerCallback(getKeyguardCallback());
+
+        mContext.registerReceiverAsUser(mTickReceiver, UserHandle.SYSTEM,
                 new IntentFilter(Intent.ACTION_TIME_TICK), null,
                 Dependency.get(Dependency.TIME_TICK_HANDLER));
-
-        updateDisclosure();
     }
 
     /**
@@ -331,7 +341,7 @@
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
     }
 
-    BroadcastReceiver mTickReceiver = new BroadcastReceiver() {
+    private final BroadcastReceiver mTickReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             mHandler.post(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index af464c6..5db5498 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -136,12 +136,7 @@
         if (channel.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID)) {
             channelNameText = mContext.getString(R.string.notification_header_default_channel);
         } else {
-            if (info != null && channel.getNameResId() != 0) {
-                channelNameText = pm.getText(pkg, channel.getNameResId(), info);
-            }
-            if (channel.getName() != null) {
-                channelNameText = channel.getName();
-            }
+            channelNameText = channel.getName();
         }
         ((TextView) findViewById(R.id.pkgname)).setText(appName);
         ((TextView) findViewById(R.id.channel_name)).setText(channelNameText);
@@ -154,12 +149,7 @@
                         iNotificationManager.getNotificationChannelGroupForPackage(
                                 channel.getGroup(), pkg, appUid);
                 if (notificationChannelGroup != null) {
-                    if (info != null && notificationChannelGroup.getNameResId() != 0) {
-                        groupName = pm.getText(pkg, notificationChannelGroup.getNameResId(), info);
-                    }
-                    if (notificationChannelGroup.getName() != null) {
-                        groupName = notificationChannelGroup.getName();
-                    }
+                    groupName = notificationChannelGroup.getName();
                 }
             } catch (RemoteException e) {
                 Log.e(TAG, e.toString());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 6cd3eae..aa0fcbd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -45,6 +45,7 @@
 public class CollapsedStatusBarFragment extends Fragment implements CommandQueue.Callbacks {
 
     public static final String TAG = "CollapsedStatusBarFragment";
+    private static final String EXTRA_PANEL_STATE = "panel_state";
     private PhoneStatusBarView mStatusBar;
     private KeyguardMonitor mKeyguardMonitor;
     private NetworkController mNetworkController;
@@ -73,14 +74,23 @@
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         mStatusBar = (PhoneStatusBarView) view;
-        mDarkIconManager = new DarkIconManager((LinearLayout) view.findViewById(R.id.statusIcons));
+        if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {
+            mStatusBar.go(savedInstanceState.getInt(EXTRA_PANEL_STATE));
+        }
+        mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons));
         Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
-        mSystemIconArea = (LinearLayout) mStatusBar.findViewById(R.id.system_icon_area);
+        mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
         mSignalClusterView = reinflateSignalCluster(mStatusBar);
         Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mSignalClusterView);
     }
 
     @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putInt(EXTRA_PANEL_STATE, mStatusBar.getState());
+    }
+
+    @Override
     public void onResume() {
         super.onResume();
         SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
@@ -101,8 +111,7 @@
 
     public void initNotificationIconArea(NotificationIconAreaController
             notificationIconAreaController) {
-        ViewGroup notificationIconArea = (ViewGroup) mStatusBar
-                .findViewById(R.id.notification_icon_area);
+        ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);
         mNotificationIconAreaInner =
                 notificationIconAreaController.getNotificationInnerAreaView();
         if (mNotificationIconAreaInner.getParent() != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 99f8aaf..2b52b48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -35,6 +35,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Configuration;
+import android.database.ContentObserver;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.inputmethodservice.InputMethodService;
@@ -46,6 +47,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.support.annotation.VisibleForTesting;
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
@@ -104,6 +106,7 @@
     private int mNavigationIconHints = 0;
     private int mNavigationBarMode;
     private AccessibilityManager mAccessibilityManager;
+    private MagnificationContentObserver mMagnificationObserver;
 
     private int mDisabledFlags1;
     private StatusBar mStatusBar;
@@ -135,6 +138,12 @@
         mAccessibilityManager = getContext().getSystemService(AccessibilityManager.class);
         mAccessibilityManager.addAccessibilityServicesStateChangeListener(
                 this::updateAccessibilityServicesState);
+        mMagnificationObserver = new MagnificationContentObserver(
+                getContext().getMainThreadHandler());
+        getContext().getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED), false,
+                mMagnificationObserver);
+
         if (savedInstanceState != null) {
             mDisabledFlags1 = savedInstanceState.getInt(EXTRA_DISABLE_STATE, 0);
         }
@@ -142,7 +151,7 @@
 
         try {
             WindowManagerGlobal.getWindowManagerService()
-                    .watchRotation(mRotationWatcher);
+                    .watchRotation(mRotationWatcher, getContext().getDisplay().getDisplayId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -154,6 +163,7 @@
         mCommandQueue.removeCallbacks(this);
         mAccessibilityManager.removeAccessibilityServicesStateChangeListener(
                 this::updateAccessibilityServicesState);
+        getContext().getContentResolver().unregisterContentObserver(mMagnificationObserver);
         try {
             WindowManagerGlobal.getWindowManagerService()
                     .removeRotationWatcher(mRotationWatcher);
@@ -387,6 +397,7 @@
         ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
         accessibilityButton.setOnClickListener(this::onAccessibilityClick);
         accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
+        updateAccessibilityServicesState();
     }
 
     private boolean onHomeTouch(View v, MotionEvent event) {
@@ -550,10 +561,18 @@
     }
 
     private void updateAccessibilityServicesState() {
+        int requestingServices = 0;
+        try {
+            if (Settings.Secure.getInt(getContext().getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED) == 1) {
+                requestingServices++;
+            }
+        } catch (Settings.SettingNotFoundException e) {
+        }
+
         final List<AccessibilityServiceInfo> services =
                 mAccessibilityManager.getEnabledAccessibilityServiceList(
                         AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
-        int requestingServices = 0;
         for (int i = services.size() - 1; i >= 0; --i) {
             AccessibilityServiceInfo info = services.get(i);
             if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) {
@@ -600,6 +619,18 @@
         mNavigationBarView.getBarTransitions().finishAnimations();
     }
 
+    private class MagnificationContentObserver extends ContentObserver {
+
+        public MagnificationContentObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            NavigationBarFragment.this.updateAccessibilityServicesState();
+        }
+    }
+
     private final Stub mRotationWatcher = new Stub() {
         @Override
         public void onRotationChanged(int rotation) throws RemoteException {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 23d3816..cefe972 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -46,6 +46,10 @@
         mState = state;
     }
 
+    public int getState() {
+        return mState;
+    }
+
     public PanelBar(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
index a87b50a..d6c080a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
@@ -129,6 +129,7 @@
 
     public void notifyKeyguardDoneFading() {
         mKeyguardFadingAway = false;
+        mKeyguardGoingAway = false;
         notifyKeyguardChanged();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 7c4f2ee..369ce69 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -1,226 +1,94 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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
  *
- * 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
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
  */
+
 package com.android.systemui.tuner;
 
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.UserInfo;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.provider.Settings;
-import android.provider.Settings.Secure;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.ArraySet;
 
 import static android.provider.Settings.System.SHOW_BATTERY_PERCENT;
 import com.android.systemui.DemoMode;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
-import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.SystemUI;
-import com.android.systemui.SystemUIApplication;
-import com.android.systemui.settings.CurrentUserTracker;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
-import com.android.systemui.util.leak.LeakDetector;
 
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
-
-
-public class TunerService {
+public abstract class TunerService {
 
     public static final String ACTION_CLEAR = "com.android.systemui.action.CLEAR_TUNER";
 
-    private static final String TUNER_VERSION = "sysui_tuner_version";
+    public abstract void clearAll();
+    public abstract void destroy();
 
-    private static final int CURRENT_TUNER_VERSION = 1;
+    public abstract String getValue(String setting);
+    public abstract int getValue(String setting, int def);
+    public abstract String getValue(String setting, String def);
 
-    private final Observer mObserver = new Observer();
-    // Map of Uris we listen on to their settings keys.
-    private final ArrayMap<Uri, String> mListeningUris = new ArrayMap<>();
-    // Map of settings keys to the listener.
-    private final HashMap<String, Set<Tunable>> mTunableLookup = new HashMap<>();
-    // Set of all tunables, used for leak detection.
-    private final HashSet<Tunable> mTunables = LeakDetector.ENABLED ? new HashSet<>() : null;
-    private final Context mContext;
+    public abstract void setValue(String setting, String value);
+    public abstract void setValue(String setting, int value);
 
-    private ContentResolver mContentResolver;
-    private int mCurrentUser;
-    private CurrentUserTracker mUserTracker;
+    public abstract void addTunable(Tunable tunable, String... keys);
+    public abstract void removeTunable(Tunable tunable);
 
-    public TunerService(Context context) {
-        mContext = context;
-        mContentResolver = mContext.getContentResolver();
+    public interface Tunable {
+        void onTuningChanged(String key, String newValue);
+    }
 
-        for (UserInfo user : UserManager.get(mContext).getUsers()) {
-            mCurrentUser = user.getUserHandle().getIdentifier();
-            if (getValue(TUNER_VERSION, 0) != CURRENT_TUNER_VERSION) {
-                upgradeTuner(getValue(TUNER_VERSION, 0), CURRENT_TUNER_VERSION);
+    private static Context userContext(Context context) {
+        try {
+            return context.createPackageContextAsUser(context.getPackageName(), 0,
+                    new UserHandle(ActivityManager.getCurrentUser()));
+        } catch (NameNotFoundException e) {
+            return context;
+        }
+    }
+
+    public static final void setTunerEnabled(Context context, boolean enabled) {
+        userContext(context).getPackageManager().setComponentEnabledSetting(
+                new ComponentName(context, TunerActivity.class),
+                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                PackageManager.DONT_KILL_APP);
+
+        userContext(context).getPackageManager().setComponentEnabledSetting(
+                new ComponentName(context, TunerActivity.ACTIVITY_ALIAS_NAME),
+                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                PackageManager.DONT_KILL_APP);
+    }
+
+    public static final boolean isTunerEnabled(Context context) {
+        return userContext(context).getPackageManager().getComponentEnabledSetting(
+                new ComponentName(context, TunerActivity.class))
+                == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+    }
+
+    public static class ClearReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (ACTION_CLEAR.equals(intent.getAction())) {
+                Dependency.get(TunerService.class).clearAll();
             }
         }
-
-        mCurrentUser = ActivityManager.getCurrentUser();
-        mUserTracker = new CurrentUserTracker(mContext) {
-            @Override
-            public void onUserSwitched(int newUserId) {
-                mCurrentUser = newUserId;
-                reloadAll();
-                reregisterAll();
-            }
-        };
-        mUserTracker.startTracking();
-    }
-
-    public void destroy() {
-        mUserTracker.stopTracking();
-    }
-
-    private void upgradeTuner(int oldVersion, int newVersion) {
-        if (oldVersion < 1) {
-            String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST);
-            if (blacklistStr != null) {
-                ArraySet<String> iconBlacklist =
-                        StatusBarIconController.getIconBlacklist(blacklistStr);
-
-                iconBlacklist.add("rotate");
-                iconBlacklist.add("headset");
-
-                Settings.Secure.putStringForUser(mContentResolver,
-                        StatusBarIconController.ICON_BLACKLIST,
-                        TextUtils.join(",", iconBlacklist), mCurrentUser);
-            }
-        }
-        setValue(TUNER_VERSION, newVersion);
-    }
-
-    public String getValue(String setting) {
-        return Settings.Secure.getStringForUser(mContentResolver, setting, mCurrentUser);
-    }
-
-    public void setValue(String setting, String value) {
-         Settings.Secure.putStringForUser(mContentResolver, setting, value, mCurrentUser);
-    }
-
-    public int getValue(String setting, int def) {
-        return Settings.Secure.getIntForUser(mContentResolver, setting, def, mCurrentUser);
-    }
-
-    public String getValue(String setting, String def) {
-        String ret = Secure.getStringForUser(mContentResolver, setting, mCurrentUser);
-        if (ret == null) return def;
-        return ret;
-    }
-
-    public void setValue(String setting, int value) {
-         Settings.Secure.putIntForUser(mContentResolver, setting, value, mCurrentUser);
-    }
-
-    public void addTunable(Tunable tunable, String... keys) {
-        for (String key : keys) {
-            addTunable(tunable, key);
-        }
-    }
-
-    private void addTunable(Tunable tunable, String key) {
-        if (!mTunableLookup.containsKey(key)) {
-            mTunableLookup.put(key, new ArraySet<Tunable>());
-        }
-        mTunableLookup.get(key).add(tunable);
-        if (LeakDetector.ENABLED) {
-            mTunables.add(tunable);
-            Dependency.get(LeakDetector.class).trackCollection(mTunables, "TunerService.mTunables");
-        }
-        Uri uri = Settings.Secure.getUriFor(key);
-        if (!mListeningUris.containsKey(uri)) {
-            mListeningUris.put(uri, key);
-            mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
-        }
-        // Send the first state.
-        String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);
-        tunable.onTuningChanged(key, value);
-    }
-
-    public void removeTunable(Tunable tunable) {
-        for (Set<Tunable> list : mTunableLookup.values()) {
-            list.remove(tunable);
-        }
-        if (LeakDetector.ENABLED) {
-            mTunables.remove(tunable);
-        }
-    }
-
-    protected void reregisterAll() {
-        if (mListeningUris.size() == 0) {
-            return;
-        }
-        mContentResolver.unregisterContentObserver(mObserver);
-        for (Uri uri : mListeningUris.keySet()) {
-            mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
-        }
-    }
-
-    public void reloadSetting(Uri uri) {
-        String key = mListeningUris.get(uri);
-        Set<Tunable> tunables = mTunableLookup.get(key);
-        if (tunables == null) {
-            return;
-        }
-        String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);
-        for (Tunable tunable : tunables) {
-            tunable.onTuningChanged(key, value);
-        }
-    }
-
-    private void reloadAll() {
-        for (String key : mTunableLookup.keySet()) {
-            String value = Settings.Secure.getStringForUser(mContentResolver, key,
-                    mCurrentUser);
-            for (Tunable tunable : mTunableLookup.get(key)) {
-                tunable.onTuningChanged(key, value);
-            }
-        }
-    }
-
-    public void clearAll() {
-        // A couple special cases.
-        Settings.Global.putString(mContentResolver, DemoMode.DEMO_MODE_ALLOWED, null);
-        Settings.System.putString(mContentResolver,
-                SHOW_BATTERY_PERCENT, null);
-        Intent intent = new Intent(DemoMode.ACTION_DEMO);
-        intent.putExtra(DemoMode.EXTRA_COMMAND, DemoMode.COMMAND_EXIT);
-        mContext.sendBroadcast(intent);
-
-        for (String key : mTunableLookup.keySet()) {
-            Settings.Secure.putString(mContentResolver, key, null);
-        }
     }
 
     public static final void showResetRequest(final Context context, final Runnable onDisabled) {
@@ -247,59 +115,4 @@
         });
         dialog.show();
     }
-
-    public static final void setTunerEnabled(Context context, boolean enabled) {
-        userContext(context).getPackageManager().setComponentEnabledSetting(
-                new ComponentName(context, TunerActivity.class),
-                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
-                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
-                        PackageManager.DONT_KILL_APP);
-
-        userContext(context).getPackageManager().setComponentEnabledSetting(
-                new ComponentName(context, TunerActivity.ACTIVITY_ALIAS_NAME),
-                enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
-                        : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
-                PackageManager.DONT_KILL_APP);
-    }
-
-    public static final boolean isTunerEnabled(Context context) {
-        return userContext(context).getPackageManager().getComponentEnabledSetting(
-                new ComponentName(context, TunerActivity.class))
-                == PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-    }
-
-    private static Context userContext(Context context) {
-        try {
-            return context.createPackageContextAsUser(context.getPackageName(), 0,
-                    new UserHandle(ActivityManager.getCurrentUser()));
-        } catch (NameNotFoundException e) {
-            return context;
-        }
-    }
-
-    private class Observer extends ContentObserver {
-        public Observer() {
-            super(new Handler(Looper.getMainLooper()));
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri, int userId) {
-            if (userId == ActivityManager.getCurrentUser()) {
-                reloadSetting(uri);
-            }
-        }
-    }
-
-    public interface Tunable {
-        void onTuningChanged(String key, String newValue);
-    }
-
-    public static class ClearReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (ACTION_CLEAR.equals(intent.getAction())) {
-                Dependency.get(TunerService.class).clearAll();
-            }
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
new file mode 100644
index 0000000..8e584bc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.tuner;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.systemui.DemoMode;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.SystemUI;
+import com.android.systemui.SystemUIApplication;
+import com.android.systemui.settings.CurrentUserTracker;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.util.leak.LeakDetector;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+
+public class TunerServiceImpl extends TunerService {
+
+    private static final String TUNER_VERSION = "sysui_tuner_version";
+
+    private static final int CURRENT_TUNER_VERSION = 1;
+
+    private final Observer mObserver = new Observer();
+    // Map of Uris we listen on to their settings keys.
+    private final ArrayMap<Uri, String> mListeningUris = new ArrayMap<>();
+    // Map of settings keys to the listener.
+    private final HashMap<String, Set<Tunable>> mTunableLookup = new HashMap<>();
+    // Set of all tunables, used for leak detection.
+    private final HashSet<Tunable> mTunables = LeakDetector.ENABLED ? new HashSet<>() : null;
+    private final Context mContext;
+
+    private ContentResolver mContentResolver;
+    private int mCurrentUser;
+    private CurrentUserTracker mUserTracker;
+
+    public TunerServiceImpl(Context context) {
+        mContext = context;
+        mContentResolver = mContext.getContentResolver();
+
+        for (UserInfo user : UserManager.get(mContext).getUsers()) {
+            mCurrentUser = user.getUserHandle().getIdentifier();
+            if (getValue(TUNER_VERSION, 0) != CURRENT_TUNER_VERSION) {
+                upgradeTuner(getValue(TUNER_VERSION, 0), CURRENT_TUNER_VERSION);
+            }
+        }
+
+        mCurrentUser = ActivityManager.getCurrentUser();
+        mUserTracker = new CurrentUserTracker(mContext) {
+            @Override
+            public void onUserSwitched(int newUserId) {
+                mCurrentUser = newUserId;
+                reloadAll();
+                reregisterAll();
+            }
+        };
+        mUserTracker.startTracking();
+    }
+
+    @Override
+    public void destroy() {
+        mUserTracker.stopTracking();
+    }
+
+    private void upgradeTuner(int oldVersion, int newVersion) {
+        if (oldVersion < 1) {
+            String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST);
+            if (blacklistStr != null) {
+                ArraySet<String> iconBlacklist =
+                        StatusBarIconController.getIconBlacklist(blacklistStr);
+
+                iconBlacklist.add("rotate");
+                iconBlacklist.add("headset");
+
+                Settings.Secure.putStringForUser(mContentResolver,
+                        StatusBarIconController.ICON_BLACKLIST,
+                        TextUtils.join(",", iconBlacklist), mCurrentUser);
+            }
+        }
+        setValue(TUNER_VERSION, newVersion);
+    }
+
+    @Override
+    public String getValue(String setting) {
+        return Settings.Secure.getStringForUser(mContentResolver, setting, mCurrentUser);
+    }
+
+    @Override
+    public void setValue(String setting, String value) {
+         Settings.Secure.putStringForUser(mContentResolver, setting, value, mCurrentUser);
+    }
+
+    @Override
+    public int getValue(String setting, int def) {
+        return Settings.Secure.getIntForUser(mContentResolver, setting, def, mCurrentUser);
+    }
+
+    @Override
+    public String getValue(String setting, String def) {
+        String ret = Secure.getStringForUser(mContentResolver, setting, mCurrentUser);
+        if (ret == null) return def;
+        return ret;
+    }
+
+    @Override
+    public void setValue(String setting, int value) {
+         Settings.Secure.putIntForUser(mContentResolver, setting, value, mCurrentUser);
+    }
+
+    @Override
+    public void addTunable(Tunable tunable, String... keys) {
+        for (String key : keys) {
+            addTunable(tunable, key);
+        }
+    }
+
+    private void addTunable(Tunable tunable, String key) {
+        if (!mTunableLookup.containsKey(key)) {
+            mTunableLookup.put(key, new ArraySet<Tunable>());
+        }
+        mTunableLookup.get(key).add(tunable);
+        if (LeakDetector.ENABLED) {
+            mTunables.add(tunable);
+            Dependency.get(LeakDetector.class).trackCollection(mTunables, "TunerService.mTunables");
+        }
+        Uri uri = Settings.Secure.getUriFor(key);
+        if (!mListeningUris.containsKey(uri)) {
+            mListeningUris.put(uri, key);
+            mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
+        }
+        // Send the first state.
+        String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);
+        tunable.onTuningChanged(key, value);
+    }
+
+    @Override
+    public void removeTunable(Tunable tunable) {
+        for (Set<Tunable> list : mTunableLookup.values()) {
+            list.remove(tunable);
+        }
+        if (LeakDetector.ENABLED) {
+            mTunables.remove(tunable);
+        }
+    }
+
+    protected void reregisterAll() {
+        if (mListeningUris.size() == 0) {
+            return;
+        }
+        mContentResolver.unregisterContentObserver(mObserver);
+        for (Uri uri : mListeningUris.keySet()) {
+            mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
+        }
+    }
+
+    private void reloadSetting(Uri uri) {
+        String key = mListeningUris.get(uri);
+        Set<Tunable> tunables = mTunableLookup.get(key);
+        if (tunables == null) {
+            return;
+        }
+        String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);
+        for (Tunable tunable : tunables) {
+            tunable.onTuningChanged(key, value);
+        }
+    }
+
+    private void reloadAll() {
+        for (String key : mTunableLookup.keySet()) {
+            String value = Settings.Secure.getStringForUser(mContentResolver, key,
+                    mCurrentUser);
+            for (Tunable tunable : mTunableLookup.get(key)) {
+                tunable.onTuningChanged(key, value);
+            }
+        }
+    }
+
+    @Override
+    public void clearAll() {
+        // A couple special cases.
+        Settings.Global.putString(mContentResolver, DemoMode.DEMO_MODE_ALLOWED, null);
+        Intent intent = new Intent(DemoMode.ACTION_DEMO);
+        intent.putExtra(DemoMode.EXTRA_COMMAND, DemoMode.COMMAND_EXIT);
+        mContext.sendBroadcast(intent);
+
+        for (String key : mTunableLookup.keySet()) {
+            Settings.Secure.putString(mContentResolver, key, null);
+        }
+    }
+
+    private class Observer extends ContentObserver {
+        public Observer() {
+            super(new Handler(Looper.getMainLooper()));
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri, int userId) {
+            if (userId == ActivityManager.getCurrentUser()) {
+                reloadSetting(uri);
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index 5df3beb..cd85a76 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -38,19 +38,19 @@
         nm.createNotificationChannels(Arrays.asList(
                 new NotificationChannel(
                         ALERTS,
-                        R.string.notification_channel_alerts,
+                        context.getString(R.string.notification_channel_alerts),
                         NotificationManager.IMPORTANCE_HIGH),
                 new NotificationChannel(
                         SCREENSHOTS,
-                        R.string.notification_channel_screenshot,
+                        context.getString(R.string.notification_channel_screenshot),
                         NotificationManager.IMPORTANCE_LOW),
                 new NotificationChannel(
                         GENERAL,
-                        R.string.notification_channel_general,
+                        context.getString(R.string.notification_channel_general),
                         NotificationManager.IMPORTANCE_MIN),
                 new NotificationChannel(
                         STORAGE,
-                        R.string.notification_channel_storage,
+                        context.getString(R.string.notification_channel_storage),
                         isTv(context)
                                 ? NotificationManager.IMPORTANCE_DEFAULT
                                 : NotificationManager.IMPORTANCE_LOW)
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 760d875..10ff8ad 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -26,6 +26,7 @@
 LOCAL_PROTO_JAVA_OUTPUT_PARAMS := optional_field_style=accessors
 
 LOCAL_PACKAGE_NAME := SystemUITests
+LOCAL_COMPATIBILITY_SUITE := device-tests
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     $(call all-Iaidl-files-under, src) \
@@ -49,7 +50,8 @@
     mockito-updated-target-minus-junit4 \
     SystemUI-proto \
     SystemUI-tags \
-    legacy-android-test
+    legacy-android-test \
+    testables
 
 LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.car
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
index 27955ec..c297ae8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
@@ -39,21 +39,21 @@
     @Test
     public void testClassDependency() {
         FlashlightController f = mock(FlashlightController.class);
-        injectTestDependency(FlashlightController.class, f);
+        mDependency.injectTestDependency(FlashlightController.class, f);
         Assert.assertEquals(f, Dependency.get(FlashlightController.class));
     }
 
     @Test
     public void testStringDependency() {
         Looper l = Looper.getMainLooper();
-        injectTestDependency(Dependency.BG_LOOPER, l);
+        mDependency.injectTestDependency(Dependency.BG_LOOPER, l);
         assertEquals(l, Dependency.get(Dependency.BG_LOOPER));
     }
 
     @Test
     public void testDump() {
         Dumpable d = mock(Dumpable.class);
-        injectTestDependency(DUMPABLE, d);
+        mDependency.injectTestDependency(DUMPABLE, d);
         Dependency.get(DUMPABLE);
         mDependency.dump(null, mock(PrintWriter.class), null);
         verify(d).dump(eq(null), any(), eq(null));
@@ -62,7 +62,7 @@
     @Test
     public void testConfigurationChanged() {
         ConfigurationChangedReceiver d = mock(ConfigurationChangedReceiver.class);
-        injectTestDependency(CONFIGURATION_CHANGED_RECEIVER, d);
+        mDependency.injectTestDependency(CONFIGURATION_CHANGED_RECEIVER, d);
         Dependency.get(CONFIGURATION_CHANGED_RECEIVER);
         mDependency.onConfigurationChanged(null);
         verify(d).onConfigurationChanged(eq(null));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
new file mode 100644
index 0000000..15cebc7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.app.Fragment;
+import android.support.test.InstrumentationRegistry;
+import android.testing.BaseFragmentTest;
+
+import com.android.systemui.utils.leaks.LeakCheckedTest;
+import com.android.systemui.utils.leaks.LeakCheckedTest.SysuiLeakCheck;
+
+import org.junit.Before;
+import org.junit.Rule;
+
+public abstract class SysuiBaseFragmentTest extends BaseFragmentTest {
+
+    public static final Class<?>[] ALL_SUPPORTED_CLASSES = LeakCheckedTest.ALL_SUPPORTED_CLASSES;
+
+    @Rule
+    public final SysuiLeakCheck mLeakCheck = new SysuiLeakCheck();
+
+    protected final TestableDependency mDependency = new TestableDependency(mContext);
+    protected SysuiTestableContext mSysuiContext;
+
+    public SysuiBaseFragmentTest(Class<? extends Fragment> cls) {
+        super(cls);
+    }
+
+    @Before
+    public void SysuiSetup() {
+        System.setProperty("dexmaker.share_classloader", "true");
+        SystemUIFactory.createFromConfig(mContext);
+        // TODO: Figure out another way to give reference to a SysuiTestableContext.
+        mSysuiContext = (SysuiTestableContext) mContext;
+    }
+
+    @Override
+    protected SysuiTestableContext getContext() {
+        return new SysuiTestableContext(InstrumentationRegistry.getContext(), mLeakCheck);
+    }
+
+    public void injectLeakCheckedDependencies(Class<?>... cls) {
+        for (Class<?> c : cls) {
+            injectLeakCheckedDependency(c);
+        }
+    }
+
+    public <T> void injectLeakCheckedDependency(Class<T> c) {
+        mDependency.injectTestDependency(c, mLeakCheck.getLeakChecker(c));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index c0e7e80..aadae0f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -15,49 +15,38 @@
  */
 package com.android.systemui;
 
-import static org.mockito.Mockito.mock;
-
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.MessageQueue;
 import android.support.test.InstrumentationRegistry;
-import android.util.ArrayMap;
+import android.testing.LeakCheck;
 
-import com.android.systemui.Dependency.DependencyKey;
-import com.android.systemui.utils.TestableContext;
-import com.android.systemui.utils.leaks.Tracker;
-
-import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 
 /**
  * Base class that does System UI specific setup.
  */
 public abstract class SysuiTestCase {
 
-    private Throwable mException;
     private Handler mHandler;
-    protected TestableContext mContext;
-    protected TestDependency mDependency;
+    @Rule
+    public SysuiTestableContext mContext = new SysuiTestableContext(
+            InstrumentationRegistry.getContext(), getLeakCheck());
+    public TestableDependency mDependency = new TestableDependency(mContext);
 
     @Before
     public void SysuiSetup() throws Exception {
-        mException = null;
         System.setProperty("dexmaker.share_classloader", "true");
-        mContext = new TestableContext(InstrumentationRegistry.getTargetContext(), this);
         SystemUIFactory.createFromConfig(mContext);
-        mDependency = new TestDependency();
-        mDependency.mContext = mContext;
-        mDependency.start();
     }
 
-    @After
-    public void cleanup() throws Exception {
-        mContext.getSettingsProvider().clearOverrides(this);
+    protected LeakCheck getLeakCheck() {
+        return null;
     }
 
-    protected Context getContext() {
+    public Context getContext() {
         return mContext;
     }
 
@@ -84,48 +73,11 @@
         }
     }
 
-    // Used for leak tracking, returns null to indicate no leak tracking by default.
-    public Tracker getTracker(String tag) {
-        return null;
-    }
-
-    public <T> T injectMockDependency(Class<T> cls) {
-        final T mock = mock(cls);
-        mDependency.injectTestDependency(cls, mock);
-        return mock;
-    }
-
-    public <T> void injectTestDependency(Class<T> cls, T obj) {
-        mDependency.injectTestDependency(cls, obj);
-    }
-
-    public <T> void injectTestDependency(DependencyKey<T> key, T obj) {
-        mDependency.injectTestDependency(key, obj);
-    }
-
     public static final class EmptyRunnable implements Runnable {
         public void run() {
         }
     }
 
-    public static class TestDependency extends Dependency {
-        private final ArrayMap<Object, Object> mObjs = new ArrayMap<>();
-
-        private <T> void injectTestDependency(DependencyKey<T> key, T obj) {
-            mObjs.put(key, obj);
-        }
-
-        private <T> void injectTestDependency(Class<T> key, T obj) {
-            mObjs.put(key, obj);
-        }
-
-        @Override
-        protected <T> T createDependency(Object key) {
-            if (mObjs.containsKey(key)) return (T) mObjs.get(key);
-            return super.createDependency(key);
-        }
-    }
-
     public static final class Idler implements MessageQueue.IdleHandler {
         private final Runnable mCallback;
         private boolean mIdle;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
new file mode 100644
index 0000000..b94a2fb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestableContext.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.content.Context;
+import android.testing.LeakCheck;
+import android.testing.TestableContext;
+import android.util.ArrayMap;
+
+public class SysuiTestableContext extends TestableContext implements SysUiServiceProvider {
+
+    private ArrayMap<Class<?>, Object> mComponents;
+
+    public SysuiTestableContext(Context base) {
+        super(base);
+    }
+
+    public SysuiTestableContext(Context base, LeakCheck check) {
+        super(base, check);
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> T getComponent(Class<T> interfaceType) {
+        return (T) (mComponents != null ? mComponents.get(interfaceType) : null);
+    }
+
+    public <T, C extends T> void putComponent(Class<T> interfaceType, C component) {
+        if (mComponents == null) mComponents = new ArrayMap<>();
+        mComponents.put(interfaceType, component);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
new file mode 100644
index 0000000..53a7994
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui;
+
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.util.ArrayMap;
+
+public class TestableDependency extends Dependency {
+    private final ArrayMap<Object, Object> mObjs = new ArrayMap<>();
+
+    public TestableDependency(Context context) {
+        mContext = context;
+        if (SystemUIFactory.getInstance() == null) {
+            SystemUIFactory.createFromConfig(context);
+        }
+        start();
+    }
+
+    public <T> T injectMockDependency(Class<T> cls) {
+        final T mock = mock(cls);
+        injectTestDependency(cls, mock);
+        return mock;
+    }
+
+    public <T> void injectTestDependency(DependencyKey<T> key, T obj) {
+        mObjs.put(key, obj);
+    }
+
+    public <T> void injectTestDependency(Class<T> key, T obj) {
+        mObjs.put(key, obj);
+    }
+
+    @Override
+    protected <T> T createDependency(Object key) {
+        if (mObjs.containsKey(key)) return (T) mObjs.get(key);
+        return super.createDependency(key);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
index 5477afa8..048936b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
@@ -47,7 +47,7 @@
             return;
         }
 
-        mContext.getSettingsProvider().acquireOverridesBuilder(this)
+        mContext.getSettingsProvider().acquireOverridesBuilder()
                 .addSetting("secure", Settings.Secure.DOZE_ALWAYS_ON, null)
                 .build();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index ba39671..cdbde5e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -39,19 +39,20 @@
 import static org.mockito.Mockito.when;
 
 import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
 import android.view.Display;
 
-import com.android.systemui.SysUIRunner;
-import com.android.systemui.UiThreadTest;
 import com.android.internal.hardware.AmbientDisplayConfiguration;
 import com.android.systemui.util.wakelock.WakeLockFake;
 
+import android.testing.UiThreadTest;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @SmallTest
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @UiThreadTest
 public class DozeMachineTest {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java
index 53053fa..8484bed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/notification/PropertyAnimatorTest.java
@@ -14,22 +14,25 @@
 
 package com.android.systemui.notification;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-
+import android.testing.AndroidTestingRunner;
+import android.testing.UiThreadTest;
 import android.util.FloatProperty;
 import android.util.Property;
 import android.view.View;
 import android.view.animation.Interpolator;
 
-import com.android.systemui.SysUIRunner;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.UiThreadTest;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.stack.AnimationFilter;
 import com.android.systemui.statusbar.stack.AnimationProperties;
@@ -39,18 +42,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyObject;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
 @SmallTest
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @UiThreadTest
 public class PropertyAnimatorTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
index 658966c..4f0815d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
@@ -72,7 +72,7 @@
     private PackageManager mMockPm;
     private PluginListener mMockListener;
     private PluginInstanceManager mPluginInstanceManager;
-    private PluginManager mMockManager;
+    private PluginManagerImpl mMockManager;
     private VersionInfo mMockVersionInfo;
 
     @Before
@@ -82,7 +82,7 @@
         mContextWrapper = new MyContextWrapper(getContext());
         mMockPm = mock(PackageManager.class);
         mMockListener = mock(PluginListener.class);
-        mMockManager = mock(PluginManager.class);
+        mMockManager = mock(PluginManagerImpl.class);
         when(mMockManager.getClassLoader(any(), any()))
                 .thenReturn(getClass().getClassLoader());
         mMockVersionInfo = mock(VersionInfo.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
index 053e5cf2..a3d5d5f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
@@ -34,7 +34,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.annotations.ProvidesInterface;
 import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
-import com.android.systemui.plugins.PluginManager.PluginInstanceManagerFactory;
+import com.android.systemui.plugins.PluginManagerImpl.PluginInstanceManagerFactory;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -50,7 +50,7 @@
 
     private PluginInstanceManagerFactory mMockFactory;
     private PluginInstanceManager mMockPluginInstance;
-    private PluginManager mPluginManager;
+    private PluginManagerImpl mPluginManager;
     private PluginListener mMockListener;
 
     private UncaughtExceptionHandler mRealExceptionHandler;
@@ -66,7 +66,8 @@
         when(mMockFactory.createPluginInstanceManager(Mockito.any(), Mockito.any(), Mockito.any(),
                 Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.any()))
                 .thenReturn(mMockPluginInstance);
-        mPluginManager = new PluginManager(getContext(), mMockFactory, true, mMockExceptionHandler);
+        mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, true,
+                mMockExceptionHandler);
         resetExceptionHandler();
         mMockListener = mock(PluginListener.class);
     }
@@ -98,7 +99,7 @@
 
     @Test
     public void testNonDebuggable() {
-        mPluginManager = new PluginManager(getContext(), mMockFactory, false,
+        mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false,
                 mMockExceptionHandler);
         resetExceptionHandler();
 
@@ -148,7 +149,7 @@
 
         ComponentName testComponent = new ComponentName(getContext().getPackageName(),
                 PluginManagerTest.class.getName());
-        Intent intent = new Intent(PluginManager.DISABLE_PLUGIN);
+        Intent intent = new Intent(PluginManagerImpl.DISABLE_PLUGIN);
         intent.setData(Uri.parse("package://" + testComponent.flattenToString()));
         mPluginManager.onReceive(mContext, intent);
         verify(nm).cancel(eq(testComponent.getClassName()), eq(SystemMessage.NOTE_PLUGIN));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 7153340..deb31da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -21,14 +21,16 @@
 
 import com.android.keyguard.CarrierText;
 import com.android.systemui.Dependency;
-import com.android.systemui.FragmentTestCase;
 import com.android.systemui.R;
-import com.android.systemui.SysUIRunner;
+
+import android.testing.AndroidTestingRunner;
+
+import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.util.LayoutInflaterBuilder;
-import com.android.systemui.utils.TestableLooper;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.LayoutInflaterBuilder;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -38,9 +40,9 @@
 import android.view.View;
 import android.widget.FrameLayout;
 
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
-public class QSFragmentTest extends FragmentTestCase {
+public class QSFragmentTest extends SysuiBaseFragmentTest {
 
     public QSFragmentTest() {
         super(QSFragment.class);
@@ -56,8 +58,9 @@
                         .replace(CarrierText.class, View.class)
                         .build());
 
-        injectTestDependency(Dependency.BG_LOOPER, TestableLooper.get(this).getLooper());
-        injectMockDependency(UserSwitcherController.class);
+        mDependency.injectTestDependency(Dependency.BG_LOOPER,
+                TestableLooper.get(this).getLooper());
+        mDependency.injectMockDependency(UserSwitcherController.class);
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index e38c30f..1ff373c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -33,8 +33,8 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.util.LayoutInflaterBuilder;
-import com.android.systemui.utils.TestableImageView;
+import android.testing.LayoutInflaterBuilder;
+import android.testing.TestableImageView;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -57,8 +57,8 @@
 
     @Before
     public void setUp() {
-        injectTestDependency(SecurityController.class, mSecurityController);
-        injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
+        mDependency.injectTestDependency(SecurityController.class, mSecurityController);
+        mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
         mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE,
                 new LayoutInflaterBuilder(mContext)
                         .replace("ImageView", TestableImageView.class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index d1e17f4..5ae107a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -21,13 +21,13 @@
 import static org.mockito.Mockito.when;
 
 import com.android.systemui.Dependency;
-import com.android.systemui.SysUIRunner;
+import android.testing.AndroidTestingRunner;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.State;
 import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.utils.TestableLooper;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -37,7 +37,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 @SmallTest
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 public class TileQueryHelperTest extends SysuiTestCase {
     private TestableLooper mBGLooper;
@@ -46,7 +46,7 @@
     @Before
     public void setup() {
         mBGLooper = TestableLooper.get(this);
-        injectTestDependency(Dependency.BG_LOOPER, mBGLooper.getLooper());
+        mDependency.injectTestDependency(Dependency.BG_LOOPER, mBGLooper.getLooper());
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 6d7b50f..ddd6615 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -25,12 +25,12 @@
 import android.service.quicksettings.Tile;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.systemui.SysUIRunner;
+import android.testing.AndroidTestingRunner;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.utils.TestableLooper;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
 
 import org.junit.After;
 import org.junit.Before;
@@ -42,7 +42,7 @@
 import java.util.ArrayList;
 
 @SmallTest
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
 public class TileServicesTest extends SysuiTestCase {
     private static int NUM_FAKES = TileServices.DEFAULT_MAX_BOUND * 2;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 6424a0a..bdd05c7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar;
 
-import static android.support.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread;
-
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
@@ -34,12 +32,11 @@
 import android.os.Looper;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
-import android.support.test.internal.runner.junit4.statement.UiThreadStatement;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 import android.view.ViewGroup;
 
-import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
@@ -124,7 +121,7 @@
         when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
         createController();
 
-        final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
+        final KeyguardUpdateMonitorCallback monitor = mController.getKeyguardCallback();
         reset(mDisclosure);
 
         when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
@@ -186,12 +183,10 @@
         });
         mInstrumentation.waitForIdleSync();
 
-        boolean[] held = new boolean[2];
+        Boolean[] held = new Boolean[1];
         mInstrumentation.runOnMainSync(() -> {
             held[0] = mWakeLock.isHeld();
-            held[1] = true;
         });
-        assertFalse("wake lock still held", held[0]);
-        assertTrue("held was not written yet", held[1]);
+        assertFalse("WakeLock expected: RELEASED, was: HELD", held[0]);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
index 8520bdb..726300f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -33,41 +33,35 @@
 import static org.mockito.Mockito.when;
 
 import android.app.INotificationManager;
-import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.ParceledListSlice;
 import android.graphics.drawable.Drawable;
 import android.service.notification.StatusBarNotification;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
+import android.testing.AndroidTestingRunner;
+import android.testing.UiThreadTest;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.Switch;
 import android.widget.TextView;
-import com.android.internal.util.CharSequences;
+
 import com.android.systemui.R;
-import com.android.systemui.SysUIRunner;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.UiThreadTest;
 
 import org.junit.Before;
-import org.junit.runner.RunWith;
 import org.junit.Test;
-import org.mockito.Mockito;
-import java.util.Arrays;
+import org.junit.runner.RunWith;
+
 import java.util.Collections;
 import java.util.concurrent.CountDownLatch;
 
 @SmallTest
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @UiThreadTest
 public class NotificationInfoTest extends SysuiTestCase {
     private static final String TEST_PACKAGE_NAME = "test_package";
@@ -167,27 +161,6 @@
     }
 
     @Test
-    public void testBindNotification_SetsGroupName_resId() throws Exception {
-        when(mMockPackageManager.getText(eq(TEST_PACKAGE_NAME),
-                eq(R.string.legacy_vpn_name), anyObject())).thenReturn(
-                getContext().getString(R.string.legacy_vpn_name));
-        mNotificationChannel.setGroup("test_group_id");
-        final NotificationChannelGroup notificationChannelGroup =
-                new NotificationChannelGroup("test_group_id", R.string.legacy_vpn_name);
-        when(mMockINotificationManager.getNotificationChannelGroupForPackage(
-                eq("test_group_id"), eq(TEST_PACKAGE_NAME), anyInt()))
-                .thenReturn(notificationChannelGroup);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, mNotificationChannel, null, null, null);
-        final TextView groupNameView = (TextView) mNotificationInfo.findViewById(R.id.group_name);
-        assertEquals(View.VISIBLE, groupNameView.getVisibility());
-        assertEquals(mContext.getString(R.string.legacy_vpn_name), groupNameView.getText());
-        final TextView groupDividerView =
-                (TextView) mNotificationInfo.findViewById(R.id.pkg_group_divider);
-        assertEquals(View.VISIBLE, groupDividerView.getVisibility());
-    }
-
-    @Test
     public void testBindNotification_SetsTextChannelName() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 mMockStatusBarNotification, mNotificationChannel, null, null, null);
@@ -196,21 +169,6 @@
     }
 
     @Test
-    public void testBindNotification_SetsTextChannelName_resId() throws Exception {
-        when(mMockPackageManager.getText(eq(TEST_PACKAGE_NAME),
-                eq(R.string.notification_menu_accessibility), anyObject())).thenReturn(
-                getContext().getString(R.string.notification_menu_accessibility));
-        NotificationChannel notificationChannelResId = new NotificationChannel(
-                TEST_CHANNEL, R.string.notification_menu_accessibility,
-                NotificationManager.IMPORTANCE_LOW);
-        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                mMockStatusBarNotification, notificationChannelResId, null, null, null);
-        final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
-        assertEquals(getContext().getString(R.string.notification_menu_accessibility),
-                textView.getText());
-    }
-
-    @Test
     public void testBindNotification_SetsOnClickListenerForSettings() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
index b8be4fa..c2c6336 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationMenuRowTest.java
@@ -14,17 +14,17 @@
 
 package com.android.systemui.statusbar;
 
-import com.android.systemui.SysUIRunner;
-import com.android.systemui.utils.TestableLooper;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
-import com.android.systemui.utils.ViewUtils;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.testing.ViewUtils;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
 public class NotificationMenuRowTest extends LeakCheckedTest {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 3ccb160..e41a827 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -22,7 +22,7 @@
 import com.android.internal.app.NightDisplayController;
 import com.android.systemui.Prefs;
 import com.android.systemui.Prefs.Key;
-import com.android.systemui.SysUIRunner;
+import android.testing.AndroidTestingRunner;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.qs.QSTileHost;
 
@@ -32,7 +32,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 public class AutoTileManagerTest extends SysuiTestCase {
 
     private QSTileHost mQsTileHost;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
index f55115e0..a9acda3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragmentTest.java
@@ -23,24 +23,22 @@
 import android.view.View;
 import android.view.ViewPropertyAnimator;
 
-import com.android.systemui.FragmentTestCase;
 import com.android.systemui.R;
-import com.android.systemui.SysUIRunner;
+import android.testing.AndroidTestingRunner;
+
+import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.policy.KeyguardMonitor;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.SecurityController;
 import com.android.systemui.tuner.TunerService;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.TestableLooper.RunWithLooper;
 
 import org.junit.Before;
 import org.junit.runner.RunWith;
 import org.junit.Test;
 import org.mockito.Mockito;
 
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
-public class CollapsedStatusBarFragmentTest extends FragmentTestCase {
+public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
 
     private NotificationIconAreaController mMockNotificiationAreaController;
     private View mNotificationAreaInner;
@@ -51,9 +49,9 @@
 
     @Before
     public void setup() {
-        mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
-        mContext.putComponent(StatusBar.class, mock(StatusBar.class));
-        mContext.putComponent(TunerService.class, mock(TunerService.class));
+        mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
+        mSysuiContext.putComponent(StatusBar.class, mock(StatusBar.class));
+        mSysuiContext.putComponent(TunerService.class, mock(TunerService.class));
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
         mMockNotificiationAreaController = mock(NotificationIconAreaController.class);
         mNotificationAreaInner = mock(View.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
index 1fa9846..e7cdcb5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarFragmentTest.java
@@ -19,24 +19,25 @@
 
 import android.content.Context;
 import android.os.Looper;
+import android.testing.AndroidTestingRunner;
 import android.view.Display;
 import android.view.WindowManager;
 
 import com.android.systemui.Dependency;
-import com.android.systemui.FragmentTestCase;
-import com.android.systemui.SysUIRunner;
+
+import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.TestableLooper.RunWithLooper;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
-public class NavigationBarFragmentTest extends FragmentTestCase {
+public class NavigationBarFragmentTest extends SysuiBaseFragmentTest {
 
     public NavigationBarFragmentTest() {
         super(NavigationBarFragment.class);
@@ -44,11 +45,11 @@
 
     @Before
     public void setup() {
-        injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
-        mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
-        mContext.putComponent(StatusBar.class, mock(StatusBar.class));
-        mContext.putComponent(Recents.class, mock(Recents.class));
-        mContext.putComponent(Divider.class, mock(Divider.class));
+        mDependency.injectTestDependency(Dependency.BG_LOOPER, Looper.getMainLooper());
+        mSysuiContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
+        mSysuiContext.putComponent(StatusBar.class, mock(StatusBar.class));
+        mSysuiContext.putComponent(Recents.class, mock(Recents.class));
+        mSysuiContext.putComponent(Divider.class, mock(Divider.class));
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
         WindowManager windowManager = mock(WindowManager.class);
         Display defaultDisplay = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerTest.java
index e3a5ef0..3e79a04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerTest.java
@@ -42,8 +42,8 @@
 
     @Before
     public void setup() {
-        mPluginManager = injectMockDependency(PluginManager.class);
-        mTunerService = injectMockDependency(TunerService.class);
+        mPluginManager = mDependency.injectMockDependency(PluginManager.class);
+        mTunerService = mDependency.injectMockDependency(TunerService.class);
         mExtensionController = Dependency.get(ExtensionController.class);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 8cbf95b..efa232b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -18,7 +18,7 @@
 
 import com.android.settingslib.Utils;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
-import com.android.systemui.utils.FakeSettingsProvider.SettingOverrider;
+import android.testing.TestableSettings.SettingOverrider;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -92,9 +92,9 @@
                         attr);
 
         // Must set the Settings value before instantiating the NetworkControllerImpl due to bugs in
-        // FakeSettingsProvider.
+        // TestableSettings.
         SettingOverrider settingsOverrider =
-                mContext.getSettingsProvider().acquireOverridesBuilder(this)
+                mContext.getSettingsProvider().acquireOverridesBuilder()
                         .addSetting("global", Settings.Global.NETWORK_SCORING_UI_ENABLED, "1")
                         .build();
         super.setUp(); // re-instantiate NetworkControllImpl now that setting has been updated
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProvider.java b/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProvider.java
deleted file mode 100644
index f40fe4c..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProvider.java
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.utils;
-
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.provider.Settings;
-import android.support.annotation.VisibleForTesting;
-import android.test.mock.MockContentProvider;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.utils.FakeSettingsProvider.SettingOverrider.Builder;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Allows calls to android.provider.Settings to be tested easier.  A SettingOverride
- * can be acquired and a set of specific settings can be set to a value (and not changed
- * in the system when set), so that they can be tested without breaking the test device.
- * <p>
- * To use, in the before method acquire the override add all settings that will affect if
- * your test passes or not.
- *
- * <pre class="prettyprint">
- * {@literal
- * mSettingOverride = mTestableContext.getSettingsProvider().acquireOverridesBuilder()
- *         .addSetting("secure", Secure.USER_SETUP_COMPLETE, "0")
- *         .build();
- * }
- * </pre>
- *
- * Then in the after free up the settings.
- *
- * <pre class="prettyprint">
- * {@literal
- * mSettingOverride.release();
- * }
- * </pre>
- */
-public class FakeSettingsProvider extends MockContentProvider {
-
-    private static final String TAG = "FakeSettingsProvider";
-    private static final boolean DEBUG = false;
-
-    // Number of times to try to acquire a setting if in use.
-    private static final int MAX_TRIES = 10;
-    // Time to wait for each setting.  WAIT_TIMEOUT * MAX_TRIES will be the maximum wait time
-    // for a setting.
-    private static final long WAIT_TIMEOUT = 1000;
-
-    private final Map<String, SettingOverrider> mOverrideMap = new ArrayMap<>();
-    private final Map<SysuiTestCase, List<SettingOverrider>> mOwners = new ArrayMap<>();
-
-    private static FakeSettingsProvider sInstance;
-    private final ContentProviderClient mSettings;
-    private final ContentResolver mResolver;
-
-    private FakeSettingsProvider(ContentProviderClient settings, ContentResolver resolver) {
-        mSettings = settings;
-        mResolver = resolver;
-    }
-
-    public Builder acquireOverridesBuilder(SysuiTestCase test) {
-        return new Builder(this, test);
-    }
-
-    public void clearOverrides(SysuiTestCase test) {
-        List<SettingOverrider> overrides = mOwners.remove(test);
-        if (overrides != null) {
-            overrides.forEach(override -> override.ensureReleased());
-        }
-    }
-
-    public Bundle call(String method, String arg, Bundle extras) {
-        // Methods are "GET_system", "GET_global", "PUT_secure", etc.
-        final String[] commands = method.split("_", 2);
-        final String op = commands[0];
-        final String table = commands[1];
-
-        synchronized (mOverrideMap) {
-            SettingOverrider overrider = mOverrideMap.get(key(table, arg));
-            if (overrider == null) {
-                // Fall through to real settings.
-                try {
-                    if (DEBUG) Log.d(TAG, "Falling through to real settings " + method);
-                    // TODO: Add our own version of caching to handle this.
-                    Bundle call = mSettings.call(method, arg, extras);
-                    call.remove(Settings.CALL_METHOD_TRACK_GENERATION_KEY);
-                    return call;
-                } catch (RemoteException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-            String value;
-            Bundle out = new Bundle();
-            switch (op) {
-                case "GET":
-                    value = overrider.get(table, arg);
-                    if (value != null) {
-                        out.putString(Settings.NameValueTable.VALUE, value);
-                    }
-                    break;
-                case "PUT":
-                    value = extras.getString(Settings.NameValueTable.VALUE, null);
-                    if (value != null) {
-                        overrider.put(table, arg, value);
-                    } else {
-                        overrider.remove(table, arg);
-                    }
-                    break;
-                default:
-                    throw new UnsupportedOperationException("Unknown command " + method);
-            }
-            return out;
-        }
-    }
-
-    private void acquireSettings(SettingOverrider overridder, Set<String> keys,
-            SysuiTestCase owner) throws AcquireTimeoutException {
-        synchronized (mOwners) {
-            List<SettingOverrider> list = mOwners.get(owner);
-            if (list == null) {
-                list = new ArrayList<>();
-                mOwners.put(owner, list);
-            }
-            list.add(overridder);
-        }
-        synchronized (mOverrideMap) {
-            for (int i = 0; i < MAX_TRIES; i++) {
-                if (checkKeys(keys, false)) break;
-                try {
-                    if (DEBUG) Log.d(TAG, "Waiting for contention to finish");
-                    mOverrideMap.wait(WAIT_TIMEOUT);
-                } catch (InterruptedException e) {
-                }
-            }
-            checkKeys(keys, true);
-            for (String key : keys) {
-                if (DEBUG) Log.d(TAG, "Acquiring " + key);
-                mOverrideMap.put(key, overridder);
-            }
-        }
-    }
-
-    private void releaseSettings(Set<String> keys) {
-        synchronized (mOverrideMap) {
-            for (String key : keys) {
-                if (DEBUG) Log.d(TAG, "Releasing " + key);
-                mOverrideMap.remove(key);
-            }
-            if (DEBUG) Log.d(TAG, "Notifying");
-            mOverrideMap.notify();
-        }
-    }
-
-    @VisibleForTesting
-    public Object getLock() {
-        return mOverrideMap;
-    }
-
-    private boolean checkKeys(Set<String> keys, boolean shouldThrow)
-            throws AcquireTimeoutException {
-        for (String key : keys) {
-            if (mOverrideMap.containsKey(key)) {
-                if (shouldThrow) {
-                    throw new AcquireTimeoutException("Could not acquire " + key);
-                }
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public static class SettingOverrider {
-        private final Set<String> mValidKeys;
-        private final Map<String, String> mValueMap = new ArrayMap<>();
-        private final FakeSettingsProvider mProvider;
-        private boolean mReleased;
-
-        private SettingOverrider(Set<String> keys, FakeSettingsProvider provider) {
-            mValidKeys = new ArraySet<>(keys);
-            mProvider = provider;
-        }
-
-        private void ensureReleased() {
-            if (!mReleased) {
-                release();
-            }
-        }
-
-        public void release() {
-            mProvider.releaseSettings(mValidKeys);
-            mReleased = true;
-        }
-
-        private void putDirect(String key, String value) {
-            mValueMap.put(key, value);
-        }
-
-        public void put(String table, String key, String value) {
-            if (!mValidKeys.contains(key(table, key))) {
-                throw new IllegalArgumentException("Key " + table + " " + key
-                        + " not acquired for this overrider");
-            }
-            mValueMap.put(key(table, key), value);
-        }
-
-        public void remove(String table, String key) {
-            if (!mValidKeys.contains(key(table, key))) {
-                throw new IllegalArgumentException("Key " + table + " " + key
-                        + " not acquired for this overrider");
-            }
-            mValueMap.remove(key(table, key));
-        }
-
-        public String get(String table, String key) {
-            if (!mValidKeys.contains(key(table, key))) {
-                throw new IllegalArgumentException("Key " + table + " " + key
-                        + " not acquired for this overrider");
-            }
-            Log.d(TAG, "Get " + table + " " + key + " " + mValueMap.get(key(table, key)));
-            return mValueMap.get(key(table, key));
-        }
-
-        public static class Builder {
-            private final FakeSettingsProvider mProvider;
-            private final SysuiTestCase mOwner;
-            private Set<String> mKeys = new ArraySet<>();
-            private Map<String, String> mValues = new ArrayMap<>();
-
-            private Builder(FakeSettingsProvider provider, SysuiTestCase test) {
-                mProvider = provider;
-                mOwner = test;
-            }
-
-            public Builder addSetting(String table, String key) {
-                mKeys.add(key(table, key));
-                return this;
-            }
-
-            public Builder addSetting(String table, String key, String value) {
-                addSetting(table, key);
-                mValues.put(key(table, key), value);
-                return this;
-            }
-
-            public SettingOverrider build() throws AcquireTimeoutException {
-                SettingOverrider overrider = new SettingOverrider(mKeys, mProvider);
-                mProvider.acquireSettings(overrider, mKeys, mOwner);
-                mValues.forEach((key, value) -> overrider.putDirect(key, value));
-                return overrider;
-            }
-        }
-    }
-
-    public static class AcquireTimeoutException extends Exception {
-        public AcquireTimeoutException(String str) {
-            super(str);
-        }
-    }
-
-    private static String key(String table, String key) {
-        return table + "_" + key;
-    }
-
-    /**
-     * Since the settings provider is cached inside android.provider.Settings, this must
-     * be gotten statically to ensure there is only one instance referenced.
-     * @param settings
-     */
-    public static FakeSettingsProvider getFakeSettingsProvider(ContentProviderClient settings,
-            ContentResolver resolver) {
-        if (sInstance == null) {
-            sInstance = new FakeSettingsProvider(settings, resolver);
-        }
-        return sInstance;
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java
index b118fdc..d94ecc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/BaseLeakChecker.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
  * except in compliance with the License. You may obtain a copy of the License at
@@ -14,6 +14,9 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+import android.testing.LeakCheck.Tracker;
+
 import com.android.systemui.Dumpable;
 import com.android.systemui.statusbar.policy.CallbackController;
 
@@ -24,7 +27,7 @@
 
     private final Tracker mTracker;
 
-    public BaseLeakChecker(LeakCheckedTest test, String tag) {
+    public BaseLeakChecker(LeakCheck test, String tag) {
         mTracker = test.getTracker(tag);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
index fa07d33..a843cca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBatteryController.java
@@ -15,6 +15,7 @@
 package com.android.systemui.utils.leaks;
 
 import android.os.Bundle;
+import android.testing.LeakCheck;
 
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -24,7 +25,7 @@
 
 public class FakeBatteryController extends BaseLeakChecker<BatteryStateChangeCallback>
         implements BatteryController {
-    public FakeBatteryController(LeakCheckedTest test) {
+    public FakeBatteryController(LeakCheck test) {
         super(test, "battery");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
index 6074a01..0ba0319 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeBluetoothController.java
@@ -14,6 +14,8 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.BluetoothController.Callback;
@@ -23,7 +25,7 @@
 public class FakeBluetoothController extends BaseLeakChecker<Callback> implements
         BluetoothController {
 
-    public FakeBluetoothController(LeakCheckedTest test) {
+    public FakeBluetoothController(LeakCheck test) {
         super(test, "bluetooth");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java
index 08211f8..51149ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeCastController.java
@@ -14,13 +14,15 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.CastController.Callback;
 
 import java.util.Set;
 
 public class FakeCastController extends BaseLeakChecker<Callback> implements CastController {
-    public FakeCastController(LeakCheckedTest test) {
+    public FakeCastController(LeakCheck test) {
         super(test, "cast");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeDataSaverController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeDataSaverController.java
index 857a785..886722e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeDataSaverController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeDataSaverController.java
@@ -14,12 +14,14 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.DataSaverController.Listener;
 
 public class FakeDataSaverController extends BaseLeakChecker<Listener> implements DataSaverController {
 
-    public FakeDataSaverController(LeakCheckedTest test) {
+    public FakeDataSaverController(LeakCheck test) {
         super(test, "datasaver");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java
index c0f5783..b9d188a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeExtensionController.java
@@ -14,7 +14,8 @@
 
 package com.android.systemui.utils.leaks;
 
-import static org.mockito.Mockito.mock;
+import android.testing.LeakCheck;
+import android.testing.LeakCheck.Tracker;
 
 import com.android.systemui.statusbar.policy.ExtensionController;
 
@@ -25,7 +26,7 @@
 
     private final Tracker mTracker;
 
-    public FakeExtensionController(LeakCheckedTest test) {
+    public FakeExtensionController(LeakCheck test) {
         mTracker = test.getTracker("extension");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeFlashlightController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeFlashlightController.java
index 630abd7..f6fd2cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeFlashlightController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeFlashlightController.java
@@ -14,12 +14,14 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.FlashlightController;
 import com.android.systemui.statusbar.policy.FlashlightController.FlashlightListener;
 
 public class FakeFlashlightController extends BaseLeakChecker<FlashlightListener>
         implements FlashlightController {
-    public FakeFlashlightController(LeakCheckedTest test) {
+    public FakeFlashlightController(LeakCheck test) {
         super(test, "flashlight");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
index 781960d..69e2361 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
@@ -14,12 +14,14 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.HotspotController.Callback;
 
 public class FakeHotspotController extends BaseLeakChecker<Callback> implements HotspotController {
 
-    public FakeHotspotController(LeakCheckedTest test) {
+    public FakeHotspotController(LeakCheck test) {
         super(test, "hotspot");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
index 21871fc..51e35cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
@@ -14,13 +14,15 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 
 public class FakeKeyguardMonitor implements KeyguardMonitor {
 
     private final BaseLeakChecker<Callback> mCallbackController;
 
-    public FakeKeyguardMonitor(LeakCheckedTest test) {
+    public FakeKeyguardMonitor(LeakCheck test) {
         mCallbackController = new BaseLeakChecker<Callback>(test, "keyguard");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java
index eab436c..29d7f1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeLocationController.java
@@ -14,12 +14,14 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.LocationController.LocationSettingsChangeCallback;
 
 public class FakeLocationController extends BaseLeakChecker<LocationSettingsChangeCallback>
         implements LocationController {
-    public FakeLocationController(LeakCheckedTest test) {
+    public FakeLocationController(LeakCheck test) {
         super(test, "location");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeManagedProfileController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeManagedProfileController.java
index 0ec0d77..18b07cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeManagedProfileController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeManagedProfileController.java
@@ -14,12 +14,14 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.phone.ManagedProfileController;
 import com.android.systemui.statusbar.phone.ManagedProfileController.Callback;
 
 public class FakeManagedProfileController extends BaseLeakChecker<Callback> implements
         ManagedProfileController {
-    public FakeManagedProfileController(LeakCheckedTest test) {
+    public FakeManagedProfileController(LeakCheck test) {
         super(test, "profile");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
index 47ed5ca..64fe8dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNetworkController.java
@@ -15,25 +15,23 @@
 package com.android.systemui.utils.leaks;
 
 import android.os.Bundle;
+import android.testing.LeakCheck;
 
 import com.android.settingslib.net.DataUsageController;
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
 public class FakeNetworkController extends BaseLeakChecker<SignalCallback>
         implements NetworkController {
 
     private final FakeDataSaverController mDataSaverController;
     private final BaseLeakChecker<EmergencyListener> mEmergencyChecker;
 
-    public FakeNetworkController(LeakCheckedTest test) {
+    public FakeNetworkController(LeakCheck test) {
         super(test, "network");
         mDataSaverController = new FakeDataSaverController(test);
-        mEmergencyChecker = new BaseLeakChecker<EmergencyListener>(test, "emergency");
+        mEmergencyChecker = new BaseLeakChecker<>(test, "emergency");
     }
 
     @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNextAlarmController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNextAlarmController.java
index 707fc4b..5ae8e22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNextAlarmController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeNextAlarmController.java
@@ -14,13 +14,15 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
 
 public class FakeNextAlarmController extends BaseLeakChecker<NextAlarmChangeCallback>
         implements NextAlarmController {
 
-    public FakeNextAlarmController(LeakCheckedTest test) {
+    public FakeNextAlarmController(LeakCheck test) {
         super(test, "alarm");
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
index 59a9361..0a83a89 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
@@ -15,17 +15,17 @@
 package com.android.systemui.utils.leaks;
 
 import android.content.Context;
+import android.testing.LeakCheck;
 
 import com.android.systemui.plugins.Plugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.PluginManager;
 
-public class FakePluginManager extends PluginManager {
+public class FakePluginManager implements PluginManager {
 
     private final BaseLeakChecker<PluginListener> mLeakChecker;
 
-    public FakePluginManager(Context context, LeakCheckedTest test) {
-        super(context);
+    public FakePluginManager(LeakCheck test) {
         mLeakChecker = new BaseLeakChecker<>(test, "Plugin");
     }
 
@@ -36,11 +36,38 @@
     }
 
     @Override
+    public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls) {
+        mLeakChecker.addCallback(listener);
+    }
+
+    @Override
+    public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
+            boolean allowMultiple) {
+        mLeakChecker.addCallback(listener);
+    }
+
+    @Override
+    public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
+            Class<?> cls) {
+        mLeakChecker.addCallback(listener);
+    }
+
+    @Override
     public void removePluginListener(PluginListener<?> listener) {
         mLeakChecker.removeCallback(listener);
     }
 
     @Override
+    public <T> boolean dependsOn(Plugin p, Class<T> cls) {
+        return false;
+    }
+
+    @Override
+    public <T extends Plugin> T getOneShotPlugin(Class<T> cls) {
+        return null;
+    }
+
+    @Override
     public <T extends Plugin> T getOneShotPlugin(String action, Class<?> cls) {
         return null;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
index 00e2404..d60fe78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeRotationLockController.java
@@ -14,12 +14,14 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.RotationLockController;
 import com.android.systemui.statusbar.policy.RotationLockController.RotationLockControllerCallback;
 
 public class FakeRotationLockController extends BaseLeakChecker<RotationLockControllerCallback>
         implements RotationLockController {
-    public FakeRotationLockController(LeakCheckedTest test) {
+    public FakeRotationLockController(LeakCheck test) {
         super(test, "rotation");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
index 2d53c77..157b8a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
@@ -14,12 +14,14 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.SecurityController;
 import com.android.systemui.statusbar.policy.SecurityController.SecurityControllerCallback;
 
 public class FakeSecurityController extends BaseLeakChecker<SecurityControllerCallback>
         implements SecurityController {
-    public FakeSecurityController(LeakCheckedTest test) {
+    public FakeSecurityController(LeakCheck test) {
         super(test, "security");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
index b13535f..6b501af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
@@ -14,6 +14,8 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
@@ -21,7 +23,7 @@
 public class FakeStatusBarIconController extends BaseLeakChecker<IconManager>
         implements StatusBarIconController {
 
-    public FakeStatusBarIconController(LeakCheckedTest test) {
+    public FakeStatusBarIconController(LeakCheck test) {
         super(test, "StatusBarGroup");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java
index b841ce9..8db82e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java
@@ -15,6 +15,7 @@
 package com.android.systemui.utils.leaks;
 
 import android.content.Context;
+import android.testing.LeakCheck;
 
 import com.android.systemui.tuner.TunerService;
 
@@ -22,10 +23,8 @@
 
     private final BaseLeakChecker<Tunable> mBaseLeakChecker;
 
-    public FakeTunerService(Context context, LeakCheckedTest test) {
-        super(context);
+    public FakeTunerService(LeakCheck test) {
         mBaseLeakChecker = new BaseLeakChecker<>(test, "tunable");
-        destroy();
     }
 
     @Override
@@ -40,4 +39,39 @@
     public void removeTunable(Tunable tunable) {
         mBaseLeakChecker.removeCallback(tunable);
     }
+
+    @Override
+    public void clearAll() {
+
+    }
+
+    @Override
+    public void destroy() {
+
+    }
+
+    @Override
+    public String getValue(String setting) {
+        return null;
+    }
+
+    @Override
+    public int getValue(String setting, int def) {
+        return def;
+    }
+
+    @Override
+    public String getValue(String setting, String def) {
+        return def;
+    }
+
+    @Override
+    public void setValue(String setting, String value) {
+
+    }
+
+    @Override
+    public void setValue(String setting, int value) {
+
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeUserInfoController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeUserInfoController.java
index 578b310..f7ef653a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeUserInfoController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeUserInfoController.java
@@ -14,12 +14,14 @@
 
 package com.android.systemui.utils.leaks;
 
+import android.testing.LeakCheck;
+
 import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
 
 public class FakeUserInfoController extends BaseLeakChecker<OnUserInfoChangedListener>
         implements UserInfoController {
-    public FakeUserInfoController(LeakCheckedTest test) {
+    public FakeUserInfoController(LeakCheck test) {
         super(test, "user_info");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
index 7581363..fb9bf7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeZenModeController.java
@@ -18,12 +18,13 @@
 import android.net.Uri;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.ZenRule;
+import android.testing.LeakCheck;
 
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.policy.ZenModeController.Callback;
 
 public class FakeZenModeController extends BaseLeakChecker<Callback> implements ZenModeController {
-    public FakeZenModeController(LeakCheckedTest test) {
+    public FakeZenModeController(LeakCheck test) {
         super(test, "zen");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
index 6c51524..94af7733 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
@@ -15,8 +15,8 @@
 package com.android.systemui.utils.leaks;
 
 import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
 
+import android.testing.LeakCheck;
 import android.util.ArrayMap;
 
 import com.android.systemui.SysuiTestCase;
@@ -25,7 +25,6 @@
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.CallbackController;
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.FlashlightController;
 import com.android.systemui.statusbar.policy.HotspotController;
@@ -41,12 +40,7 @@
 
 import org.junit.Assert;
 import org.junit.Rule;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
 
-import java.util.HashMap;
 import java.util.Map;
 
 /**
@@ -56,10 +50,7 @@
 public abstract class LeakCheckedTest extends SysuiTestCase {
     private static final String TAG = "LeakCheckedTest";
 
-    private final Map<String, Tracker> mTrackers = new HashMap<>();
-    private final Map<Class, Object> mLeakCheckers = new ArrayMap<>();
-
-    public static final Class<?>[] ALL_SUPPORTED_CLASSES = new Class[] {
+    public static final Class<?>[] ALL_SUPPORTED_CLASSES = new Class[]{
             BluetoothController.class,
             LocationController.class,
             RotationLockController.class,
@@ -80,71 +71,11 @@
     };
 
     @Rule
-    public TestWatcher successWatcher = new TestWatcher() {
-        @Override
-        protected void succeeded(Description description) {
-            verify();
-        }
-    };
-
-    public <T> T getLeakChecker(Class<T> cls) {
-        Object obj = mLeakCheckers.get(cls);
-        if (obj == null) {
-            // Lazy create checkers so we only have the ones we need.
-            if (cls == BluetoothController.class) {
-                obj = new FakeBluetoothController(this);
-            } else if (cls == LocationController.class) {
-                obj = new FakeLocationController(this);
-            } else if (cls == RotationLockController.class) {
-                obj = new FakeRotationLockController(this);
-            } else if (cls == ZenModeController.class) {
-                obj = new FakeZenModeController(this);
-            } else if (cls == CastController.class) {
-                obj = new FakeCastController(this);
-            } else if (cls == HotspotController.class) {
-                obj = new FakeHotspotController(this);
-            } else if (cls == FlashlightController.class) {
-                obj = new FakeFlashlightController(this);
-            } else if (cls == UserInfoController.class) {
-                obj = new FakeUserInfoController(this);
-            } else if (cls == KeyguardMonitor.class) {
-                obj = new FakeKeyguardMonitor(this);
-            } else if (cls == BatteryController.class) {
-                obj = new FakeBatteryController(this);
-            } else if (cls == SecurityController.class) {
-                obj = new FakeSecurityController(this);
-            } else if (cls == ManagedProfileController.class) {
-                obj = new FakeManagedProfileController(this);
-            } else if (cls == NextAlarmController.class) {
-                obj = new FakeNextAlarmController(this);
-            } else if (cls == NetworkController.class) {
-                obj = new FakeNetworkController(this);
-            } else if (cls == PluginManager.class) {
-                obj = new FakePluginManager(mContext, this);
-            } else if (cls == TunerService.class) {
-                obj = new FakeTunerService(mContext, this);
-            } else if (cls == StatusBarIconController.class) {
-                obj = new FakeStatusBarIconController(this);
-            } else {
-                Assert.fail(cls.getName() + " is not supported by LeakCheckedTest yet");
-            }
-            mLeakCheckers.put(cls, obj);
-        }
-        return (T) obj;
-    }
+    public SysuiLeakCheck mLeakCheck = new SysuiLeakCheck();
 
     @Override
-    public Tracker getTracker(String tag) {
-        Tracker t = mTrackers.get(tag);
-        if (t == null) {
-            t = new Tracker();
-            mTrackers.put(tag, t);
-        }
-        return t;
-    }
-
-    public void verify() {
-        mTrackers.values().forEach(Tracker::verify);
+    public LeakCheck getLeakCheck() {
+        return mLeakCheck;
     }
 
     public void injectLeakCheckedDependencies(Class<?>... cls) {
@@ -154,26 +85,61 @@
     }
 
     public <T> void injectLeakCheckedDependency(Class<T> c) {
-        injectTestDependency(c, getLeakChecker(c));
+        mDependency.injectTestDependency(c, mLeakCheck.getLeakChecker(c));
     }
 
-    public <T extends CallbackController> T addListening(T mock, Class<T> cls, String tag) {
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                getTracker(tag).getLeakInfo(invocation.getArguments()[0])
-                        .addAllocation(new Throwable());
-                return null;
+    public static class SysuiLeakCheck extends LeakCheck {
+
+        private final Map<Class, Object> mLeakCheckers = new ArrayMap<>();
+
+        public SysuiLeakCheck() {
+            super();
+        }
+
+        public <T> T getLeakChecker(Class<T> cls) {
+            Object obj = mLeakCheckers.get(cls);
+            if (obj == null) {
+                // Lazy create checkers so we only have the ones we need.
+                if (cls == BluetoothController.class) {
+                    obj = new FakeBluetoothController(this);
+                } else if (cls == LocationController.class) {
+                    obj = new FakeLocationController(this);
+                } else if (cls == RotationLockController.class) {
+                    obj = new FakeRotationLockController(this);
+                } else if (cls == ZenModeController.class) {
+                    obj = new FakeZenModeController(this);
+                } else if (cls == CastController.class) {
+                    obj = new FakeCastController(this);
+                } else if (cls == HotspotController.class) {
+                    obj = new FakeHotspotController(this);
+                } else if (cls == FlashlightController.class) {
+                    obj = new FakeFlashlightController(this);
+                } else if (cls == UserInfoController.class) {
+                    obj = new FakeUserInfoController(this);
+                } else if (cls == KeyguardMonitor.class) {
+                    obj = new FakeKeyguardMonitor(this);
+                } else if (cls == BatteryController.class) {
+                    obj = new FakeBatteryController(this);
+                } else if (cls == SecurityController.class) {
+                    obj = new FakeSecurityController(this);
+                } else if (cls == ManagedProfileController.class) {
+                    obj = new FakeManagedProfileController(this);
+                } else if (cls == NextAlarmController.class) {
+                    obj = new FakeNextAlarmController(this);
+                } else if (cls == NetworkController.class) {
+                    obj = new FakeNetworkController(this);
+                } else if (cls == PluginManager.class) {
+                    obj = new FakePluginManager(this);
+                } else if (cls == TunerService.class) {
+                    obj = new FakeTunerService(this);
+                } else if (cls == StatusBarIconController.class) {
+                    obj = new FakeStatusBarIconController(this);
+                } else {
+                    Assert.fail(cls.getName() + " is not supported by LeakCheckedTest yet");
+                }
+                mLeakCheckers.put(cls, obj);
             }
-        }).when(mock).addCallback(any());
-        doAnswer(new Answer<Void>() {
-            @Override
-            public Void answer(InvocationOnMock invocation) throws Throwable {
-                getTracker(tag).getLeakInfo(invocation.getArguments()[0]).clearAllocations();
-                return null;
-            }
-        }).when(mock).removeCallback(any());
-        mLeakCheckers.put(cls, mock);
-        return mock;
+            return (T) obj;
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakInfo.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakInfo.java
deleted file mode 100644
index 1d016fb..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakInfo.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.utils.leaks;
-
-import android.util.Log;
-
-import org.junit.Assert;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-public class LeakInfo {
-    private static final String TAG = "LeakInfo";
-    private List<Throwable> mThrowables = new ArrayList<>();
-
-    LeakInfo() {
-    }
-
-    public void addAllocation(Throwable t) {
-        // TODO: Drop off the first element in the stack trace here to have a cleaner stack.
-        mThrowables.add(t);
-    }
-
-    public void clearAllocations() {
-        mThrowables.clear();
-    }
-
-    void verify() {
-        if (mThrowables.size() == 0) return;
-        Log.e(TAG, "Listener or binding not properly released");
-        for (Throwable t : mThrowables) {
-            Log.e(TAG, "Allocation found", t);
-        }
-        StringWriter writer = new StringWriter();
-        mThrowables.get(0).printStackTrace(new PrintWriter(writer));
-        Assert.fail("Listener or binding not properly released\n"
-                + writer.toString());
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/Tracker.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/Tracker.java
deleted file mode 100644
index 26ffd10..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/Tracker.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the
- * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.android.systemui.utils.leaks;
-
-import android.util.ArrayMap;
-
-import com.android.systemui.utils.leaks.LeakInfo;
-
-import java.util.Map;
-
-public class Tracker {
-    private Map<Object, LeakInfo> mObjects = new ArrayMap<>();
-
-    public LeakInfo getLeakInfo(Object object) {
-        LeakInfo leakInfo = mObjects.get(object);
-        if (leakInfo == null) {
-            leakInfo = new LeakInfo();
-            mObjects.put(object, leakInfo);
-        }
-        return leakInfo;
-    }
-
-    void verify() {
-        mObjects.values().forEach(LeakInfo::verify);
-    }
-}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 072d3dc..58e020f 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -44,6 +44,18 @@
 
     // The view or control was updated.
     TYPE_UPDATE = 6;
+
+    // Type for APP_TRANSITION event: The transition started a new activity for which it's process
+    // wasn't running.
+    TYPE_TRANSITION_COLD_LAUNCH = 7;
+
+    // Type for APP_TRANSITION event: The transition started a new activity for which it's process
+    // was already running.
+    TYPE_TRANSITION_WARM_LAUNCH = 8;
+
+    // Type for APP_TRANSITION event: The transition brought an already existing activity to the
+    // front.
+    TYPE_TRANSITION_HOT_LAUNCH = 9;
   }
 
   // Known visual elements: views or controls.
@@ -1168,7 +1180,7 @@
     // OS: 6.0
     BRIGHTNESS_DIALOG = 220;
 
-    // OPEN: Settings > Apps > Configure Apps > Draw over other apps
+    // OPEN: Settings > Apps > Configure Apps > Display over other apps
     // CATEGORY: SETTINGS
     // OS: 6.0
     SYSTEM_ALERT_WINDOW_APPS = 221;
@@ -3215,7 +3227,7 @@
     // ACTION: Allow "Draw over other apps" for an app
     APP_SPECIAL_PERMISSION_APPDRAW_ALLOW = 770;
 
-    // ACTION: Deny "Draw over other apps" for an app
+    // ACTION: Deny "Display over other apps" for an app
     APP_SPECIAL_PERMISSION_APPDRAW_DENY = 771;
 
     // ACTION: Allow "VR helper services" for an app
@@ -3559,6 +3571,107 @@
     // ACTION: Settings > Connected devices > Bluetooth master switch Toggle
     ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE = 870;
 
+    // The name of the activity being launched in an app transition event.
+    APP_TRANSITION_ACTIVITY_NAME = 871;
+
+    // ACTION: Settings > App detail > Uninstall
+    ACTION_SETTINGS_UNINSTALL_APP = 872;
+
+    // ACTION: Settings > App detail > Uninstall Device admin app
+    ACTION_SETTINGS_UNINSTALL_DEVICE_ADMIN = 873;
+
+    // ACTION: Settings > App detail > Disable app
+    ACTION_SETTINGS_DISABLE_APP = 874;
+
+    // ACTION: Settings > App detail > Enable app
+    ACTION_SETTINGS_ENABLE_APP = 875;
+
+    // ACTION: Settings > App detail > Clear data
+    ACTION_SETTINGS_CLEAR_APP_DATA = 876;
+
+    // ACTION: Settings > App detail > Clear cache
+    ACTION_SETTINGS_CLEAR_APP_CACHE = 877;
+
+    // ACTION: Clicking on any search result in Settings.
+    ACTION_CLICK_SETTINGS_SEARCH_INLINE_RESULT = 878;
+
+    // FIELD: Settings inline search result name
+    FIELD_SETTINGS_SEARCH_INLINE_RESULT_NAME = 879;
+
+    // FIELD: Settings inline search result value
+    FIELD_SETTINGS_SEARCH_INLINE_RESULT_VALUE = 880;
+
+    // ACTION: Settings > Search > Click saved queries
+    ACTION_CLICK_SETTINGS_SEARCH_SAVED_QUERY = 881;
+
+    // OPEN: Settings > Security & screen lock -> Lock screen preferences
+    // CATEGORY: SETTINGS
+    SETTINGS_LOCK_SCREEN_PREFERENCES = 882;
+
+    // ACTION: An app requested the app-op permission ACCESS_NOTIFICATIONS
+    // PACKAGE: The package name of the app requesting the permission
+    ACTION_APPOP_REQUEST_ACCESS_NOTIFICATIONS = 883;
+
+    // ACTION: An app was granted the app-op permission ACCESS_NOTIFICATIONS
+    // PACKAGE: The package name of the app that was granted the permission
+    ACTION_APPOP_GRANT_ACCESS_NOTIFICATIONS = 884;
+
+    // ACTION: An app requested the app-op permission ACCESS_NOTIFICATIONS and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    ACTION_APPOP_DENIED_ACCESS_NOTIFICATIONS = 885;
+
+    // ACTION: The app-op permission ACCESS_NOTIFICATIONS was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    ACTION_APPOP_REVOKE_ACCESS_NOTIFICATIONS = 886;
+
+    // ACTION: An app requested the app-op permission SYSTEM_ALERT_WINDOW
+    // PACKAGE: The package name of the app requesting the permission
+    ACTION_APPOP_REQUEST_SYSTEM_ALERT_WINDOW = 887;
+
+    // ACTION: An app was granted the app-op permission SYSTEM_ALERT_WINDOW
+    // PACKAGE: The package name of the app that was granted the permission
+    ACTION_APPOP_GRANT_SYSTEM_ALERT_WINDOW = 888;
+
+    // ACTION: An app requested the app-op permission SYSTEM_ALERT_WINDOW and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    ACTION_APPOP_DENIED_SYSTEM_ALERT_WINDOW = 889;
+
+    // ACTION: The app-op permission SYSTEM_ALERT_WINDOW was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    ACTION_APPOP_REVOKE_SYSTEM_ALERT_WINDOW = 890;
+
+    // ACTION: An app requested the app-op permission REQUEST_WRITE_SETTINGS
+    // PACKAGE: The package name of the app requesting the permission
+    ACTION_APPOP_REQUEST_WRITE_SETTINGS = 891;
+
+    // ACTION: An app was granted the app-op permission REQUEST_WRITE_SETTINGS
+    // PACKAGE: The package name of the app that was granted the permission
+    ACTION_APPOP_GRANT_WRITE_SETTINGS = 892;
+
+    // ACTION: An app requested the app-op permission REQUEST_WRITE_SETTINGS and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    ACTION_APPOP_DENIED_WRITE_SETTINGS = 893;
+
+    // ACTION: The app-op permission REQUEST_WRITE_SETTINGS was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    ACTION_APPOP_REVOKE_WRITE_SETTINGS = 894;
+
+    // ACTION: An app requested the app-op permission REQUEST_INSTALL_PACKAGES
+    // PACKAGE: The package name of the app requesting the permission
+    ACTION_APPOP_REQUEST_REQUEST_INSTALL_PACKAGES = 895;
+
+    // ACTION: An app was granted the app-op permission REQUEST_INSTALL_PACKAGES
+    // PACKAGE: The package name of the app that was granted the permission
+    ACTION_APPOP_GRANT_REQUEST_INSTALL_PACKAGES = 896;
+
+    // ACTION: An app requested the app-op permission REQUEST_INSTALL_PACKAGES and the request was denied
+    // PACKAGE: The package name of the app requesting the permission
+    ACTION_APPOP_DENIED_REQUEST_INSTALL_PACKAGES = 897;
+
+    // ACTION: The app-op permission REQUEST_INSTALL_PACKAGES was revoked for an app
+    // PACKAGE: The package name of the app the permission was revoked for
+    ACTION_APPOP_REVOKE_REQUEST_INSTALL_PACKAGES = 898;
+
     // ---- End O Constants, all O constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index b4630ef..c532efb 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -48,9 +48,6 @@
 
 using namespace android;
 
-template <typename... T>
-void UNUSED(T... t) {}
-
 #define PER_ARRAY_TYPE(flag, fnc, readonly, ...) {                                      \
     jint len = 0;                                                                       \
     void *ptr = nullptr;                                                                \
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index fd93865..9e4d89c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -77,9 +77,6 @@
      */
     static final int FLAG_FEATURE_INJECT_MOTION_EVENTS = 0x00000010;
 
-    static final int FEATURES_AFFECTING_MOTION_EVENTS = FLAG_FEATURE_INJECT_MOTION_EVENTS
-            | FLAG_FEATURE_AUTOCLICK | FLAG_FEATURE_TOUCH_EXPLORATION
-            | FLAG_FEATURE_SCREEN_MAGNIFIER;
     /**
      * Flag for enabling the feature to control the screen magnifier. If
      * {@link #FLAG_FEATURE_SCREEN_MAGNIFIER} is set this flag is ignored
@@ -90,6 +87,16 @@
      */
     static final int FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER = 0x00000020;
 
+    /**
+     * Flag for enabling the feature to trigger the screen magnifier
+     * from another on-device interaction.
+     */
+    static final int FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER = 0x00000040;
+
+    static final int FEATURES_AFFECTING_MOTION_EVENTS = FLAG_FEATURE_INJECT_MOTION_EVENTS
+            | FLAG_FEATURE_AUTOCLICK | FLAG_FEATURE_TOUCH_EXPLORATION
+            | FLAG_FEATURE_SCREEN_MAGNIFIER | FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
+
     private final Runnable mProcessBatchedEventsRunnable = new Runnable() {
         @Override
         public void run() {
@@ -379,6 +386,12 @@
         }
     }
 
+    void notifyAccessibilityButtonClicked() {
+        if (mMagnificationGestureHandler != null) {
+            mMagnificationGestureHandler.notifyShortcutTriggered();
+        }
+    }
+
     private void enableFeatures() {
         resetStreamState();
 
@@ -393,11 +406,14 @@
         }
 
         if ((mEnabledFeatures & FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER) != 0
-                || (mEnabledFeatures  & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0) {
+                || ((mEnabledFeatures & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0)
+                || ((mEnabledFeatures & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0)) {
             final boolean detectControlGestures = (mEnabledFeatures
                     & FLAG_FEATURE_SCREEN_MAGNIFIER) != 0;
+            final boolean triggerable = (mEnabledFeatures
+                    & FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER) != 0;
             mMagnificationGestureHandler = new MagnificationGestureHandler(
-                    mContext, mAms, detectControlGestures);
+                    mContext, mAms, detectControlGestures, triggerable);
             addFirstEventHandler(mMagnificationGestureHandler);
         }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index e0d7806..397938a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -50,6 +50,7 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.display.DisplayManager;
+import android.hardware.fingerprint.IFingerprintService;
 import android.hardware.input.InputManager;
 import android.net.Uri;
 import android.os.Binder;
@@ -69,7 +70,6 @@
 import android.os.UserManager;
 import android.os.UserManagerInternal;
 import android.provider.Settings;
-import android.hardware.fingerprint.IFingerprintService;
 import android.provider.SettingsStringUtil.ComponentNameSet;
 import android.provider.SettingsStringUtil.SettingStringHelper;
 import android.text.TextUtils;
@@ -100,7 +100,9 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.SomeArgs;
+import com.android.internal.util.IntPair;
 import com.android.server.LocalServices;
 import com.android.server.policy.AccessibilityShortcutController;
 import com.android.server.statusbar.StatusBarManagerInternal;
@@ -120,6 +122,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Consumer;
 
 /**
  * This class is instantiated by the system as a system level service and can be
@@ -413,10 +416,12 @@
                     removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
                 } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
                     // We will update when the automation service dies.
-                    UserState userState = getCurrentUserStateLocked();
-                    if (!userState.isUiAutomationSuppressingOtherServices()) {
-                        if (readConfigurationForUserStateLocked(userState)) {
-                            onUserStateChangedLocked(userState);
+                    synchronized (mLock) {
+                        UserState userState = getCurrentUserStateLocked();
+                        if (!userState.isUiAutomationSuppressingOtherServices()) {
+                            if (readConfigurationForUserStateLocked(userState)) {
+                                onUserStateChangedLocked(userState);
+                            }
                         }
                     }
                 } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) {
@@ -434,7 +439,7 @@
     }
 
     @Override
-    public int addClient(IAccessibilityManagerClient client, int userId) {
+    public long addClient(IAccessibilityManagerClient client, int userId) {
         synchronized (mLock) {
             // We treat calls from a profile as if made by its parent as profiles
             // share the accessibility state of the parent. The call below
@@ -450,7 +455,8 @@
                 if (DEBUG) {
                     Slog.i(LOG_TAG, "Added global client for pid:" + Binder.getCallingPid());
                 }
-                return userState.getClientState();
+                return IntPair.of(
+                        userState.getClientState(), userState.mLastSentRelevantEventTypes);
             } else {
                 userState.mUserClients.register(client);
                 // If this client is not for the current user we do not
@@ -460,7 +466,9 @@
                     Slog.i(LOG_TAG, "Added user client for pid:" + Binder.getCallingPid()
                             + " and userId:" + mCurrentUserId);
                 }
-                return (resolvedUserId == mCurrentUserId) ? userState.getClientState() : 0;
+                return IntPair.of(
+                        (resolvedUserId == mCurrentUserId) ? userState.getClientState() : 0,
+                        userState.mLastSentRelevantEventTypes);
             }
         }
     }
@@ -773,6 +781,7 @@
                 userState.mIsTouchExplorationEnabled = false;
                 userState.mIsEnhancedWebAccessibilityEnabled = false;
                 userState.mIsDisplayMagnificationEnabled = false;
+                userState.mIsNavBarMagnificationEnabled = false;
                 userState.mIsAutoclickEnabled = false;
                 userState.mEnabledServices.clear();
             }
@@ -823,6 +832,7 @@
             userState.mIsTouchExplorationEnabled = touchExplorationEnabled;
             userState.mIsEnhancedWebAccessibilityEnabled = false;
             userState.mIsDisplayMagnificationEnabled = false;
+            userState.mIsNavBarMagnificationEnabled = false;
             userState.mIsAutoclickEnabled = false;
             userState.mEnabledServices.clear();
             userState.mEnabledServices.add(service);
@@ -1144,11 +1154,16 @@
 
     private void notifyAccessibilityButtonClickedLocked() {
         final UserState state = getCurrentUserStateLocked();
-        for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
-            final Service service = state.mBoundServices.get(i);
-            // TODO(b/34720082): Only notify a single user-defined service
-            if (service.mRequestAccessibilityButton) {
-                service.notifyAccessibilityButtonClickedLocked();
+        if (state.mIsNavBarMagnificationEnabled) {
+            mMainHandler.obtainMessage(
+                    MainHandler.MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER).sendToTarget();
+        } else {
+            for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
+                final Service service = state.mBoundServices.get(i);
+                // TODO(b/34720082): Only notify a single user-defined service
+                if (service.mRequestAccessibilityButton) {
+                    service.notifyAccessibilityButtonClickedLocked();
+                }
             }
         }
     }
@@ -1323,6 +1338,35 @@
         scheduleNotifyClientsOfServicesStateChange(userState);
     }
 
+    private void updateRelevantEventsLocked(UserState userState) {
+        int relevantEventTypes = AccessibilityCache.CACHE_CRITICAL_EVENTS_MASK;
+        for (Service service : userState.mBoundServices) {
+            relevantEventTypes |= service.mEventTypes;
+        }
+        int finalRelevantEventTypes = relevantEventTypes;
+
+        if (userState.mLastSentRelevantEventTypes != finalRelevantEventTypes) {
+            userState.mLastSentRelevantEventTypes = finalRelevantEventTypes;
+            mMainHandler.obtainMessage(MainHandler.MSG_SEND_RELEVANT_EVENTS_CHANGED_TO_CLIENTS,
+                    userState.mUserId, finalRelevantEventTypes);
+            mMainHandler.post(() -> {
+                broadcastToClients(userState, (client) -> {
+                    try {
+                        client.setRelevantEventTypes(finalRelevantEventTypes);
+                    } catch (RemoteException re) {
+                        /* ignore */
+                    }
+                });
+            });
+        }
+    }
+
+    private void broadcastToClients(
+            UserState userState, Consumer<IAccessibilityManagerClient> clientAction) {
+        mGlobalClients.broadcast(clientAction);
+        userState.mUserClients.broadcast(clientAction);
+    }
+
     /**
      * Determines if given event can be dispatched to a service based on the package of the
      * event source. Specifically, a service is notified if it is interested in events from the
@@ -1511,6 +1555,9 @@
             if (userState.mIsDisplayMagnificationEnabled) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
             }
+            if (userState.mIsNavBarMagnificationEnabled) {
+                flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
+            }
             if (userHasMagnificationServicesLocked(userState)) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER;
             }
@@ -1633,6 +1680,7 @@
         scheduleUpdateFingerprintGestureHandling(userState);
         scheduleUpdateInputFilter(userState);
         scheduleUpdateClientsIfNeededLocked(userState);
+        updateRelevantEventsLocked(userState);
     }
 
     private void updateAccessibilityFocusBehaviorLocked(UserState userState) {
@@ -1743,7 +1791,7 @@
         somethingChanged |= readTouchExplorationEnabledSettingLocked(userState);
         somethingChanged |= readHighTextContrastEnabledSettingLocked(userState);
         somethingChanged |= readEnhancedWebAccessibilityEnabledChangedLocked(userState);
-        somethingChanged |= readDisplayMagnificationEnabledSettingLocked(userState);
+        somethingChanged |= readMagnificationEnabledSettingsLocked(userState);
         somethingChanged |= readAutoclickEnabledSettingLocked(userState);
         somethingChanged |= readAccessibilityShortcutSettingLocked(userState);
         return somethingChanged;
@@ -1772,13 +1820,19 @@
         return false;
     }
 
-    private boolean readDisplayMagnificationEnabledSettingLocked(UserState userState) {
+    private boolean readMagnificationEnabledSettingsLocked(UserState userState) {
         final boolean displayMagnificationEnabled = Settings.Secure.getIntForUser(
                 mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
                 0, userState.mUserId) == 1;
-        if (displayMagnificationEnabled != userState.mIsDisplayMagnificationEnabled) {
+        final boolean navBarMagnificationEnabled = Settings.Secure.getIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
+                0, userState.mUserId) == 1;
+        if ((displayMagnificationEnabled != userState.mIsDisplayMagnificationEnabled)
+                || (navBarMagnificationEnabled != userState.mIsNavBarMagnificationEnabled)) {
             userState.mIsDisplayMagnificationEnabled = displayMagnificationEnabled;
+            userState.mIsNavBarMagnificationEnabled = navBarMagnificationEnabled;
             return true;
         }
         return false;
@@ -1980,8 +2034,8 @@
             return;
         }
 
-        if (userState.mIsDisplayMagnificationEnabled ||
-                userHasListeningMagnificationServicesLocked(userState)) {
+        if (userState.mIsDisplayMagnificationEnabled || userState.mIsNavBarMagnificationEnabled
+                || userHasListeningMagnificationServicesLocked(userState)) {
             // Initialize the magnification controller if necessary
             getMagnificationController();
             mMagnificationController.register();
@@ -2203,6 +2257,8 @@
                 pw.append(", touchExplorationEnabled=" + userState.mIsTouchExplorationEnabled);
                 pw.append(", displayMagnificationEnabled="
                         + userState.mIsDisplayMagnificationEnabled);
+                pw.append(", navBarMagnificationEnabled="
+                        + userState.mIsNavBarMagnificationEnabled);
                 pw.append(", autoclickEnabled=" + userState.mIsAutoclickEnabled);
                 if (userState.mUiAutomationService != null) {
                     pw.append(", ");
@@ -2281,6 +2337,8 @@
         public static final int MSG_CLEAR_ACCESSIBILITY_FOCUS = 9;
         public static final int MSG_SEND_SERVICES_STATE_CHANGED_TO_CLIENTS = 10;
         public static final int MSG_UPDATE_FINGERPRINT = 11;
+        public static final int MSG_SEND_RELEVANT_EVENTS_CHANGED_TO_CLIENTS = 12;
+        public static final int MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER = 13;
 
         public MainHandler(Looper looper) {
             super(looper);
@@ -2351,6 +2409,30 @@
                 case MSG_UPDATE_FINGERPRINT: {
                     updateFingerprintGestureHandling((UserState) msg.obj);
                 } break;
+
+                case MSG_SEND_RELEVANT_EVENTS_CHANGED_TO_CLIENTS: {
+                    final int userId = msg.arg1;
+                    final int relevantEventTypes = msg.arg2;
+                    final UserState userState;
+                    synchronized (mLock) {
+                        userState = getUserStateLocked(userId);
+                    }
+                    broadcastToClients(userState, (client) -> {
+                        try {
+                            client.setRelevantEventTypes(relevantEventTypes);
+                        } catch (RemoteException re) {
+                            /* ignore */
+                        }
+                    });
+                } break;
+
+               case MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER: {
+                    synchronized (mLock) {
+                        if (mHasInputFilter && mInputFilter != null) {
+                            mInputFilter.notifyAccessibilityButtonClicked();
+                        }
+                    }
+                }
             }
         }
 
@@ -2380,19 +2462,13 @@
 
         private void sendStateToClients(int clientState,
                 RemoteCallbackList<IAccessibilityManagerClient> clients) {
-            try {
-                final int userClientCount = clients.beginBroadcast();
-                for (int i = 0; i < userClientCount; i++) {
-                    IAccessibilityManagerClient client = clients.getBroadcastItem(i);
-                    try {
-                        client.setState(clientState);
-                    } catch (RemoteException re) {
-                        /* ignore */
-                    }
+            clients.broadcast((client) -> {
+                try {
+                    client.setState(clientState);
+                } catch (RemoteException re) {
+                    /* ignore */
                 }
-            } finally {
-                clients.finishBroadcast();
-            }
+            });
         }
 
         private void notifyClientsOfServicesStateChange(
@@ -4710,6 +4786,8 @@
         public final CopyOnWriteArrayList<Service> mBoundServices =
                 new CopyOnWriteArrayList<>();
 
+        public int mLastSentRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK;
+
         public final Map<ComponentName, Service> mComponentNameToServiceMap =
                 new HashMap<>();
 
@@ -4737,6 +4815,7 @@
         public boolean mIsTextHighContrastEnabled;
         public boolean mIsEnhancedWebAccessibilityEnabled;
         public boolean mIsDisplayMagnificationEnabled;
+        public boolean mIsNavBarMagnificationEnabled;
         public boolean mIsAutoclickEnabled;
         public boolean mIsPerformGesturesEnabled;
         public boolean mIsFilterKeyEventsEnabled;
@@ -4805,6 +4884,7 @@
             mIsTouchExplorationEnabled = false;
             mIsEnhancedWebAccessibilityEnabled = false;
             mIsDisplayMagnificationEnabled = false;
+            mIsNavBarMagnificationEnabled = false;
             mIsAutoclickEnabled = false;
             mSoftKeyboardShowMode = 0;
 
@@ -4837,6 +4917,9 @@
         private final Uri mDisplayMagnificationEnabledUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
 
+        private final Uri mNavBarMagnificationEnabledUri = Settings.Secure.getUriFor(
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
+
         private final Uri mAutoclickEnabledUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED);
 
@@ -4876,6 +4959,8 @@
                     false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(mDisplayMagnificationEnabledUri,
                     false, this, UserHandle.USER_ALL);
+            contentResolver.registerContentObserver(mNavBarMagnificationEnabledUri,
+                    false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(mAutoclickEnabledUri,
                     false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(mEnabledAccessibilityServicesUri,
@@ -4915,8 +5000,9 @@
                     if (readTouchExplorationEnabledSettingLocked(userState)) {
                         onUserStateChangedLocked(userState);
                     }
-                } else if (mDisplayMagnificationEnabledUri.equals(uri)) {
-                    if (readDisplayMagnificationEnabledSettingLocked(userState)) {
+                } else if (mDisplayMagnificationEnabledUri.equals(uri)
+                        || mNavBarMagnificationEnabledUri.equals(uri)) {
+                    if (readMagnificationEnabledSettingsLocked(userState)) {
                         onUserStateChangedLocked(userState);
                     }
                 } else if (mAutoclickEnabledUri.equals(uri)) {
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index f65046c..caa74b9 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -685,6 +685,12 @@
         }
     }
 
+    void setForceShowMagnifiableBounds(boolean show) {
+        if (mRegistered) {
+            mWindowManager.setForceShowMagnifiableBounds(show);
+        }
+    }
+
     private void getMagnifiedFrameInContentCoordsLocked(Rect outFrame) {
         final float scale = getSentScale();
         final float offsetX = getSentOffsetX();
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index f6e5340..7e82eda 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -16,7 +16,10 @@
 
 package com.android.server.accessibility;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.os.Handler;
 import android.os.Message;
 import android.util.MathUtils;
@@ -57,22 +60,30 @@
  *    be the same but when the finger goes up the screen will stay magnified.
  *    In other words, the initial magnified state is sticky.
  *
- * 3. Pinching with any number of additional fingers when viewport dragging
+ * 3. Magnification can optionally be "triggered" by some external shortcut
+ *    affordance. When this occurs via {@link #notifyShortcutTriggered()} a
+ *    subsequent tap in a magnifiable region will engage permanent screen
+ *    magnification as described in #1. Alternatively, a subsequent long-press
+ *    or drag will engage magnification with viewport dragging as described in
+ *    #2. Once magnified, all following behaviors apply whether magnification
+ *    was engaged via a triple-tap or by a triggered shortcut.
+ *
+ * 4. Pinching with any number of additional fingers when viewport dragging
  *    is enabled, i.e. the user triple tapped and holds, would adjust the
  *    magnification scale which will become the current default magnification
  *    scale. The next time the user magnifies the same magnification scale
  *    would be used.
  *
- * 4. When in a permanent magnified state the user can use two or more fingers
+ * 5. When in a permanent magnified state the user can use two or more fingers
  *    to pan the viewport. Note that in this mode the content is panned as
  *    opposed to the viewport dragging mode in which the viewport is moved.
  *
- * 5. When in a permanent magnified state the user can use two or more
+ * 6. When in a permanent magnified state the user can use two or more
  *    fingers to change the magnification scale which will become the current
  *    default magnification scale. The next time the user magnifies the same
  *    magnification scale would be used.
  *
- * 6. The magnification scale will be persisted in settings and in the cloud.
+ * 7. The magnification scale will be persisted in settings and in the cloud.
  */
 class MagnificationGestureHandler implements EventStreamTransformation {
     private static final String LOG_TAG = "MagnificationEventHandler";
@@ -94,8 +105,10 @@
     private final MagnifiedContentInteractionStateHandler mMagnifiedContentInteractionStateHandler;
     private final StateViewportDraggingHandler mStateViewportDraggingHandler;
 
+    private final ScreenStateReceiver mScreenStateReceiver;
 
-    private final boolean mDetectControlGestures;
+    private final boolean mDetectTripleTap;
+    private final boolean mTriggerable;
 
     private EventStreamTransformation mNext;
 
@@ -104,19 +117,39 @@
 
     private boolean mTranslationEnabledBeforePan;
 
+    private boolean mShortcutTriggered;
+
     private PointerCoords[] mTempPointerCoords;
     private PointerProperties[] mTempPointerProperties;
 
     private long mDelegatingStateDownTime;
 
+    /**
+     * @param context Context for resolving various magnification-related resources
+     * @param ams AccessibilityManagerService used to obtain a {@link MagnificationController}
+     * @param detectTripleTap {@code true} if this detector should detect and respond to triple-tap
+     *                                    gestures for engaging and disengaging magnification,
+     *                                    {@code false} if it should ignore such gestures
+     * @param triggerable {@code true} if this detector should be "triggerable" by some external
+     *                                shortcut invoking {@link #notifyShortcutTriggered}, {@code
+     *                                false} if it should ignore such triggers.
+     */
     public MagnificationGestureHandler(Context context, AccessibilityManagerService ams,
-            boolean detectControlGestures) {
+            boolean detectTripleTap, boolean triggerable) {
         mMagnificationController = ams.getMagnificationController();
         mDetectingStateHandler = new DetectingStateHandler(context);
         mStateViewportDraggingHandler = new StateViewportDraggingHandler();
         mMagnifiedContentInteractionStateHandler =
                 new MagnifiedContentInteractionStateHandler(context);
-        mDetectControlGestures = detectControlGestures;
+        mDetectTripleTap = detectTripleTap;
+        mTriggerable = triggerable;
+
+        if (triggerable) {
+            mScreenStateReceiver = new ScreenStateReceiver(context, this);
+            mScreenStateReceiver.register();
+        } else {
+            mScreenStateReceiver = null;
+        }
 
         transitionToState(STATE_DETECTING);
     }
@@ -129,7 +162,7 @@
             }
             return;
         }
-        if (!mDetectControlGestures) {
+        if (!mDetectTripleTap && !mTriggerable) {
             if (mNext != null) {
                 dispatchTransformedEvent(event, rawEvent, policyFlags);
             }
@@ -151,7 +184,7 @@
             break;
             case STATE_MAGNIFIED_INTERACTION: {
                 // mMagnifiedContentInteractionStateHandler handles events only
-                // if this is the current state since it uses ScaleGestureDetecotr
+                // if this is the current state since it uses ScaleGestureDetector
                 // and a GestureDetector which need well formed event stream.
             }
             break;
@@ -193,11 +226,34 @@
 
     @Override
     public void onDestroy() {
+        if (mScreenStateReceiver != null) {
+            mScreenStateReceiver.unregister();
+        }
         clear();
     }
 
+    void notifyShortcutTriggered() {
+        if (mTriggerable) {
+            if (mMagnificationController.resetIfNeeded(true)) {
+                clear();
+            } else {
+                setMagnificationShortcutTriggered(!mShortcutTriggered);
+            }
+        }
+    }
+
+    private void setMagnificationShortcutTriggered(boolean state) {
+        if (mShortcutTriggered == state) {
+            return;
+        }
+
+        mShortcutTriggered = state;
+        mMagnificationController.setForceShowMagnifiableBounds(state);
+    }
+
     private void clear() {
         mCurrentState = STATE_DETECTING;
+        setMagnificationShortcutTriggered(false);
         mDetectingStateHandler.clear();
         mStateViewportDraggingHandler.clear();
         mMagnifiedContentInteractionStateHandler.clear();
@@ -575,31 +631,51 @@
                     mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
                     if (!mMagnificationController.magnificationRegionContains(
                             event.getX(), event.getY())) {
-                        transitionToDelegatingStateAndClear();
+                        transitionToDelegatingState(!mShortcutTriggered);
                         return;
                     }
-                    if (mTapCount == ACTION_TAP_COUNT - 1 && mLastDownEvent != null
-                            && GestureUtils.isMultiTap(mLastDownEvent, event,
-                            mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) {
+                    if (mShortcutTriggered) {
                         Message message = mHandler.obtainMessage(MESSAGE_ON_ACTION_TAP_AND_HOLD,
                                 policyFlags, 0, event);
                         mHandler.sendMessageDelayed(message,
                                 ViewConfiguration.getLongPressTimeout());
-                    } else if (mTapCount < ACTION_TAP_COUNT) {
+                        return;
+                    }
+                    if (mDetectTripleTap) {
+                        if ((mTapCount == ACTION_TAP_COUNT - 1) && (mLastDownEvent != null)
+                                && GestureUtils.isMultiTap(mLastDownEvent, event, mMultiTapTimeSlop,
+                                        mMultiTapDistanceSlop, 0)) {
+                            Message message = mHandler.obtainMessage(MESSAGE_ON_ACTION_TAP_AND_HOLD,
+                                    policyFlags, 0, event);
+                            mHandler.sendMessageDelayed(message,
+                                    ViewConfiguration.getLongPressTimeout());
+                        } else if (mTapCount < ACTION_TAP_COUNT) {
+                            Message message = mHandler.obtainMessage(
+                                    MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+                            mHandler.sendMessageDelayed(message, mMultiTapTimeSlop);
+                        }
+                        clearLastDownEvent();
+                        mLastDownEvent = MotionEvent.obtain(event);
+                    } else if (mMagnificationController.isMagnifying()) {
+                        // If magnified, consume an ACTION_DOWN until mMultiTapTimeSlop or
+                        // mTapDistanceSlop is reached to ensure MAGNIFIED_INTERACTION is reachable.
                         Message message = mHandler.obtainMessage(
                                 MESSAGE_TRANSITION_TO_DELEGATING_STATE);
                         mHandler.sendMessageDelayed(message, mMultiTapTimeSlop);
+                        return;
+                    } else {
+                        transitionToDelegatingState(true);
+                        return;
                     }
-                    clearLastDownEvent();
-                    mLastDownEvent = MotionEvent.obtain(event);
                 }
                 break;
                 case MotionEvent.ACTION_POINTER_DOWN: {
                     if (mMagnificationController.isMagnifying()) {
+                        mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
                         transitionToState(STATE_MAGNIFIED_INTERACTION);
                         clear();
                     } else {
-                        transitionToDelegatingStateAndClear();
+                        transitionToDelegatingState(true);
                     }
                 }
                 break;
@@ -608,29 +684,34 @@
                         final double distance = GestureUtils.computeDistance(mLastDownEvent,
                                 event, 0);
                         if (Math.abs(distance) > mTapDistanceSlop) {
-                            transitionToDelegatingStateAndClear();
+                            transitionToDelegatingState(true);
                         }
                     }
                 }
                 break;
                 case MotionEvent.ACTION_UP: {
+                    if (!mMagnificationController.magnificationRegionContains(
+                            event.getX(), event.getY())) {
+                        transitionToDelegatingState(!mShortcutTriggered);
+                        return;
+                    }
+                    if (mShortcutTriggered) {
+                        clear();
+                        onActionTap(event, policyFlags);
+                        return;
+                    }
                     if (mLastDownEvent == null) {
                         return;
                     }
                     mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD);
-                    if (!mMagnificationController.magnificationRegionContains(
-                            event.getX(), event.getY())) {
-                        transitionToDelegatingStateAndClear();
-                        return;
-                    }
                     if (!GestureUtils.isTap(mLastDownEvent, event, mTapTimeSlop,
                             mTapDistanceSlop, 0)) {
-                        transitionToDelegatingStateAndClear();
+                        transitionToDelegatingState(true);
                         return;
                     }
-                    if (mLastTapUpEvent != null && !GestureUtils.isMultiTap(mLastTapUpEvent,
-                            event, mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) {
-                        transitionToDelegatingStateAndClear();
+                    if (mLastTapUpEvent != null && !GestureUtils.isMultiTap(
+                            mLastTapUpEvent, event, mMultiTapTimeSlop, mMultiTapDistanceSlop, 0)) {
+                        transitionToDelegatingState(true);
                         return;
                     }
                     mTapCount++;
@@ -655,6 +736,7 @@
 
         @Override
         public void clear() {
+            setMagnificationShortcutTriggered(false);
             mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD);
             mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
             clearTapDetectionState();
@@ -714,10 +796,12 @@
             }
         }
 
-        private void transitionToDelegatingStateAndClear() {
+        private void transitionToDelegatingState(boolean andClear) {
             transitionToState(STATE_DELEGATING);
             sendDelayedMotionEvents();
-            clear();
+            if (andClear) {
+                clear();
+            }
         }
 
         private void onActionTap(MotionEvent up, int policyFlags) {
@@ -820,4 +904,30 @@
             mPolicyFlags = 0;
         }
     }
+
+    /**
+     * BroadcastReceiver used to cancel the magnification shortcut when the screen turns off
+     */
+    private static class ScreenStateReceiver extends BroadcastReceiver {
+        private final Context mContext;
+        private final MagnificationGestureHandler mGestureHandler;
+
+        public ScreenStateReceiver(Context context, MagnificationGestureHandler gestureHandler) {
+            mContext = context;
+            mGestureHandler = gestureHandler;
+        }
+
+        public void register() {
+            mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_SCREEN_OFF));
+        }
+
+        public void unregister() {
+            mContext.unregisterReceiver(this);
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mGestureHandler.setMagnificationShortcutTriggered(false);
+        }
+    }
 }
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
similarity index 88%
rename from services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
rename to services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index b90a2a2..af1193d 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -67,13 +67,13 @@
  * Entry point service for autofill management.
  *
  * <p>This service provides the {@link IAutoFillManager} implementation and keeps a list of
- * {@link AutoFillManagerServiceImpl} per user; the real work is done by
- * {@link AutoFillManagerServiceImpl} itself.
+ * {@link AutofillManagerServiceImpl} per user; the real work is done by
+ * {@link AutofillManagerServiceImpl} itself.
  */
 // TODO(b/33197203): Handle removing of packages
-public final class AutoFillManagerService extends SystemService {
+public final class AutofillManagerService extends SystemService {
 
-    private static final String TAG = "AutoFillManagerService";
+    private static final String TAG = "AutofillManagerService";
 
     static final String RECEIVER_BUNDLE_EXTRA_SESSIONS = "sessions";
 
@@ -83,7 +83,7 @@
     private final Object mLock = new Object();
 
     /**
-     * Cache of {@link AutoFillManagerServiceImpl} per user id.
+     * Cache of {@link AutofillManagerServiceImpl} per user id.
      * <p>
      * It has to be mapped by user id because the same current user could have simultaneous sessions
      * associated to different user profiles (for example, in a multi-window environment or when
@@ -97,7 +97,7 @@
      */
     // TODO(b/33197203): Update the above comment
     @GuardedBy("mLock")
-    private SparseArray<AutoFillManagerServiceImpl> mServicesCache = new SparseArray<>();
+    private SparseArray<AutofillManagerServiceImpl> mServicesCache = new SparseArray<>();
 
     // TODO(b/33197203): set a different max (or disable it) on low-memory devices.
     private final LocalLog mRequestsHistory = new LocalLog(20);
@@ -115,7 +115,7 @@
         }
     };
 
-    public AutoFillManagerService(Context context) {
+    public AutofillManagerService(Context context) {
         super(context);
         mContext = context;
         mUi = new AutoFillUI(mContext);
@@ -157,10 +157,11 @@
      *
      * @return service instance.
      */
-    @NonNull AutoFillManagerServiceImpl getServiceForUserLocked(int userId) {
-        AutoFillManagerServiceImpl service = mServicesCache.get(userId);
+    @NonNull
+    AutofillManagerServiceImpl getServiceForUserLocked(int userId) {
+        AutofillManagerServiceImpl service = mServicesCache.get(userId);
         if (service == null) {
-            service = new AutoFillManagerServiceImpl(mContext, mLock,
+            service = new AutofillManagerServiceImpl(mContext, mLock,
                     mRequestsHistory, userId, mUi);
             mServicesCache.put(userId, service);
         }
@@ -174,7 +175,7 @@
         final IBinder activityToken = getTopActivityForUser();
         if (activityToken != null) {
             synchronized (mLock) {
-                final AutoFillManagerServiceImpl service = mServicesCache.get(userId);
+                final AutofillManagerServiceImpl service = mServicesCache.get(userId);
                 if (service == null) {
                     Log.w(TAG, "handleSaveForUser(): no cached service for userId " + userId);
                     return;
@@ -258,7 +259,7 @@
      * Removes a cached service for a given user.
      */
     private void removeCachedServiceLocked(int userId) {
-        final AutoFillManagerServiceImpl service = mServicesCache.get(userId);
+        final AutofillManagerServiceImpl service = mServicesCache.get(userId);
         if (service != null) {
             mServicesCache.delete(userId);
             service.destroyLocked();
@@ -269,7 +270,7 @@
      * Updates a cached service for a given user.
      */
     private void updateCachedServiceLocked(int userId) {
-        AutoFillManagerServiceImpl service = mServicesCache.get(userId);
+        AutofillManagerServiceImpl service = mServicesCache.get(userId);
         if (service != null) {
             service.updateLocked();
         }
@@ -299,7 +300,7 @@
         @Override
         public void setAuthenticationResult(Bundle data, IBinder activityToken, int userId) {
             synchronized (mLock) {
-                final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+                final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
                 service.setAuthenticationResultLocked(data, activityToken);
             }
         }
@@ -307,7 +308,7 @@
         @Override
         public void setHasCallback(IBinder activityToken, int userId, boolean hasIt) {
             synchronized (mLock) {
-                final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+                final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
                 service.setHasCallback(activityToken, hasIt);
             }
         }
@@ -315,13 +316,13 @@
         @Override
         public void startSession(IBinder activityToken, IBinder windowToken, IBinder appCallback,
                 AutofillId autofillId, Rect bounds, AutofillValue value, int userId,
-                boolean hasCallback) {
+                boolean hasCallback, int flags) {
             // TODO(b/33197203): make sure it's called by resumed / focused activity
 
             synchronized (mLock) {
-                final AutoFillManagerServiceImpl service = getServiceForUserLocked(userId);
+                final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
                 service.startSessionLocked(activityToken, windowToken, appCallback,
-                        autofillId, bounds, value, hasCallback);
+                        autofillId, bounds, value, hasCallback, flags);
             }
         }
 
@@ -329,7 +330,7 @@
         public void updateSession(IBinder activityToken, AutofillId id, Rect bounds,
                 AutofillValue value, int flags, int userId) {
             synchronized (mLock) {
-                final AutoFillManagerServiceImpl service = mServicesCache.get(
+                final AutofillManagerServiceImpl service = mServicesCache.get(
                         UserHandle.getCallingUserId());
                 if (service != null) {
                     service.updateSessionLocked(activityToken, id, bounds, value, flags);
@@ -340,7 +341,7 @@
         @Override
         public void finishSession(IBinder activityToken, int userId) {
             synchronized (mLock) {
-                final AutoFillManagerServiceImpl service = mServicesCache.get(
+                final AutofillManagerServiceImpl service = mServicesCache.get(
                         UserHandle.getCallingUserId());
                 if (service != null) {
                     service.finishSessionLocked(activityToken);
@@ -349,6 +350,17 @@
         }
 
         @Override
+        public void cancelSession(IBinder activityToken, int userId) {
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service = mServicesCache.get(
+                        UserHandle.getCallingUserId());
+                if (service != null) {
+                    service.cancelSessionLocked(activityToken);
+                }
+            }
+        }
+
+        @Override
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (mContext.checkCallingPermission(
                     Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
@@ -366,7 +378,7 @@
                     pw.println(size);
                     for (int i = 0; i < size; i++) {
                         pw.print("\nService at index "); pw.println(i);
-                        final AutoFillManagerServiceImpl impl = mServicesCache.valueAt(i);
+                        final AutofillManagerServiceImpl impl = mServicesCache.valueAt(i);
                         impl.dumpLocked("  ", pw);
                     }
                 }
@@ -379,7 +391,7 @@
         @Override
         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
                 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
-            (new AutoFillManagerServiceShellCommand(AutoFillManagerService.this)).exec(
+            (new AutofillManagerServiceShellCommand(AutofillManagerService.this)).exec(
                     this, in, out, err, args, callback, resultReceiver);
         }
     }
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
similarity index 87%
rename from services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
rename to services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index e691623..3e5ad82 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -19,8 +19,8 @@
 import static android.service.autofill.AutofillService.EXTRA_ACTIVITY_TOKEN;
 import static android.service.voice.VoiceInteractionSession.KEY_RECEIVER_EXTRAS;
 import static android.service.voice.VoiceInteractionSession.KEY_STRUCTURE;
-import static android.view.autofill.AutofillManager.FLAG_FOCUS_GAINED;
-import static android.view.autofill.AutofillManager.FLAG_FOCUS_LOST;
+import static android.view.autofill.AutofillManager.FLAG_VIEW_ENTERED;
+import static android.view.autofill.AutofillManager.FLAG_VIEW_EXITED;
 import static android.view.autofill.AutofillManager.FLAG_START_SESSION;
 import static android.view.autofill.AutofillManager.FLAG_VALUE_CHANGED;
 
@@ -60,6 +60,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.LocalLog;
+import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
 import android.view.autofill.AutofillId;
@@ -77,13 +78,13 @@
 import java.util.Map.Entry;
 
 /**
- * Bridge between the {@code system_server}'s {@link AutoFillManagerService} and the
+ * Bridge between the {@code system_server}'s {@link AutofillManagerService} and the
  * app's {@link IAutoFillService} implementation.
  *
  */
-final class AutoFillManagerServiceImpl {
+final class AutofillManagerServiceImpl {
 
-    private static final String TAG = "AutoFillManagerServiceImpl";
+    private static final String TAG = "AutofillManagerServiceImpl";
 
     private static final int MSG_SERVICE_SAVE = 1;
 
@@ -169,11 +170,11 @@
             structure.sanitizeForParceling(true);
 
             // TODO(b/33197203): Need to pipe the bundle
-            session.mRemoteFillService.onFillRequest(structure, null);
+            session.mRemoteFillService.onFillRequest(structure, null, session.mFlags);
         }
     };
 
-    AutoFillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
+    AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
             int userId, AutoFillUI ui) {
         mContext = context;
         mLock = lock;
@@ -240,7 +241,7 @@
     }
 
     /**
-     * Used by {@link AutoFillManagerServiceShellCommand} to request save for the current top app.
+     * Used by {@link AutofillManagerServiceShellCommand} to request save for the current top app.
      */
     void requestSaveForUserLocked(IBinder activityToken) {
         if (!hasService()) {
@@ -284,7 +285,8 @@
     }
 
     void startSessionLocked(IBinder activityToken, IBinder windowToken, IBinder appCallbackToken,
-            AutofillId autofillId,  Rect bounds, AutofillValue value, boolean hasCallback) {
+            AutofillId autofillId,  Rect bounds, AutofillValue value, boolean hasCallback,
+            int flags) {
         if (!hasService()) {
             return;
         }
@@ -292,7 +294,7 @@
         final String historyItem = "s=" + mInfo.getServiceInfo().packageName
                 + " u=" + mUserId + " a=" + activityToken
 
-                + " i=" + autofillId + " b=" + bounds + " hc=" + hasCallback;
+                + " i=" + autofillId + " b=" + bounds + " hc=" + hasCallback + " f=" + flags;
         mRequestsHistory.log(historyItem);
 
         // TODO(b/33197203): Handle partitioning
@@ -303,7 +305,7 @@
         }
 
         final Session newSession = createSessionByTokenLocked(activityToken,
-                windowToken, appCallbackToken, hasCallback);
+                windowToken, appCallbackToken, hasCallback, flags);
         newSession.updateLocked(autofillId, bounds, value, FLAG_START_SESSION);
     }
 
@@ -321,10 +323,24 @@
         session.showSaveLocked();
     }
 
+    void cancelSessionLocked(IBinder activityToken) {
+        if (!hasService()) {
+            return;
+        }
+
+        final Session session = mSessions.get(activityToken);
+        if (session == null) {
+            Slog.w(TAG, "cancelSessionLocked(): no session for " + activityToken);
+            return;
+        }
+
+        session.destroyLocked();
+    }
+
     private Session createSessionByTokenLocked(IBinder activityToken, IBinder windowToken,
-            IBinder appCallbackToken, boolean hasCallback) {
+            IBinder appCallbackToken, boolean hasCallback, int flags) {
         final Session newSession = new Session(mContext, activityToken,
-                windowToken, appCallbackToken, hasCallback);
+                windowToken, appCallbackToken, hasCallback, flags);
         mSessions.put(activityToken, newSession);
 
         /*
@@ -457,7 +473,7 @@
 
     @Override
     public String toString() {
-        return "AutoFillManagerServiceImpl: [userId=" + mUserId
+        return "AutofillManagerServiceImpl: [userId=" + mUserId
                 + ", component=" + (mInfo != null
                 ? mInfo.getServiceInfo().getComponentName() : null) + "]";
     }
@@ -610,17 +626,23 @@
         private AssistStructure mStructure;
 
         /**
-         * Whether the client has an {@link android.view.autofill.AutoFillManager.AutofillCallback}.
+         * Whether the client has an {@link android.view.autofill.AutofillManager.AutofillCallback}.
          */
         private boolean mHasCallback;
 
+        /**
+         * Flags used to start the session.
+         */
+        private int mFlags;
+
         private Session(Context context, IBinder activityToken, IBinder windowToken,
-                IBinder client, boolean hasCallback) {
+                IBinder client, boolean hasCallback, int flags) {
             mRemoteFillService = new RemoteFillService(context,
                     mInfo.getServiceInfo().getComponentName(), mUserId, this);
             mActivityToken = activityToken;
             mWindowToken = windowToken;
             mHasCallback = hasCallback;
+            mFlags = flags;
 
             mClient = IAutoFillManagerClient.Stub.asInterface(client);
             try {
@@ -788,35 +810,101 @@
                 Slog.d(TAG, "showSaveLocked(): saveInfo=" + saveInfo);
             }
 
-            if (saveInfo == null || saveInfo.getSavableIds() == null
-                    || saveInfo.getSavableIds().isEmpty()) {
+            /*
+             * The Save dialog is only shown if all conditions below are met:
+             *
+             * - saveInfo is not null
+             * - autofillValue of all required ids is not null
+             * - autofillValue of at least one id (required or optional) has changed.
+             */
+
+            if (saveInfo == null) {
                 return;
             }
 
-            final int size = saveInfo.getSavableIds().size();
-            for (int i = 0; i < size; i++) {
-                final AutofillId id = saveInfo.getSavableIds().valueAt(i);
+            final AutofillId[] requiredIds = saveInfo.getRequiredIds();
+            if (requiredIds == null || requiredIds.length == 0) {
+                Slog.w(TAG, "showSaveLocked(): no required ids on saveInfo");
+                return;
+            }
+
+            boolean allRequiredAreNotEmpty = true;
+            boolean atLeastOneChanged = false;
+            for (int i = 0; i < requiredIds.length; i++) {
+                final AutofillId id = requiredIds[i];
                 final ViewState state = mViewStates.get(id);
-                if (state != null && state.mValueUpdated) {
+                if (state == null || state.mAutofillValue == null
+                         || state.mAutofillValue.isEmpty()) {
+                    final ViewNode node = findViewNodeByIdLocked(id);
+                    if (node == null) {
+                        Slog.w(TAG, "Service passed invalid id on SavableInfo: " + id);
+                        allRequiredAreNotEmpty = false;
+                        break;
+                    }
+                    final AutofillValue initialValue = node.getAutofillValue();
+                    if (initialValue == null || initialValue.isEmpty()) {
+                        if (DEBUG) {
+                            Slog.d(TAG, "finishSessionLocked(): empty initial value for " + id );
+                        }
+                        allRequiredAreNotEmpty = false;
+                        break;
+                    }
+                }
+                if (state.mValueUpdated) {
                     final AutofillValue filledValue = findValue(mAutoFilledDataset, id);
-                    if (state.mAutofillValue == null || state.mAutofillValue.equals(filledValue)) {
-                        continue;
+                    if (!state.mAutofillValue.equals(filledValue)) {
+                        if (DEBUG) {
+                            Slog.d(TAG, "finishSessionLocked(): found a change on " + id + ": "
+                                    + filledValue + " => " + state.mAutofillValue);
+                        }
+                        atLeastOneChanged = true;
                     }
-                    if (DEBUG) {
-                        Slog.d(TAG, "finishSessionLocked(): found a change on " + id + ": "
-                                + state.mAutofillValue);
+                } else {
+                    if (state.mAutofillValue == null || state.mAutofillValue.isEmpty()) {
+                        if (DEBUG) {
+                            Slog.d(TAG, "finishSessionLocked(): empty value for " + id + ": "
+                                    + state.mAutofillValue);
+                        }
+                        allRequiredAreNotEmpty = false;
+                        break;
+
                     }
+                }
+            }
+
+            if (allRequiredAreNotEmpty) {
+                if (!atLeastOneChanged && saveInfo.getOptionalIds() != null) {
+                    for (int i = 0; i < saveInfo.getOptionalIds().length; i++) {
+                        final AutofillId id = saveInfo.getOptionalIds()[i];
+                        final ViewState state = mViewStates.get(id);
+                        if (state != null && state.mAutofillValue != null && state.mValueUpdated) {
+                            final AutofillValue filledValue = findValue(mAutoFilledDataset, id);
+                            if (!state.mAutofillValue.equals(filledValue)) {
+                                if (DEBUG) {
+                                    Slog.d(TAG, "finishSessionLocked(): found a change on optional "
+                                            + id + ": " + filledValue + " => "
+                                            + state.mAutofillValue);
+                                }
+                                atLeastOneChanged = true;
+                                break;
+                            }
+                        }
+                    }
+                }
+                if (atLeastOneChanged) {
                     getUiForShowing().showSaveUi(
                             mInfo.getServiceInfo().loadLabel(mContext.getPackageManager()),
                             saveInfo);
                     return;
                 }
             }
-
             // Nothing changed...
             if (DEBUG) {
-                Slog.d(TAG, "showSaveLocked(): with no changes, comes no responsibilities");
+                Slog.d(TAG, "showSaveLocked(): with no changes, comes no responsibilities."
+                        + "allRequiredAreNotNull=" + allRequiredAreNotEmpty
+                        + ", atLeastOneChanged=" + atLeastOneChanged);
             }
+            removeSelf();
         }
 
         /**
@@ -899,13 +987,17 @@
                     viewState.mAutofillValue = value;
 
                     // Update the chooser UI
-                    getUiForShowing().filterFillUi(value.coerceToString());
+                    if (value.isText()) {
+                        getUiForShowing().filterFillUi(value.getTextValue().toString());
+                    } else {
+                        getUiForShowing().filterFillUi(null);
+                    }
                 }
 
                 return;
             }
 
-            if ((flags & FLAG_FOCUS_GAINED) != 0) {
+            if ((flags & FLAG_VIEW_ENTERED) != 0) {
                 // Remove the UI if the ViewState has changed.
                 if (mCurrentViewState != viewState) {
                     mUi.hideFillUi(mCurrentViewState != null ? mCurrentViewState.mId : null);
@@ -923,7 +1015,7 @@
                 return;
             }
 
-            if ((flags & FLAG_FOCUS_LOST) != 0) {
+            if ((flags & FLAG_VIEW_EXITED) != 0) {
                 if (mCurrentViewState == viewState) {
                     mUi.hideFillUi(viewState.mId);
                     mCurrentViewState = null;
@@ -937,13 +1029,9 @@
         @Override
         public void onFillReady(ViewState viewState, FillResponse response, Rect bounds,
                 AutofillId filledId, @Nullable AutofillValue value) {
-            String filterText = "";
-            if (value != null) {
-                // TODO(b/33197203): Handle other AutofillValue types
-                final CharSequence text = value.getTextValue();
-                if (text != null) {
-                    filterText = text.toString();
-                }
+            String filterText = null;
+            if (value != null && value.isText()) {
+                filterText = value.getTextValue().toString();
             }
 
             getUiForShowing().showFillUi(filledId, response, bounds, filterText);
@@ -1001,7 +1089,7 @@
         }
 
         CharSequence getServiceName() {
-            return AutoFillManagerServiceImpl.this.getServiceName();
+            return AutofillManagerServiceImpl.this.getServiceName();
         }
 
         private Intent createAuthFillInIntent(AssistStructure structure) {
@@ -1020,6 +1108,7 @@
 
         void dumpLocked(String prefix, PrintWriter pw) {
             pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken);
+            pw.print(prefix); pw.print("mFlags: "); pw.println(mFlags);
             pw.print(prefix); pw.print("mCurrentResponse: "); pw.println(mCurrentResponse);
             pw.print(prefix); pw.print("mAutoFilledDataset: "); pw.println(mAutoFilledDataset);
             pw.print(prefix); pw.print("mCurrentViewStates: "); pw.println(mCurrentViewState);
diff --git a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
similarity index 96%
rename from services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
rename to services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index 76d9aea..80560f1 100644
--- a/services/autofill/java/com/android/server/autofill/AutoFillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -16,7 +16,7 @@
 
 package com.android.server.autofill;
 
-import static com.android.server.autofill.AutoFillManagerService.RECEIVER_BUNDLE_EXTRA_SESSIONS;
+import static com.android.server.autofill.AutofillManagerService.RECEIVER_BUNDLE_EXTRA_SESSIONS;
 
 import android.app.ActivityManager;
 import android.os.Bundle;
@@ -30,11 +30,11 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-public final class AutoFillManagerServiceShellCommand extends ShellCommand {
+public final class AutofillManagerServiceShellCommand extends ShellCommand {
 
-    private final AutoFillManagerService mService;
+    private final AutofillManagerService mService;
 
-    public AutoFillManagerServiceShellCommand(AutoFillManagerService service) {
+    public AutofillManagerServiceShellCommand(AutofillManagerService service) {
         mService = service;
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 76385b1..b1cc89b 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -132,9 +132,10 @@
         mCallbacks.onServiceDied(this);
     }
 
-    public void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle extras) {
+    public void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle extras,
+            int flags) {
         cancelScheduledUnbind();
-        final PendingFillRequest request = new PendingFillRequest(structure, extras, this);
+        final PendingFillRequest request = new PendingFillRequest(structure, extras, this, flags);
         mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, request).sendToTarget();
     }
 
@@ -182,7 +183,7 @@
             return;
         }
         if (!isBound()) {
-            if (mPendingRequest != null && mPendingRequest != pendingRequest) {
+            if (mPendingRequest != null) {
                 mPendingRequest.cancel();
             }
             mPendingRequest = pendingRequest;
@@ -240,7 +241,7 @@
         }
         mBinding = false;
         if (isBound()) {
-            // TODO(b/33197203, b/35395043): synchronize access instead
+            // TODO(b/33197203): synchronize access instead?
             // Need to double check if it's null, since it could be set on onServiceDisconnected()
             if (mAutoFillService != null) {
                 try {
@@ -322,7 +323,7 @@
             }
 
             try {
-                // TODO(b/33197203, b/35395043): synchronize access instead
+                // TODO(b/33197203): synchronize access instead?
                 // Need to double check if it's null, since it could be set on
                 // onServiceDisconnected()
                 if (mAutoFillService != null) {
@@ -337,9 +338,10 @@
                 Slog.w(LOG_TAG, "Exception calling onConnected(): " + e);
             }
 
-
             if (mPendingRequest != null) {
-                handlePendingRequest(mPendingRequest);
+                PendingRequest pendingRequest = mPendingRequest;
+                mPendingRequest = null;
+                handlePendingRequest(pendingRequest);
             }
 
             mServiceDied = false;
@@ -417,11 +419,13 @@
         private final IFillCallback mCallback;
         private ICancellationSignal mCancellation;
         private boolean mCancelled;
+        private int mFlags;
 
         public PendingFillRequest(AssistStructure structure,
-                Bundle extras, RemoteFillService service) {
+                Bundle extras, RemoteFillService service, int flags) {
             mStructure = structure;
             mExtras = extras;
+            mFlags = flags;
             mWeakService = new WeakReference<>(service);
             mCallback = new IFillCallback.Stub() {
                 @Override
@@ -468,7 +472,7 @@
             if (remoteService != null) {
                 try {
                     remoteService.mAutoFillService.onFillRequest(mStructure,
-                            mExtras, mCallback);
+                            mExtras, mCallback, mFlags);
                     synchronized (mLock) {
                         mStructure = null;
                         mExtras = null;
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 3eefa7c..c7e59a3 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -123,7 +123,7 @@
             }
             hideSaveUiUiThread();
             if (mFillUi != null) {
-                mFillUi.filter(filterText);
+                mFillUi.setFilterText(filterText);
             }
         });
     }
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 4a380c5..a7d9fe9 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -44,6 +44,8 @@
 final class FillUi {
     private static final String TAG = "FillUi";
 
+    private static final int VISIBLE_OPTIONS_MAX_COUNT = 3;
+
     interface Callback {
         void onResponsePicked(@NonNull FillResponse response);
         void onDatasetPicked(@NonNull Dataset dataset);
@@ -56,6 +58,8 @@
 
     private final @NonNull Callback mCallback;
 
+    private final @NonNull ListView mListView;
+
     private final @Nullable ArrayAdapter<ViewItem> mAdapter;
 
     private @Nullable String mFilterText;
@@ -73,6 +77,9 @@
         mCallback = callback;
 
         if (response.getAuthentication() != null) {
+            mListView = null;
+            mAdapter = null;
+
             final View content;
             try {
                 content = response.getPresentation().apply(context, null);
@@ -80,7 +87,6 @@
                 callback.onCanceled();
                 Slog.e(TAG, "Error inflating remote views", e);
                 mWindow = null;
-                mAdapter = null;
                 return;
             }
             final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
@@ -89,10 +95,9 @@
             content.setOnClickListener(v -> mCallback.onResponsePicked(response));
             mContentWidth = content.getMeasuredWidth();
             mContentHeight = content.getMeasuredHeight();
-            mAdapter = null;
 
             mWindow = new AnchoredWindow(windowToken, content);
-            mWindow.update(mContentWidth, mContentHeight, mAnchorBounds);
+            mWindow.show(mContentWidth, mContentHeight, mAnchorBounds);
         } else {
             final int datasetCount = response.getDatasets().size();
             final ArrayList<ViewItem> items = new ArrayList<>(datasetCount);
@@ -108,8 +113,13 @@
                         Slog.e(TAG, "Error inflating remote views", e);
                         continue;
                     }
-                    items.add(new ViewItem(dataset, value.coerceToString()
-                            .toLowerCase(), view));
+
+                    String valueText = null;
+                    if (value.isText()) {
+                        valueText = value.getTextValue().toString().toLowerCase();
+                    }
+
+                    items.add(new ViewItem(dataset, valueText, view));
                 }
             }
 
@@ -121,16 +131,22 @@
             };
 
             final LayoutInflater inflater = LayoutInflater.from(context);
-            final ListView listView = (ListView) inflater.inflate(
+            mListView = (ListView) inflater.inflate(
                     com.android.internal.R.layout.autofill_dataset_picker, null);
-            listView.setAdapter(mAdapter);
-            listView.setOnItemClickListener((adapter, view, position, id) -> {
+            mListView.setAdapter(mAdapter);
+            mListView.setOnItemClickListener((adapter, view, position, id) -> {
                 final ViewItem vi = mAdapter.getItem(position);
                 mCallback.onDatasetPicked(vi.getDataset());
             });
 
-            filter(filterText);
-            mWindow = new AnchoredWindow(windowToken, listView);
+            if (filterText == null) {
+                mFilterText = null;
+            } else {
+                mFilterText = filterText.toLowerCase();
+            }
+
+            applyNewFilterText();
+            mWindow = new AnchoredWindow(windowToken, mListView);
         }
     }
 
@@ -138,36 +154,54 @@
         throwIfDestroyed();
         if (!mAnchorBounds.equals(anchorBounds)) {
             mAnchorBounds.set(anchorBounds);
-            mWindow.update(mContentWidth, mContentHeight, anchorBounds);
+            mWindow.show(mContentWidth, mContentHeight, anchorBounds);
         }
     }
 
-    public void filter(@Nullable String filterText) {
-        throwIfDestroyed();
-        if (mAdapter == null) {
-            return;
-        }
-        if (Objects.equal(mFilterText, filterText)) {
-            return;
-        }
-        mFilterText = filterText;
-        mAdapter.getFilter().filter(filterText, (count) -> {
+    private void applyNewFilterText() {
+        mAdapter.getFilter().filter(mFilterText, (count) -> {
             if (mDestroyed) {
                 return;
             }
             if (count <= 0) {
-                mCallback.onCanceled();
+                mWindow.hide();
             } else {
                 if (updateContentSize()) {
-                    mWindow.update(mContentWidth, mContentHeight, mAnchorBounds);
+                    mWindow.show(mContentWidth, mContentHeight, mAnchorBounds);
+                }
+                if (mAdapter.getCount() > VISIBLE_OPTIONS_MAX_COUNT) {
+                    mListView.setVerticalScrollBarEnabled(true);
+                    mListView.onVisibilityAggregated(true);
+                } else {
+                    mListView.setVerticalScrollBarEnabled(false);
                 }
             }
         });
     }
 
+    public void setFilterText(@Nullable String filterText) {
+        throwIfDestroyed();
+        if (mAdapter == null) {
+            return;
+        }
+
+        if (filterText == null) {
+            filterText = null;
+        } else {
+            filterText = filterText.toLowerCase();
+        }
+
+        if (Objects.equal(mFilterText, filterText)) {
+            return;
+        }
+        mFilterText = filterText;
+
+        applyNewFilterText();
+    }
+
     public void destroy() {
         throwIfDestroyed();
-        mWindow.destroy();
+        mWindow.hide();
         mDestroyed = true;
     }
 
@@ -193,7 +227,7 @@
 
         final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
         final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
-        final int itemCount = mAdapter.getCount();
+        final int itemCount = Math.min(mAdapter.getCount(), VISIBLE_OPTIONS_MAX_COUNT);
         for (int i = 0; i < itemCount; i++) {
             View view = mAdapter.getItem(i).getView();
             view.measure(widthMeasureSpec, heightMeasureSpec);
@@ -224,7 +258,7 @@
 
         ViewItem(Dataset dataset, String value, View view) {
             mDataset = dataset;
-            mValue = value.toLowerCase();
+            mValue = value;
             mView = view;
         }
 
@@ -266,7 +300,7 @@
         /**
          * Hides the window.
          */
-        void destroy() {
+        void hide() {
             if (mContentView.isAttachedToWindow()) {
                 mContentView.setOnTouchListener(null);
                 mWm.removeView(mContentView);
@@ -283,7 +317,7 @@
             return false;
         }
 
-        public void update(int desiredWidth, int desiredHeight, Rect anchorBounds) {
+        public void show(int desiredWidth, int desiredHeight, Rect anchorBounds) {
             final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
             params.setTitle("FillUi");
             params.token = mActivityToken;
@@ -293,7 +327,6 @@
                     | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                     | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                     | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-            params.format = PixelFormat.TRANSLUCENT;
 
             mWm.getDefaultDisplay().getRealSize(mTempPoint);
             final int screenWidth = mTempPoint.x;
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index de11f36..a5e357c 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -235,6 +235,7 @@
     }
 
     public AppOpsService(File storagePath, Handler handler) {
+        LockGuard.installLock(this, LockGuard.INDEX_APP_OPS);
         mFile = new AtomicFile(storagePath);
         mHandler = handler;
         readState();
diff --git a/services/core/java/com/android/server/BackgroundDexOptJobService.java b/services/core/java/com/android/server/BackgroundDexOptJobService.java
deleted file mode 100644
index 69e6ac5..0000000
--- a/services/core/java/com/android/server/BackgroundDexOptJobService.java
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server;
-
-import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
-
-import android.app.job.JobInfo;
-import android.app.job.JobParameters;
-import android.app.job.JobScheduler;
-import android.app.job.JobService;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.BatteryManager;
-import android.os.Environment;
-import android.os.ServiceManager;
-import android.os.storage.StorageManager;
-import android.util.ArraySet;
-import android.util.Log;
-import com.android.server.pm.PackageManagerService;
-
-import java.io.File;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.TimeUnit;
-
-public class BackgroundDexOptJobService extends JobService {
-    private static final String TAG = "BackgroundDexOptJobService";
-
-    private static final boolean DEBUG = false;
-
-    private static final int JOB_IDLE_OPTIMIZE = 800;
-    private static final int JOB_POST_BOOT_UPDATE = 801;
-
-    private static final long IDLE_OPTIMIZATION_PERIOD = DEBUG
-            ? TimeUnit.MINUTES.toMillis(1)
-            : TimeUnit.DAYS.toMillis(1);
-
-    private static ComponentName sDexoptServiceName = new ComponentName(
-            "android",
-            BackgroundDexOptJobService.class.getName());
-
-    /**
-     * Set of failed packages remembered across job runs.
-     */
-    static final ArraySet<String> sFailedPackageNames = new ArraySet<String>();
-
-    /**
-     * Atomics set to true if the JobScheduler requests an abort.
-     */
-    final AtomicBoolean mAbortPostBootUpdate = new AtomicBoolean(false);
-    final AtomicBoolean mAbortIdleOptimization = new AtomicBoolean(false);
-
-    /**
-     * Atomic set to true if one job should exit early because another job was started.
-     */
-    final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false);
-
-    private final File mDataDir = Environment.getDataDirectory();
-
-    public static void schedule(Context context) {
-        JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
-
-        // Schedule a one-off job which scans installed packages and updates
-        // out-of-date oat files.
-        js.schedule(new JobInfo.Builder(JOB_POST_BOOT_UPDATE, sDexoptServiceName)
-                    .setMinimumLatency(TimeUnit.MINUTES.toMillis(1))
-                    .setOverrideDeadline(TimeUnit.MINUTES.toMillis(1))
-                    .build());
-
-        // Schedule a daily job which scans installed packages and compiles
-        // those with fresh profiling data.
-        js.schedule(new JobInfo.Builder(JOB_IDLE_OPTIMIZE, sDexoptServiceName)
-                    .setRequiresDeviceIdle(true)
-                    .setRequiresCharging(true)
-                    .setPeriodic(IDLE_OPTIMIZATION_PERIOD)
-                    .build());
-
-        if (DEBUG_DEXOPT) {
-            Log.i(TAG, "Jobs scheduled");
-        }
-    }
-
-    public static void notifyPackageChanged(String packageName) {
-        // The idle maintanance job skips packages which previously failed to
-        // compile. The given package has changed and may successfully compile
-        // now. Remove it from the list of known failing packages.
-        synchronized (sFailedPackageNames) {
-            sFailedPackageNames.remove(packageName);
-        }
-    }
-
-    // Returns the current battery level as a 0-100 integer.
-    private int getBatteryLevel() {
-        IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
-        Intent intent = registerReceiver(null, filter);
-        int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
-        int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
-
-        if (level < 0 || scale <= 0) {
-            // Battery data unavailable. This should never happen, so assume the worst.
-            return 0;
-        }
-
-        return (100 * level / scale);
-    }
-
-    private long getLowStorageThreshold() {
-        @SuppressWarnings("deprecation")
-        final long lowThreshold = StorageManager.from(this).getStorageLowBytes(mDataDir);
-        if (lowThreshold == 0) {
-            Log.e(TAG, "Invalid low storage threshold");
-        }
-
-        return lowThreshold;
-    }
-
-    private boolean runPostBootUpdate(final JobParameters jobParams,
-            final PackageManagerService pm, final ArraySet<String> pkgs) {
-        if (mExitPostBootUpdate.get()) {
-            // This job has already been superseded. Do not start it.
-            return false;
-        }
-        new Thread("BackgroundDexOptService_PostBootUpdate") {
-            @Override
-            public void run() {
-                postBootUpdate(jobParams, pm, pkgs);
-            }
-
-        }.start();
-        return true;
-    }
-
-    private void postBootUpdate(JobParameters jobParams, PackageManagerService pm,
-            ArraySet<String> pkgs) {
-        // Load low battery threshold from the system config. This is a 0-100 integer.
-        final int lowBatteryThreshold = getResources().getInteger(
-                com.android.internal.R.integer.config_lowBatteryWarningLevel);
-        final long lowThreshold = getLowStorageThreshold();
-
-        mAbortPostBootUpdate.set(false);
-
-        for (String pkg : pkgs) {
-            if (mAbortPostBootUpdate.get()) {
-                // JobScheduler requested an early abort.
-                return;
-            }
-            if (mExitPostBootUpdate.get()) {
-                // Different job, which supersedes this one, is running.
-                break;
-            }
-            if (getBatteryLevel() < lowBatteryThreshold) {
-                // Rather bail than completely drain the battery.
-                break;
-            }
-            long usableSpace = mDataDir.getUsableSpace();
-            if (usableSpace < lowThreshold) {
-                // Rather bail than completely fill up the disk.
-                Log.w(TAG, "Aborting background dex opt job due to low storage: " +
-                        usableSpace);
-                break;
-            }
-
-            if (DEBUG_DEXOPT) {
-                Log.i(TAG, "Updating package " + pkg);
-            }
-
-            // Update package if needed. Note that there can be no race between concurrent
-            // jobs because PackageDexOptimizer.performDexOpt is synchronized.
-
-            // checkProfiles is false to avoid merging profiles during boot which
-            // might interfere with background compilation (b/28612421).
-            // Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
-            // behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
-            // trade-off worth doing to save boot time work.
-            pm.performDexOpt(pkg,
-                    /* checkProfiles */ false,
-                    PackageManagerService.REASON_BOOT,
-                    /* force */ false);
-        }
-        // Ran to completion, so we abandon our timeslice and do not reschedule.
-        jobFinished(jobParams, /* reschedule */ false);
-    }
-
-    private boolean runIdleOptimization(final JobParameters jobParams,
-            final PackageManagerService pm, final ArraySet<String> pkgs) {
-        new Thread("BackgroundDexOptService_IdleOptimization") {
-            @Override
-            public void run() {
-                idleOptimization(jobParams, pm, pkgs);
-            }
-        }.start();
-        return true;
-    }
-
-    private void idleOptimization(JobParameters jobParams, PackageManagerService pm,
-            ArraySet<String> pkgs) {
-        Log.i(TAG, "Performing idle optimizations");
-        // If post-boot update is still running, request that it exits early.
-        mExitPostBootUpdate.set(true);
-
-        mAbortIdleOptimization.set(false);
-
-        final long lowThreshold = getLowStorageThreshold();
-        for (String pkg : pkgs) {
-            if (mAbortIdleOptimization.get()) {
-                // JobScheduler requested an early abort.
-                return;
-            }
-
-            synchronized (sFailedPackageNames) {
-                if (sFailedPackageNames.contains(pkg)) {
-                    // Skip previously failing package
-                    continue;
-                }
-            }
-
-            long usableSpace = mDataDir.getUsableSpace();
-            if (usableSpace < lowThreshold) {
-                // Rather bail than completely fill up the disk.
-                Log.w(TAG, "Aborting background dex opt job due to low storage: " +
-                        usableSpace);
-                break;
-            }
-
-            // Conservatively add package to the list of failing ones in case performDexOpt
-            // never returns.
-            synchronized (sFailedPackageNames) {
-                sFailedPackageNames.add(pkg);
-            }
-            // Optimize package if needed. Note that there can be no race between
-            // concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
-            if (pm.performDexOpt(pkg,
-                    /* checkProfiles */ true,
-                    PackageManagerService.REASON_BACKGROUND_DEXOPT,
-                    /* force */ false)) {
-                // Dexopt succeeded, remove package from the list of failing ones.
-                synchronized (sFailedPackageNames) {
-                    sFailedPackageNames.remove(pkg);
-                }
-            }
-        }
-        // Ran to completion, so we abandon our timeslice and do not reschedule.
-        jobFinished(jobParams, /* reschedule */ false);
-    }
-
-    @Override
-    public boolean onStartJob(JobParameters params) {
-        if (DEBUG_DEXOPT) {
-            Log.i(TAG, "onStartJob");
-        }
-
-        // NOTE: PackageManagerService.isStorageLow uses a different set of criteria from
-        // the checks above. This check is not "live" - the value is determined by a background
-        // restart with a period of ~1 minute.
-        PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
-        if (pm.isStorageLow()) {
-            if (DEBUG_DEXOPT) {
-                Log.i(TAG, "Low storage, skipping this run");
-            }
-            return false;
-        }
-
-        final ArraySet<String> pkgs = pm.getOptimizablePackages();
-        if (pkgs == null || pkgs.isEmpty()) {
-            if (DEBUG_DEXOPT) {
-                Log.i(TAG, "No packages to optimize");
-            }
-            return false;
-        }
-
-        if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
-            return runPostBootUpdate(params, pm, pkgs);
-        } else {
-            return runIdleOptimization(params, pm, pkgs);
-        }
-    }
-
-    @Override
-    public boolean onStopJob(JobParameters params) {
-        if (DEBUG_DEXOPT) {
-            Log.i(TAG, "onStopJob");
-        }
-
-        if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
-            mAbortPostBootUpdate.set(true);
-        } else {
-            mAbortIdleOptimization.set(true);
-        }
-        return false;
-    }
-}
diff --git a/services/core/java/com/android/server/FontManagerService.java b/services/core/java/com/android/server/FontManagerService.java
index 593c322..55a945a 100644
--- a/services/core/java/com/android/server/FontManagerService.java
+++ b/services/core/java/com/android/server/FontManagerService.java
@@ -71,11 +71,8 @@
                 return null;
             }
 
-            final int size = config.getFamilies().size();
-            for (int i = 0; i < size; ++i) {
-                FontConfig.Family family = config.getFamilies().get(i);
-                for (int j = 0; j < family.getFonts().size(); ++j) {
-                    FontConfig.Font font = family.getFonts().get(j);
+            for (FontConfig.Family family : config.getFamilies()) {
+                for (FontConfig.Font font : family.getFonts()) {
                     File fontFile = new File(font.getFontName());
                     try {
                         font.setFd(ParcelFileDescriptor.open(
diff --git a/services/core/java/com/android/server/LockGuard.java b/services/core/java/com/android/server/LockGuard.java
index 3a381ae..b744917 100644
--- a/services/core/java/com/android/server/LockGuard.java
+++ b/services/core/java/com/android/server/LockGuard.java
@@ -53,10 +53,29 @@
  * <li>A guarded synchronized block takes 50ns when disabled.
  * <li>A guarded synchronized block takes 460ns per lock checked when enabled.
  * </ul>
+ * <p>
+ * This class also supports a second simpler mode of operation where well-known
+ * locks are explicitly registered and checked via indexes.
  */
 public class LockGuard {
     private static final String TAG = "LockGuard";
 
+    public static final boolean ENABLED = false;
+
+    /**
+     * Well-known locks ordered by fixed index. Locks with a specific index
+     * should never be acquired while holding a lock of a lower index.
+     */
+    public static final int INDEX_APP_OPS = 0;
+    public static final int INDEX_POWER = 1;
+    public static final int INDEX_USER = 2;
+    public static final int INDEX_PACKAGES = 3;
+    public static final int INDEX_STORAGE = 4;
+    public static final int INDEX_WINDOW = 5;
+    public static final int INDEX_ACTIVITY = 6;
+
+    private static Object[] sKnownFixed = new Object[INDEX_ACTIVITY + 1];
+
     private static ArrayMap<Object, LockInfo> sKnown = new ArrayMap<>(0, true);
 
     private static class LockInfo {
@@ -119,11 +138,41 @@
     }
 
     /**
+     * Yell if any lower-level locks are being held by the calling thread that
+     * is about to acquire the given lock.
+     */
+    public static void guard(int index) {
+        for (int i = 0; i < index; i++) {
+            final Object lock = sKnownFixed[i];
+            if (lock != null && Thread.holdsLock(lock)) {
+                Slog.w(TAG, "Calling thread " + Thread.currentThread().getName() + " is holding "
+                        + lockToString(i) + " while trying to acquire "
+                        + lockToString(index), new Throwable());
+            }
+        }
+    }
+
+    /**
      * Report the given lock with a well-known label.
      */
-    public static void installLock(Object lock, String label) {
+    public static Object installLock(Object lock, String label) {
         final LockInfo info = findOrCreateLockInfo(lock);
         info.label = label;
+        return lock;
+    }
+
+    /**
+     * Report the given lock with a well-known index.
+     */
+    public static Object installLock(Object lock, int index) {
+        sKnownFixed[index] = lock;
+        return lock;
+    }
+
+    public static Object installNewLock(int index) {
+        final Object lock = new Object();
+        installLock(lock, index);
+        return lock;
     }
 
     private static String lockToString(Object lock) {
@@ -135,6 +184,19 @@
         }
     }
 
+    private static String lockToString(int index) {
+        switch (index) {
+            case INDEX_APP_OPS: return "APP_OPS";
+            case INDEX_POWER: return "POWER";
+            case INDEX_USER: return "USER";
+            case INDEX_PACKAGES: return "PACKAGES";
+            case INDEX_STORAGE: return "STORAGE";
+            case INDEX_WINDOW: return "WINDOW";
+            case INDEX_ACTIVITY: return "ACTIVITY";
+            default: return Integer.toString(index);
+        }
+    }
+
     public static void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         for (int i = 0; i < sKnown.size(); i++) {
             final Object lock = sKnown.keyAt(i);
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index bcee2c1b..34c73d2 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -146,6 +146,7 @@
     private final LockPatternUtils mLockPatternUtils;
     private final NotificationManager mNotificationManager;
     private final UserManager mUserManager;
+    private final DevicePolicyManager mDevicePolicyManager;
     private final IActivityManager mActivityManager;
 
     private final KeyStore mKeyStore;
@@ -333,6 +334,10 @@
             return (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         }
 
+        public DevicePolicyManager getDevicePolicyManager() {
+            return (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        }
+
         public KeyStore getKeyStore() {
             return KeyStore.getInstance();
         }
@@ -380,6 +385,7 @@
         mStorage = injector.getStorage();
         mNotificationManager = injector.getNotificationManager();
         mUserManager = injector.getUserManager();
+        mDevicePolicyManager = injector.getDevicePolicyManager();
         mStrongAuthTracker = injector.getStrongAuthTracker();
         mStrongAuthTracker.register(mStrongAuth);
 
@@ -2015,14 +2021,17 @@
             }
         }
         long handle = getSyntheticPasswordHandleLocked(userId);
-        AuthenticationToken auth = mSpManager.unwrapPasswordBasedSyntheticPassword(
-                getGateKeeperService(), handle, savedCredential, userId).authToken;
+        AuthenticationResult authResult = mSpManager.unwrapPasswordBasedSyntheticPassword(
+                getGateKeeperService(), handle, savedCredential, userId);
+        VerifyCredentialResponse response = authResult.gkResponse;
+        AuthenticationToken auth = authResult.authToken;
         if (auth != null) {
             // We are performing a trusted credential change i.e. a correct existing credential
             // is provided
             setLockCredentialWithAuthTokenLocked(credential, credentialType, auth, userId);
             mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId);
-        } else {
+        } else if (response != null
+                && response.getResponseCode() == VerifyCredentialResponse.RESPONSE_ERROR){
             // We are performing an untrusted credential change i.e. by DevicePolicyManager.
             // So provision a new SP and SID. This would invalidate existing escrow tokens.
             // Still support this for now but this flow will be removed in the next release.
@@ -2031,6 +2040,10 @@
             initializeSyntheticPasswordLocked(null, credential, credentialType, userId);
             synchronizeUnifiedWorkChallengeForProfiles(userId, null);
             mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId);
+        } else /* response == null || responseCode == VerifyCredentialResponse.RESPONSE_RETRY */ {
+            Slog.w(TAG, "spBasedSetLockCredentialInternalLocked: " +
+                    (response != null ? "rate limit exceeded" : "failed"));
+            return;
         }
         notifyActivePasswordMetricsAvailable(credential, userId);
 
@@ -2042,7 +2055,7 @@
         if (DEBUG) Slog.d(TAG, "addEscrowToken: user=" + userId);
         synchronized (mSpManager) {
             enableSyntheticPasswordLocked();
-            // Migrate to synthetic password based credentials if ther user has no password,
+            // Migrate to synthetic password based credentials if the user has no password,
             // the token can then be activated immediately.
             AuthenticationToken auth = null;
             if (!isUserSecure(userId)) {
@@ -2201,22 +2214,20 @@
                 Slog.i(TAG, "Managed profile can have escrow token");
                 return;
             }
-            DevicePolicyManager dpm = (DevicePolicyManager)
-                    mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
             // Devices with Device Owner should have escrow enabled on all users.
-            if (dpm.getDeviceOwnerComponentOnAnyUser() != null) {
+            if (mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser() != null) {
                 Slog.i(TAG, "Corp-owned device can have escrow token");
                 return;
             }
             // We could also have a profile owner on the given (non-managed) user for unicorn cases
-            if (dpm.getProfileOwnerAsUser(userId) != null) {
+            if (mDevicePolicyManager.getProfileOwnerAsUser(userId) != null) {
                 Slog.i(TAG, "User with profile owner can have escrow token");
                 return;
             }
             // If the device is yet to be provisioned (still in SUW), there is still
             // a chance that Device Owner will be set on the device later, so postpone
             // disabling escrow token for now.
-            if (!dpm.isDeviceProvisioned()) {
+            if (!mDevicePolicyManager.isDeviceProvisioned()) {
                 Slog.i(TAG, "Postpone disabling escrow tokens until device is provisioned");
                 return;
             }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 1f544a0..3667ecd 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -308,7 +308,7 @@
      * <em>Never</em> hold the lock while performing downcalls into vold, since
      * unsolicited events can suddenly appear to update data structures.
      */
-    private final Object mLock = new Object();
+    private final Object mLock = LockGuard.installNewLock(LockGuard.INDEX_STORAGE);
 
     /** Set of users that we know are unlocked. */
     @GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/SyntheticPasswordManager.java b/services/core/java/com/android/server/SyntheticPasswordManager.java
index 6267880..2517613 100644
--- a/services/core/java/com/android/server/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/SyntheticPasswordManager.java
@@ -526,7 +526,7 @@
      * RESPONSE_OK, since user authentication failures are detected earlier when trying to
      * decrypt SP.
      */
-    public VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper,
+    public @Nullable VerifyCredentialResponse verifyChallenge(IGateKeeperService gatekeeper,
             @NonNull AuthenticationToken auth, long challenge, int userId) throws RemoteException {
         byte[] spHandle = loadSyntheticPasswordHandle(userId);
         if (spHandle == null) {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 0d5a3e0..accae0d 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -4003,12 +4003,12 @@
     public Account[] getAccountsAsUser(String type, int userId, String opPackageName) {
         int callingUid = Binder.getCallingUid();
         mAppOpsManager.checkPackage(callingUid, opPackageName);
-        return getAccountsAsUser(type, userId, opPackageName /* callingPackage */, -1,
+        return getAccountsAsUserForPackage(type, userId, opPackageName /* callingPackage */, -1,
                 opPackageName, false /* includeUserManagedNotVisible */);
     }
 
     @NonNull
-    private Account[] getAccountsAsUser(
+    private Account[] getAccountsAsUserForPackage(
             String type,
             int userId,
             String callingPackage,
@@ -4061,7 +4061,7 @@
             return getAccountsInternal(
                     accounts,
                     callingUid,
-                    callingPackage,
+                    opPackageName,
                     visibleAccountTypes,
                     includeUserManagedNotVisible);
         } finally {
@@ -4178,7 +4178,7 @@
             throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
                     + callingUid + " with uid=" + uid);
         }
-        return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid,
+        return getAccountsAsUserForPackage(null, UserHandle.getCallingUserId(), packageName, uid,
                 opPackageName, true /* includeUserManagedNotVisible */);
     }
 
@@ -4197,11 +4197,10 @@
             return EMPTY_ACCOUNT_ARRAY;
         }
         if (!UserHandle.isSameApp(callingUid, Process.SYSTEM_UID)
-                && !isAccountManagedByCaller(type, callingUid, userId)) {
-            return EMPTY_ACCOUNT_ARRAY;
+                && (type != null && !isAccountManagedByCaller(type, callingUid, userId))) {
+                return EMPTY_ACCOUNT_ARRAY;
         }
-
-        return getAccountsAsUser(type, userId,
+        return getAccountsAsUserForPackage(type, userId,
                 packageName, packageUid, opPackageName, true /* includeUserManagedNotVisible */);
     }
 
@@ -4363,7 +4362,7 @@
          * security policy.
          *
          * In particular we want to make sure that the Authenticator doesn't try to trick users
-         * into launching aribtrary intents on the device via by tricking to click authenticator
+         * into launching arbitrary intents on the device via by tricking to click authenticator
          * supplied entries in the system Settings app.
          */
         protected void checkKeyIntent(
@@ -4375,12 +4374,9 @@
                 ResolveInfo resolveInfo = pm.resolveActivityAsUser(intent, 0, mAccounts.userId);
                 ActivityInfo targetActivityInfo = resolveInfo.activityInfo;
                 int targetUid = targetActivityInfo.applicationInfo.uid;
-                if (!GrantCredentialsPermissionActivity.class.getName().equals(
-                        targetActivityInfo.getClass().getName())
-                        && !CantAddAccountActivity.class
-                                .equals(targetActivityInfo.getClass().getName())
-                        && PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid,
-                                targetUid)) {
+                if (!isExportedSystemActivity(targetActivityInfo)
+                        && (PackageManager.SIGNATURE_MATCH != pm.checkSignatures(authUid,
+                                targetUid))) {
                     String pkgName = targetActivityInfo.packageName;
                     String activityName = targetActivityInfo.name;
                     String tmpl = "KEY_INTENT resolved to an Activity (%s) in a package (%s) that "
@@ -4393,6 +4389,13 @@
             }
         }
 
+        private boolean isExportedSystemActivity(ActivityInfo activityInfo) {
+            String className = activityInfo.name;
+            return "android".equals(activityInfo.packageName) &&
+                    (GrantCredentialsPermissionActivity.class.getName().equals(className)
+                    || CantAddAccountActivity.class.getName().equals(className));
+        }
+
         private void close() {
             synchronized (mSessions) {
                 if (mSessions.remove(toString()) == null) {
@@ -5371,10 +5374,13 @@
     @NonNull
     private Account[] filterAccounts(UserAccounts accounts, Account[] unfiltered, int callingUid,
             String callingPackage, boolean includeManagedNotVisible) {
-        // filter based on visibility.
+        String visibilityFilterPackage = callingPackage;
+        if (visibilityFilterPackage == null) {
+            visibilityFilterPackage = getPackageNameForUid(callingUid);
+        }
         Map<Account, Integer> firstPass = new LinkedHashMap<>();
         for (Account account : unfiltered) {
-            int visibility = resolveAccountVisibility(account, callingPackage, accounts);
+            int visibility = resolveAccountVisibility(account, visibilityFilterPackage, accounts);
             if ((visibility == AccountManager.VISIBILITY_VISIBLE
                     || visibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE)
                     || (includeManagedNotVisible
@@ -5394,7 +5400,7 @@
     @NonNull
     private Map<Account, Integer> filterSharedAccounts(UserAccounts userAccounts,
             @NonNull Map<Account, Integer> unfiltered, int callingUid,
-            String callingPackage) {
+            @Nullable String callingPackage) {
         // first part is to filter shared accounts.
         // unfiltered type check is not necessary.
         if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
@@ -5474,7 +5480,7 @@
      */
     @NonNull
     protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
-            int callingUid, String callingPackage, boolean includeManagedNotVisible) {
+            int callingUid, @Nullable String callingPackage, boolean includeManagedNotVisible) {
         if (callingPackage == null) {
             callingPackage = getPackageNameForUid(callingUid);
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5a6d9aa..55d661c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -691,6 +691,9 @@
             Process.setThreadPriority(tid, -2);
         }
         state.regionCounter++;
+        if (LockGuard.ENABLED) {
+            LockGuard.guard(LockGuard.INDEX_ACTIVITY);
+        }
     }
 
     static void resetPriorityAfterLockedSection() {
@@ -2657,6 +2660,7 @@
     // Note: This method is invoked on the main thread but may need to attach various
     // handlers to other threads.  So take care to be explicit about the looper.
     public ActivityManagerService(Context systemContext) {
+        LockGuard.installLock(this, LockGuard.INDEX_ACTIVITY);
         mContext = systemContext;
         mFactoryTest = FactoryTest.getMode();
         mSystemThread = ActivityThread.currentActivityThread();
@@ -6687,16 +6691,15 @@
                     : new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop,
                                        profileStreamingOutput);
 
-            // We deprecated Build.SERIAL and only apps that target pre NMR1
-            // SDK can see it. Since access to the serial is now behind a
-            // permission we push down the value.
+            // We deprecated Build.SERIAL and it is not accessible to
+            // apps that target the v2 security sandbox. Since access to
+            // the serial is now behind a permission we push down the value.
             String buildSerial = Build.UNKNOWN;
-            // TODO: SHTOPSHIP Uncomment the check when clients migrate
-//            if (appInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
+            if (appInfo.targetSandboxVersion != 2) {
                 buildSerial = IDeviceIdentifiersPolicyService.Stub.asInterface(
                         ServiceManager.getService(Context.DEVICE_IDENTIFIERS_SERVICE))
                         .getSerial();
-//            }
+            }
 
             // Check if this is a secondary process that should be incorporated into some
             // currently active instrumentation.  (Note we do this AFTER all of the profiling
@@ -8533,13 +8536,21 @@
 
         // Third...  does the caller itself have permission to access
         // this uri?
-        if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) {
-            if (!checkHoldingPermissionsLocked(pm, pi, grantUri, callingUid, modeFlags)) {
-                // Require they hold a strong enough Uri permission
-                if (!checkUriPermissionLocked(grantUri, callingUid, modeFlags)) {
-                    throw new SecurityException("Uid " + callingUid
-                            + " does not have permission to uri " + grantUri);
-                }
+        final int callingAppId = UserHandle.getAppId(callingUid);
+        if ((callingAppId == Process.SYSTEM_UID) || (callingAppId == Process.ROOT_UID)) {
+            if ("com.android.settings.files".equals(grantUri.uri.getAuthority())) {
+                // Exempted authority for cropping user photos in Settings app
+            } else {
+                Slog.w(TAG, "For security reasons, the system cannot issue a Uri permission"
+                        + " grant to " + grantUri + "; use startActivityAsCaller() instead");
+                return -1;
+            }
+        }
+        if (!checkHoldingPermissionsLocked(pm, pi, grantUri, callingUid, modeFlags)) {
+            // Require they hold a strong enough Uri permission
+            if (!checkUriPermissionLocked(grantUri, callingUid, modeFlags)) {
+                throw new SecurityException("Uid " + callingUid
+                        + " does not have permission to uri " + grantUri);
             }
         }
         return targetUid;
@@ -22945,16 +22956,9 @@
         }
 
         @Override
-        public void notifyStartingWindowDrawn() {
+        public void notifyAppTransitionStarting(SparseIntArray reasons) {
             synchronized (ActivityManagerService.this) {
-                mStackSupervisor.mActivityMetricsLogger.notifyStartingWindowDrawn();
-            }
-        }
-
-        @Override
-        public void notifyAppTransitionStarting(int reason) {
-            synchronized (ActivityManagerService.this) {
-                mStackSupervisor.mActivityMetricsLogger.notifyTransitionStarting(reason);
+                mStackSupervisor.mActivityMetricsLogger.notifyTransitionStarting(reasons);
             }
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index ff796a54..918747b 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -1,24 +1,36 @@
 package com.android.server.am;
 
+import static android.app.ActivityManager.START_SUCCESS;
+import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.ActivityManager.StackId.ASSISTANT_STACK_ID;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-
+import static android.app.ActivityManagerInternal.APP_TRANSITION_TIMEOUT;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_ACTIVITY_NAME;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DELAY_MS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_STARTING_WINDOW_DELAY_MS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_COLD_LAUNCH;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_HOT_LAUNCH;
+import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityStack.STACK_INVISIBLE;
 
-import android.annotation.Nullable;
 import android.app.ActivityManager.StackId;
 import android.content.Context;
+import android.metrics.LogMaker;
 import android.os.SystemClock;
 import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
 
-import android.metrics.LogMaker;
 import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
 import java.util.ArrayList;
 
@@ -48,12 +60,27 @@
     private long mLastLogTimeSecs;
     private final ActivityStackSupervisor mSupervisor;
     private final Context mContext;
+    private final MetricsLogger mMetricsLogger = new MetricsLogger();
 
     private long mCurrentTransitionStartTime = INVALID_START_TIME;
-    private boolean mLoggedWindowsDrawn;
-    private boolean mLoggedStartingWindowDrawn;
+
+    private int mCurrentTransitionDeviceUptime;
+    private int mCurrentTransitionDelayMs;
     private boolean mLoggedTransitionStarting;
 
+    private final SparseArray<StackTransitionInfo> mStackTransitionInfo = new SparseArray<>();
+
+    private final class StackTransitionInfo {
+        private ActivityRecord launchedActivity;
+        private int startResult;
+        private boolean currentTransitionProcessRunning;
+        private int windowsDrawnDelayMs;
+        private int startingWindowDelayMs;
+        private int reason = APP_TRANSITION_TIMEOUT;
+        private boolean loggedWindowsDrawn;
+        private boolean loggedStartingWindowDrawn;
+    }
+
     ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context) {
         mLastLogTimeSecs = SystemClock.elapsedRealtime() / 1000;
         mSupervisor = supervisor;
@@ -102,7 +129,9 @@
      * activity.
      */
     void notifyActivityLaunching() {
-        mCurrentTransitionStartTime = System.currentTimeMillis();
+        if (!isAnyTransitionActive()) {
+            mCurrentTransitionStartTime = System.currentTimeMillis();
+        }
     }
 
     /**
@@ -118,9 +147,6 @@
                         launchedActivity.appInfo.uid)
                 : null;
         final boolean processRunning = processRecord != null;
-        final String componentName = launchedActivity != null
-                ? launchedActivity.shortComponentName
-                : null;
 
         // We consider this a "process switch" if the process of the activity that gets launched
         // didn't have an activity that was in started state. In this case, we assume that lot
@@ -129,7 +155,7 @@
         final boolean processSwitch = processRecord == null
                 || !hasStartedActivity(processRecord, launchedActivity);
 
-        notifyActivityLaunched(resultCode, componentName, processRunning, processSwitch);
+        notifyActivityLaunched(resultCode, launchedActivity, processRunning, processSwitch);
     }
 
     private boolean hasStartedActivity(ProcessRecord record, ActivityRecord launchedActivity) {
@@ -151,92 +177,121 @@
      *
      * @param resultCode one of the ActivityManager.START_* flags, indicating the result of the
      *                   launch
-     * @param componentName the component name of the activity being launched
+     * @param launchedActivity the activity being launched
      * @param processRunning whether the process that will contains the activity is already running
      * @param processSwitch whether the process that will contain the activity didn't have any
      *                      activity that was stopped, i.e. the started activity is "switching"
      *                      processes
      */
-    private void notifyActivityLaunched(int resultCode, @Nullable String componentName,
+    private void notifyActivityLaunched(int resultCode, ActivityRecord launchedActivity,
             boolean processRunning, boolean processSwitch) {
 
-        if (resultCode < 0 || componentName == null || !processSwitch) {
-
-            // Failed to launch or it was not a process switch, so we don't care about the timing.
-            reset();
+        // If we are already in an existing transition, only update the activity name, but not the
+        // other attributes.
+        final int stackId = launchedActivity != null && launchedActivity.getStack() != null
+                ? launchedActivity.getStack().mStackId
+                : INVALID_STACK_ID;
+        final StackTransitionInfo info = mStackTransitionInfo.get(stackId);
+        if (launchedActivity != null && info != null) {
+            info.launchedActivity = launchedActivity;
             return;
         }
 
-        MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_COMPONENT_NAME,
-                componentName);
-        MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_PROCESS_RUNNING,
-                processRunning);
-        MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS,
-                (int) (SystemClock.uptimeMillis() / 1000));
+        final boolean otherStacksLaunching = mStackTransitionInfo.size() > 0 && info == null;
+        if ((resultCode < 0 || launchedActivity == null || !processSwitch
+                || stackId == INVALID_STACK_ID) && !otherStacksLaunching) {
 
-        LogMaker builder = new LogMaker(MetricsEvent.APP_TRANSITION);
-        builder.addTaggedData(MetricsEvent.APP_TRANSITION_COMPONENT_NAME, componentName);
-        builder.addTaggedData(MetricsEvent.APP_TRANSITION_PROCESS_RUNNING, processRunning ? 1 : 0);
-        builder.addTaggedData(MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS,
-                SystemClock.uptimeMillis() / 1000);
-        MetricsLogger.action(builder);
+            // Failed to launch or it was not a process switch, so we don't care about the timing.
+            reset(true /* abort */);
+            return;
+        } else if (otherStacksLaunching) {
+            // Don't log this stack but continue with the other stacks.
+            return;
+        }
+
+        final StackTransitionInfo newInfo = new StackTransitionInfo();
+        newInfo.launchedActivity = launchedActivity;
+        newInfo.currentTransitionProcessRunning = processRunning;
+        newInfo.startResult = resultCode;
+        mStackTransitionInfo.append(stackId, newInfo);
+        mCurrentTransitionDeviceUptime = (int) (SystemClock.uptimeMillis() / 1000);
     }
 
     /**
      * Notifies the tracker that all windows of the app have been drawn.
      */
-    void notifyWindowsDrawn() {
-        if (!isTransitionActive() || mLoggedWindowsDrawn) {
+    void notifyWindowsDrawn(int stackId) {
+        final StackTransitionInfo info = mStackTransitionInfo.get(stackId);
+        if (info == null || info.loggedWindowsDrawn) {
             return;
         }
-        MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS,
-                calculateCurrentDelay());
-        mLoggedWindowsDrawn = true;
-        if (mLoggedTransitionStarting) {
-            reset();
+        info.windowsDrawnDelayMs = calculateCurrentDelay();
+        info.loggedWindowsDrawn = true;
+        if (allStacksWindowsDrawn() && mLoggedTransitionStarting) {
+            reset(false /* abort */);
         }
     }
 
     /**
      * Notifies the tracker that the starting window was drawn.
      */
-    void notifyStartingWindowDrawn() {
-        if (!isTransitionActive() || mLoggedStartingWindowDrawn) {
+    void notifyStartingWindowDrawn(int stackId) {
+        final StackTransitionInfo info = mStackTransitionInfo.get(stackId);
+        if (info == null || info.loggedStartingWindowDrawn) {
             return;
         }
-        mLoggedStartingWindowDrawn = true;
-        MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_STARTING_WINDOW_DELAY_MS,
-                calculateCurrentDelay());
+        info.loggedStartingWindowDrawn = true;
+        info.startingWindowDelayMs = calculateCurrentDelay();
     }
 
     /**
      * Notifies the tracker that the app transition is starting.
      *
-     * @param reason The reason why we started it. Must be on of
-     *               ActivityManagerInternal.APP_TRANSITION_* reasons.
+     * @param stackIdReasons A map from stack id to a reason integer, which must be on of
+     *                       ActivityManagerInternal.APP_TRANSITION_* reasons.
      */
-    void notifyTransitionStarting(int reason) {
-        if (!isTransitionActive() || mLoggedTransitionStarting) {
+    void notifyTransitionStarting(SparseIntArray stackIdReasons) {
+        // TODO (b/36339388): Figure out why stackIdReasons can be null
+        if (stackIdReasons == null || !isAnyTransitionActive() || mLoggedTransitionStarting) {
             return;
         }
-        MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_REASON, reason);
-        MetricsLogger.action(mContext, MetricsEvent.APP_TRANSITION_DELAY_MS,
-                calculateCurrentDelay());
+        mCurrentTransitionDelayMs = calculateCurrentDelay();
         mLoggedTransitionStarting = true;
-        if (mLoggedWindowsDrawn) {
-            reset();
+        for (int index = stackIdReasons.size() - 1; index >= 0; index--) {
+            final int stackId = stackIdReasons.keyAt(index);
+            final StackTransitionInfo info = mStackTransitionInfo.get(stackId);
+            if (info == null) {
+                continue;
+            }
+            info.reason = stackIdReasons.valueAt(index);
+        }
+        if (allStacksWindowsDrawn()) {
+            reset(false /* abort */);
         }
     }
 
-    private boolean isTransitionActive() {
-        return mCurrentTransitionStartTime != INVALID_START_TIME;
+    private boolean allStacksWindowsDrawn() {
+        for (int index = mStackTransitionInfo.size() - 1; index >= 0; index--) {
+            if (!mStackTransitionInfo.valueAt(index).loggedWindowsDrawn) {
+                return false;
+            }
+        }
+        return true;
     }
 
-    private void reset() {
+    private boolean isAnyTransitionActive() {
+        return mCurrentTransitionStartTime != INVALID_START_TIME
+                && mStackTransitionInfo.size() > 0;
+    }
+
+    private void reset(boolean abort) {
+        if (!abort && isAnyTransitionActive()) {
+            logAppTransitionMultiEvents();
+        }
         mCurrentTransitionStartTime = INVALID_START_TIME;
-        mLoggedWindowsDrawn = false;
+        mCurrentTransitionDelayMs = -1;
         mLoggedTransitionStarting = false;
-        mLoggedStartingWindowDrawn = false;
+        mStackTransitionInfo.clear();
     }
 
     private int calculateCurrentDelay() {
@@ -244,4 +299,41 @@
         // Shouldn't take more than 25 days to launch an app, so int is fine here.
         return (int) (System.currentTimeMillis() - mCurrentTransitionStartTime);
     }
+
+    private void logAppTransitionMultiEvents() {
+        for (int index = mStackTransitionInfo.size() - 1; index >= 0; index--) {
+            final StackTransitionInfo info = mStackTransitionInfo.valueAt(index);
+            final int type = getTransitionType(info);
+            if (type == -1) {
+                return;
+            }
+            final LogMaker builder = new LogMaker(APP_TRANSITION);
+            builder.setPackageName(info.launchedActivity.packageName);
+            builder.addTaggedData(APP_TRANSITION_ACTIVITY_NAME, info.launchedActivity.info.name);
+            builder.setType(type);
+            builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS,
+                    mCurrentTransitionDeviceUptime);
+            builder.addTaggedData(APP_TRANSITION_DELAY_MS, mCurrentTransitionDelayMs);
+            builder.setSubtype(info.reason);
+            if (info.startingWindowDelayMs != -1) {
+                builder.addTaggedData(APP_TRANSITION_STARTING_WINDOW_DELAY_MS,
+                        info.startingWindowDelayMs);
+            }
+            builder.addTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, info.windowsDrawnDelayMs);
+            mMetricsLogger.write(builder);
+        }
+    }
+
+    private int getTransitionType(StackTransitionInfo info) {
+        if (info.currentTransitionProcessRunning) {
+            if (info.startResult == START_SUCCESS) {
+                return TYPE_TRANSITION_WARM_LAUNCH;
+            } else if (info.startResult == START_TASK_TO_FRONT) {
+                return TYPE_TRANSITION_HOT_LAUNCH;
+            }
+        } else if (info.startResult == START_SUCCESS) {
+            return TYPE_TRANSITION_COLD_LAUNCH;
+        }
+        return -1;
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 77564bb..2b2471b 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -23,6 +23,7 @@
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE;
@@ -890,6 +891,10 @@
         return task != null ? (T) task.getStack() : null;
     }
 
+    private int getStackId() {
+        return getStack() != null ? getStack().mStackId : INVALID_STACK_ID;
+    }
+
     boolean changeWindowTranslucency(boolean toOpaque) {
         if (fullscreen == toOpaque) {
             return false;
@@ -1706,9 +1711,16 @@
     }
 
     @Override
+    public void onStartingWindowDrawn() {
+        synchronized (service) {
+            mStackSupervisor.mActivityMetricsLogger.notifyStartingWindowDrawn(getStackId());
+        }
+    }
+
+    @Override
     public void onWindowsDrawn() {
         synchronized (service) {
-            mStackSupervisor.mActivityMetricsLogger.notifyWindowsDrawn();
+            mStackSupervisor.mActivityMetricsLogger.notifyWindowsDrawn(getStackId());
             if (displayStartTime != 0) {
                 reportLaunchTimeLocked(SystemClock.uptimeMillis());
             }
@@ -1941,11 +1953,6 @@
     }
 
     void setRequestedOrientation(int requestedOrientation) {
-        if (task != null && (!task.mFullscreen || !task.getStack().mFullscreen)) {
-            // Fixed screen orientation isn't supported when activities aren't in full screen mode.
-            return;
-        }
-
         final int displayId = getDisplayId();
         final Configuration displayConfig =
                 mStackSupervisor.getDisplayOverrideConfiguration(displayId);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 2d91cad..2885e66 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -66,6 +66,7 @@
 import static com.android.server.am.ActivityRecord.ASSISTANT_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
+import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_CLOSE;
 import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
@@ -505,7 +506,7 @@
         }
         onParentChanged();
 
-        activityDisplay.attachStack(this, onTop);
+        activityDisplay.attachStack(this, findStackInsertIndex(onTop));
         if (mStackId == DOCKED_STACK_ID) {
             // If we created a docked stack we want to resize it so it resizes all other stacks
             // in the system.
@@ -799,16 +800,7 @@
         }
 
         mStacks.remove(this);
-        int addIndex = mStacks.size();
-        if (addIndex > 0) {
-            final ActivityStack topStack = mStacks.get(addIndex - 1);
-            if (StackId.isAlwaysOnTop(topStack.mStackId) && topStack != this) {
-                // If the top stack is always on top, we move this stack just below it.
-                addIndex--;
-            }
-        }
-
-        mStacks.add(addIndex, this);
+        mStacks.add(findStackInsertIndex(ON_TOP), this);
         mStackSupervisor.setFocusStackUnchecked(reason, this);
         if (task != null) {
             insertTaskAtTop(task, null);
@@ -841,6 +833,25 @@
         }
     }
 
+    /**
+     * @return the index to insert a new stack into, taking the always-on-top stacks into account.
+     */
+    private int findStackInsertIndex(boolean onTop) {
+        if (onTop) {
+            int addIndex = mStacks.size();
+            if (addIndex > 0) {
+                final ActivityStack topStack = mStacks.get(addIndex - 1);
+                if (StackId.isAlwaysOnTop(topStack.mStackId) && topStack != this) {
+                    // If the top stack is always on top, we move this stack just below it.
+                    addIndex--;
+                }
+            }
+            return addIndex;
+        } else {
+            return 0;
+        }
+    }
+
     boolean isFocusable() {
         if (StackId.canReceiveKeys(mStackId)) {
             return true;
@@ -1573,9 +1584,9 @@
 
             // If the assistant stack is focused and translucent, then the docked stack is always
             // visible
-            if (topStack.isAssistantStack()
-                    && topStack.isStackTranslucent(starting, DOCKED_STACK_ID)) {
-                return STACK_VISIBLE;
+            if (topStack.isAssistantStack()) {
+                return (topStack.isStackTranslucent(starting, DOCKED_STACK_ID)) ? STACK_VISIBLE
+                        : STACK_INVISIBLE;
             }
 
             // Otherwise, the docked stack is always visible, except in the case where the top
@@ -3205,7 +3216,8 @@
                     }
                     // Move the home stack to the top if this stack is fullscreen or there is no
                     // other visible stack.
-                    if (mStackSupervisor.moveHomeStackTaskToTop(myReason)) {
+                    if (task.isOverHomeStack() &&
+                            mStackSupervisor.moveHomeStackTaskToTop(myReason)) {
                         // Activity focus was already adjusted. Nothing else to do...
                         return;
                     }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 2ae815e30..42efe0b 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -4654,14 +4654,10 @@
             mDisplayId = display.getDisplayId();
         }
 
-        void attachStack(ActivityStack stack, boolean onTop) {
+        void attachStack(ActivityStack stack, int position) {
             if (DEBUG_STACK) Slog.v(TAG_STACK, "attachStack: attaching " + stack
-                    + " to displayId=" + mDisplayId + " onTop=" + onTop);
-            if (onTop) {
-                mStacks.add(stack);
-            } else {
-                mStacks.add(0, stack);
-            }
+                    + " to displayId=" + mDisplayId + " position=" + position);
+            mStacks.add(position, stack);
         }
 
         void detachStack(ActivityStack stack) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index c0c433e..99fe418 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -2013,10 +2013,6 @@
         final Configuration parentConfig = getParent().getConfiguration();
         final float density = parentConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
 
-        // TODO: Orientation?
-        config.orientation = (config.screenWidthDp <= config.screenHeightDp)
-                ? Configuration.ORIENTATION_PORTRAIT
-                : Configuration.ORIENTATION_LANDSCAPE;
         if (mStack != null) {
             final StackWindowController stackController = mStack.getWindowContainerController();
             stackController.adjustConfigurationForBounds(bounds, insetBounds,
@@ -2030,6 +2026,10 @@
             Slog.wtf(TAG, "Expected stack when caclulating override config");
         }
 
+        config.orientation = (config.screenWidthDp <= config.screenHeightDp)
+                ? Configuration.ORIENTATION_PORTRAIT
+                : Configuration.ORIENTATION_LANDSCAPE;
+
         // For calculating screen layout, we need to use the non-decor inset screen area for the
         // calculation for compatibility reasons, i.e. screen area without system bars that could
         // never go away in Honeycomb.
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 79b99a3..333d27b 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1241,6 +1241,13 @@
     /** @see AudioManager#adjustStreamVolume(int, int, int) */
     public void adjustStreamVolume(int streamType, int direction, int flags,
             String callingPackage) {
+        if ( streamType == AudioManager.STREAM_ACCESSIBILITY
+                && (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
+                        android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE))) {
+            Log.w(TAG, "Trying to call adjustStreamVolume() for a11y without"
+                    + "BIND_ACCESSIBILITY_SERVICE / callingPackage=" + callingPackage);
+            return;
+        }
         adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage,
                 Binder.getCallingUid());
     }
@@ -1552,6 +1559,13 @@
 
     /** @see AudioManager#setStreamVolume(int, int, int) */
     public void setStreamVolume(int streamType, int index, int flags, String callingPackage) {
+        if ( streamType == AudioManager.STREAM_ACCESSIBILITY
+                && (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
+                        android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE))) {
+            Log.w(TAG, "Trying to call setStreamVolume() for a11y without"
+                    + " BIND_ACCESSIBILITY_SERVICE  callingPackage=" + callingPackage);
+            return;
+        }
         setStreamVolume(streamType, index, flags, callingPackage, callingPackage,
                 Binder.getCallingUid());
     }
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
index d5fa26c..81e891a 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
@@ -151,7 +151,8 @@
     }
 
     private static void setDnsEvent(IpConnectivityEvent out, DnsEvent in) {
-        IpConnectivityLogClass.DNSLookupBatch dnsLookupBatch = new IpConnectivityLogClass.DNSLookupBatch();
+        IpConnectivityLogClass.DNSLookupBatch dnsLookupBatch =
+                new IpConnectivityLogClass.DNSLookupBatch();
         dnsLookupBatch.networkId = netIdOf(in.netId);
         dnsLookupBatch.eventTypes = bytesToInts(in.eventTypes);
         dnsLookupBatch.returnCodes = bytesToInts(in.returnCodes);
@@ -160,7 +161,8 @@
     }
 
     private static void setIpManagerEvent(IpConnectivityEvent out, IpManagerEvent in) {
-        IpConnectivityLogClass.IpProvisioningEvent ipProvisioningEvent = new IpConnectivityLogClass.IpProvisioningEvent();
+        IpConnectivityLogClass.IpProvisioningEvent ipProvisioningEvent =
+                new IpConnectivityLogClass.IpProvisioningEvent();
         ipProvisioningEvent.ifName = in.ifName;
         ipProvisioningEvent.eventType = in.eventType;
         ipProvisioningEvent.latencyMs = (int) in.durationMs;
@@ -168,14 +170,16 @@
     }
 
     private static void setIpReachabilityEvent(IpConnectivityEvent out, IpReachabilityEvent in) {
-        IpConnectivityLogClass.IpReachabilityEvent ipReachabilityEvent = new IpConnectivityLogClass.IpReachabilityEvent();
+        IpConnectivityLogClass.IpReachabilityEvent ipReachabilityEvent =
+                new IpConnectivityLogClass.IpReachabilityEvent();
         ipReachabilityEvent.ifName = in.ifName;
         ipReachabilityEvent.eventType = in.eventType;
         out.setIpReachabilityEvent(ipReachabilityEvent);
     }
 
     private static void setDefaultNetworkEvent(IpConnectivityEvent out, DefaultNetworkEvent in) {
-        IpConnectivityLogClass.DefaultNetworkEvent defaultNetworkEvent = new IpConnectivityLogClass.DefaultNetworkEvent();
+        IpConnectivityLogClass.DefaultNetworkEvent defaultNetworkEvent =
+                new IpConnectivityLogClass.DefaultNetworkEvent();
         defaultNetworkEvent.networkId = netIdOf(in.netId);
         defaultNetworkEvent.previousNetworkId = netIdOf(in.prevNetId);
         defaultNetworkEvent.transportTypes = in.transportTypes;
@@ -184,7 +188,8 @@
     }
 
     private static void setNetworkEvent(IpConnectivityEvent out, NetworkEvent in) {
-        IpConnectivityLogClass.NetworkEvent networkEvent = new IpConnectivityLogClass.NetworkEvent();
+        IpConnectivityLogClass.NetworkEvent networkEvent =
+                new IpConnectivityLogClass.NetworkEvent();
         networkEvent.networkId = netIdOf(in.netId);
         networkEvent.eventType = in.eventType;
         networkEvent.latencyMs = (int) in.durationMs;
@@ -192,7 +197,8 @@
     }
 
     private static void setValidationProbeEvent(IpConnectivityEvent out, ValidationProbeEvent in) {
-        IpConnectivityLogClass.ValidationProbeEvent validationProbeEvent = new IpConnectivityLogClass.ValidationProbeEvent();
+        IpConnectivityLogClass.ValidationProbeEvent validationProbeEvent =
+                new IpConnectivityLogClass.ValidationProbeEvent();
         validationProbeEvent.networkId = netIdOf(in.netId);
         validationProbeEvent.latencyMs = (int) in.durationMs;
         validationProbeEvent.probeType = in.probeType;
@@ -201,8 +207,10 @@
     }
 
     private static void setApfProgramEvent(IpConnectivityEvent out, ApfProgramEvent in) {
-        IpConnectivityLogClass.ApfProgramEvent apfProgramEvent = new IpConnectivityLogClass.ApfProgramEvent();
+        IpConnectivityLogClass.ApfProgramEvent apfProgramEvent =
+                new IpConnectivityLogClass.ApfProgramEvent();
         apfProgramEvent.lifetime = in.lifetime;
+        apfProgramEvent.effectiveLifetime = in.actualLifetime;
         apfProgramEvent.filteredRas = in.filteredRas;
         apfProgramEvent.currentRas = in.currentRas;
         apfProgramEvent.programLength = in.programLength;
@@ -216,7 +224,8 @@
     }
 
     private static void setApfStats(IpConnectivityEvent out, ApfStats in) {
-        IpConnectivityLogClass.ApfStatistics apfStatistics = new IpConnectivityLogClass.ApfStatistics();
+        IpConnectivityLogClass.ApfStatistics apfStatistics =
+                new IpConnectivityLogClass.ApfStatistics();
         apfStatistics.durationMs = in.durationMs;
         apfStatistics.receivedRas = in.receivedRas;
         apfStatistics.matchingRas = in.matchingRas;
@@ -224,6 +233,8 @@
         apfStatistics.zeroLifetimeRas = in.zeroLifetimeRas;
         apfStatistics.parseErrors = in.parseErrors;
         apfStatistics.programUpdates = in.programUpdates;
+        apfStatistics.programUpdatesAll = in.programUpdatesAll;
+        apfStatistics.programUpdatesAllowingMulticast = in.programUpdatesAllowingMulticast;
         apfStatistics.maxProgramSize = in.maxProgramSize;
         out.setApfStatistics(apfStatistics);
     }
diff --git a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
index 14f2e86..552f0d1 100644
--- a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
+++ b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
@@ -83,6 +83,7 @@
             if (inLockoutMode) {
                 try {
                     Slog.w(TAG, "Forcing lockout (fp driver code should do this!)");
+                    stop(false); // cancel fingerprint authentication
                     receiver.onError(getHalDeviceId(),
                             FingerprintManager.FINGERPRINT_ERROR_LOCKOUT, 0 /* vendorCode */);
                 } catch (RemoteException e) {
@@ -107,7 +108,7 @@
     public int start() {
         IBiometricsFingerprint daemon = getFingerprintDaemon();
         if (daemon == null) {
-            Slog.w(TAG, "start authentication: no fingeprintd!");
+            Slog.w(TAG, "start authentication: no fingerprint HAL!");
             return ERROR_ESRCH;
         }
         try {
@@ -130,7 +131,7 @@
     public int stop(boolean initiatedByClient) {
         IBiometricsFingerprint daemon = getFingerprintDaemon();
         if (daemon == null) {
-            Slog.w(TAG, "stopAuthentication: no fingeprintd!");
+            Slog.w(TAG, "stopAuthentication: no fingerprint HAL!");
             return ERROR_ESRCH;
         }
         try {
diff --git a/services/core/java/com/android/server/fingerprint/ClientMonitor.java b/services/core/java/com/android/server/fingerprint/ClientMonitor.java
index 43bb21d..492cd61 100644
--- a/services/core/java/com/android/server/fingerprint/ClientMonitor.java
+++ b/services/core/java/com/android/server/fingerprint/ClientMonitor.java
@@ -28,13 +28,13 @@
 import java.util.NoSuchElementException;
 
 /**
- * Abstract base class for keeping track and dispatching events from fingerprintd to the
+ * Abstract base class for keeping track and dispatching events from fingerprint HAL to the
  * the current client.  Subclasses are responsible for coordinating the interaction with
- * fingerprintd for the specific action (e.g. authenticate, enroll, enumerate, etc.).
+ * fingerprint HAL for the specific action (e.g. authenticate, enroll, enumerate, etc.).
  */
 public abstract class ClientMonitor implements IBinder.DeathRecipient {
     protected static final String TAG = FingerprintService.TAG; // TODO: get specific name
-    protected static final int ERROR_ESRCH = 3; // Likely fingerprintd is dead. See errno.h.
+    protected static final int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. See errno.h.
     protected static final boolean DEBUG = FingerprintService.DEBUG;
     private IBinder mToken;
     private IFingerprintServiceReceiver mReceiver;
@@ -77,13 +77,13 @@
     }
 
     /**
-     * Contacts fingerprintd to start the client.
+     * Contacts fingerprint HAL to start the client.
      * @return 0 on succes, errno from driver on failure
      */
     public abstract int start();
 
     /**
-     * Contacts fingerprintd to stop the client.
+     * Contacts fingerprint HAL to stop the client.
      * @param initiatedByClient whether the operation is at the request of a client
      */
     public abstract int stop(boolean initiatedByClient);
@@ -108,7 +108,7 @@
     public abstract boolean onEnumerationResult(int fingerId, int groupId, int remaining);
 
     /**
-     * Called when we get notification from fingerprintd that an image has been acquired.
+     * Called when we get notification from fingerprint HAL that an image has been acquired.
      * Common to authenticate and enroll.
      * @param acquiredInfo info about the current image acquisition
      * @return true if client should be removed
@@ -131,7 +131,7 @@
     }
 
     /**
-     * Called when we get notification from fingerprintd that an error has occurred with the
+     * Called when we get notification from fingerprint HAL that an error has occurred with the
      * current operation. Common to authenticate, enroll, enumerate and remove.
      * @param error
      * @return true if client should be removed
diff --git a/services/core/java/com/android/server/fingerprint/EnrollClient.java b/services/core/java/com/android/server/fingerprint/EnrollClient.java
index eddcd5b..e1b78a8 100644
--- a/services/core/java/com/android/server/fingerprint/EnrollClient.java
+++ b/services/core/java/com/android/server/fingerprint/EnrollClient.java
@@ -80,7 +80,7 @@
     public int start() {
         IBiometricsFingerprint daemon = getFingerprintDaemon();
         if (daemon == null) {
-            Slog.w(TAG, "enroll: no fingeprintd!");
+            Slog.w(TAG, "enroll: no fingerprint HAL!");
             return ERROR_ESRCH;
         }
         final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
@@ -102,7 +102,7 @@
     public int stop(boolean initiatedByClient) {
         IBiometricsFingerprint daemon = getFingerprintDaemon();
         if (daemon == null) {
-            Slog.w(TAG, "stopEnrollment: no fingeprintd!");
+            Slog.w(TAG, "stopEnrollment: no fingerprint HAL!");
             return ERROR_ESRCH;
         }
         try {
diff --git a/services/core/java/com/android/server/fingerprint/EnumerateClient.java b/services/core/java/com/android/server/fingerprint/EnumerateClient.java
index 55bf689..34f245f 100644
--- a/services/core/java/com/android/server/fingerprint/EnumerateClient.java
+++ b/services/core/java/com/android/server/fingerprint/EnumerateClient.java
@@ -58,7 +58,7 @@
     public int stop(boolean initiatedByClient) {
         IBiometricsFingerprint daemon = getFingerprintDaemon();
         if (daemon == null) {
-            Slog.w(TAG, "stopAuthentication: no fingeprintd!");
+            Slog.w(TAG, "stopAuthentication: no fingerprint HAL!");
             return ERROR_ESRCH;
         }
         try {
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 0f29942..3262151 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -206,7 +206,7 @@
 
     @Override
     public void serviceDied(long cookie) {
-        Slog.v(TAG, "fingerprintd died");
+        Slog.v(TAG, "fingerprint HAL died");
         MetricsLogger.count(mContext, "fingerprintd_died", 1);
         synchronized (this) {
             mDaemon = null;
@@ -235,7 +235,7 @@
             try {
                 mHalDeviceId = mDaemon.setNotify(mDaemonCallback);
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to open fingeprintd HAL", e);
+                Slog.e(TAG, "Failed to open fingerprint HAL", e);
                 mDaemon = null; // try again later!
             }
 
@@ -391,7 +391,7 @@
     public long startPreEnroll(IBinder token) {
         IBiometricsFingerprint daemon = getFingerprintDaemon();
         if (daemon == null) {
-            Slog.w(TAG, "startPreEnroll: no fingeprintd!");
+            Slog.w(TAG, "startPreEnroll: no fingerprint HAL!");
             return 0;
         }
         try {
@@ -405,7 +405,7 @@
     public int startPostEnroll(IBinder token) {
         IBiometricsFingerprint daemon = getFingerprintDaemon();
         if (daemon == null) {
-            Slog.w(TAG, "startPostEnroll: no fingeprintd!");
+            Slog.w(TAG, "startPostEnroll: no fingerprint HAL!");
             return 0;
         }
         try {
@@ -417,7 +417,7 @@
     }
 
     /**
-     * Calls fingerprintd to switch states to the new task. If there's already a current task,
+     * Calls fingerprint HAL to switch states to the new task. If there's already a current task,
      * it calls cancel() and sets mPendingClient to begin when the current task finishes
      * ({@link FingerprintManager#FINGERPRINT_ERROR_CANCELED}).
      * @param newClient the new client that wants to connect
@@ -447,7 +447,7 @@
             IFingerprintServiceReceiver receiver, boolean restricted) {
         IBiometricsFingerprint daemon = getFingerprintDaemon();
         if (daemon == null) {
-            Slog.w(TAG, "startRemove: no fingeprintd!");
+            Slog.w(TAG, "startRemove: no fingerprint HAL!");
             return;
         }
         RemovalClient client = new RemovalClient(getContext(), mHalDeviceId, token,
@@ -469,7 +469,7 @@
         IFingerprintServiceReceiver receiver, boolean restricted) {
         IBiometricsFingerprint daemon = getFingerprintDaemon();
         if (daemon == null) {
-            Slog.w(TAG, "startEnumerate: no fingeprintd!");
+            Slog.w(TAG, "startEnumerate: no fingerprint HAL!");
             return;
         }
         EnumerateClient client = new EnumerateClient(getContext(), mHalDeviceId, token,
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index d81e092..a50ec49f 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -72,7 +72,8 @@
     @ServiceThreadOnly
     private boolean mArcEstablished = false;
 
-    // Stores whether ARC feature is enabled per port. True by default for all the ARC-enabled ports.
+    // Stores whether ARC feature is enabled per port.
+    // True by default for all the ARC-enabled ports.
     private final SparseBooleanArray mArcFeatureEnabled = new SparseBooleanArray();
 
     // Whether System audio mode is activated or not.
@@ -80,6 +81,10 @@
     @GuardedBy("mLock")
     private boolean mSystemAudioActivated = false;
 
+    // Whether the System Audio Control feature is enabled or not. True by default.
+    @GuardedBy("mLock")
+    private boolean mSystemAudioControlFeatureEnabled;
+
     // The previous port id (input) before switching to the new one. This is remembered in order to
     // be able to switch to it upon receiving <Inactive Source> from currently active source.
     // This remains valid only when the active source was switched via one touch play operation
@@ -186,6 +191,8 @@
         mAutoDeviceOff = mService.readBooleanSetting(Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
                 true);
         mAutoWakeup = mService.readBooleanSetting(Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED, true);
+        mSystemAudioControlFeatureEnabled =
+                mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true);
         mStandbyHandler = new HdmiCecStandbyModeHandler(service, this);
     }
 
@@ -778,14 +785,11 @@
                         addAndStartAction(new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
                         addAndStartAction(new PowerStatusMonitorAction(HdmiCecLocalDeviceTv.this));
 
-                        // If there is AVR, initiate System Audio Auto initiation action,
-                        // which turns on and off system audio according to last system
-                        // audio setting.
                         HdmiDeviceInfo avr = getAvrDeviceInfo();
                         if (avr != null) {
                             onNewAvrAdded(avr);
                         } else {
-                            setSystemAudioMode(false, true);
+                            setSystemAudioMode(false);
                         }
                     }
                 });
@@ -818,13 +822,13 @@
     void changeSystemAudioMode(boolean enabled, IHdmiControlCallback callback) {
         assertRunOnServiceThread();
         if (!mService.isControlEnabled() || hasAction(DeviceDiscoveryAction.class)) {
-            setSystemAudioMode(false, true);
+            setSystemAudioMode(false);
             invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
             return;
         }
         HdmiDeviceInfo avr = getAvrDeviceInfo();
         if (avr == null) {
-            setSystemAudioMode(false, true);
+            setSystemAudioMode(false);
             invokeCallback(callback, HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE);
             return;
         }
@@ -834,12 +838,13 @@
     }
 
     // # Seq 25
-    void setSystemAudioMode(boolean on, boolean updateSetting) {
-        HdmiLogger.debug("System Audio Mode change[old:%b new:%b]", mSystemAudioActivated, on);
-
-        if (updateSetting) {
-            mService.writeBooleanSetting(Global.HDMI_SYSTEM_AUDIO_ENABLED, on);
+    void setSystemAudioMode(boolean on) {
+        if (!isSystemAudioControlFeatureEnabled() && on) {
+            HdmiLogger.debug("Cannot turn on system audio mode "
+                    + "because the System Audio Control feature is disabled.");
+            return;
         }
+        HdmiLogger.debug("System Audio Mode change[old:%b new:%b]", mSystemAudioActivated, on);
         updateAudioManagerForSystemAudio(on);
         synchronized (mLock) {
             if (mSystemAudioActivated != on) {
@@ -863,8 +868,21 @@
         }
     }
 
-    boolean getSystemAudioModeSetting() {
-        return mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_ENABLED, false);
+    @ServiceThreadOnly
+    void setSystemAudioControlFeatureEnabled(boolean enabled) {
+        assertRunOnServiceThread();
+        synchronized (mLock) {
+            mSystemAudioControlFeatureEnabled = enabled;
+        }
+        if (hasSystemAudioDevice()) {
+            changeSystemAudioMode(enabled, null);
+        }
+    }
+
+    boolean isSystemAudioControlFeatureEnabled() {
+        synchronized (mLock) {
+            return mSystemAudioControlFeatureEnabled;
+        }
     }
 
     /**
@@ -1112,6 +1130,7 @@
     @ServiceThreadOnly
     protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
         assertRunOnServiceThread();
+        boolean systemAudioStatus = HdmiUtils.parseCommandParamSystemAudioStatus(message);
         if (!isMessageForSystemAudio(message)) {
             if (getAvrDeviceInfo() == null) {
                 // AVR may not have been discovered yet. Delay the message processing.
@@ -1121,10 +1140,15 @@
                 mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
             }
             return true;
+        } else if (systemAudioStatus && !isSystemAudioControlFeatureEnabled()) {
+            HdmiLogger.debug("Ignoring <Set System Audio Mode> message "
+                    + "because the System Audio Control feature is disabled: %s", message);
+            mService.maySendFeatureAbortCommand(message, Constants.ABORT_REFUSED);
+            return true;
         }
         removeAction(SystemAudioAutoInitiationAction.class);
         SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
-                message.getSource(), HdmiUtils.parseCommandParamSystemAudioStatus(message), null);
+                message.getSource(), systemAudioStatus, null);
         addAndStartAction(action);
         return true;
     }
@@ -1138,7 +1162,7 @@
             // Ignore this message.
             return true;
         }
-        setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message), true);
+        setSystemAudioMode(HdmiUtils.parseCommandParamSystemAudioStatus(message));
         return true;
     }
 
@@ -1882,6 +1906,7 @@
         pw.println("mArcFeatureEnabled: " + mArcFeatureEnabled);
         pw.println("mSystemAudioActivated: " + mSystemAudioActivated);
         pw.println("mSystemAudioMute: " + mSystemAudioMute);
+        pw.println("mSystemAudioControlFeatureEnabled: " + mSystemAudioControlFeatureEnabled);
         pw.println("mAutoDeviceOff: " + mAutoDeviceOff);
         pw.println("mAutoWakeup: " + mAutoWakeup);
         pw.println("mSkipRoutingControl: " + mSkipRoutingControl);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 89b10ac..6864e1e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -486,7 +486,7 @@
                 Global.HDMI_CONTROL_ENABLED,
                 Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
                 Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
-                Global.HDMI_SYSTEM_AUDIO_ENABLED,
+                Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
                 Global.MHL_INPUT_SWITCHING_ENABLED,
                 Global.MHL_POWER_CHARGE_ENABLED
         };
@@ -525,9 +525,9 @@
                     }
                     // No need to propagate to HAL.
                     break;
-                case Global.HDMI_SYSTEM_AUDIO_ENABLED:
-                    if (isTvDeviceEnabled() && tv().isSystemAudioActivated() != enabled) {
-                        tv().changeSystemAudioMode(enabled, null);
+                case Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED:
+                    if (isTvDeviceEnabled()) {
+                        tv().setSystemAudioControlFeatureEnabled(enabled);
                     }
                     break;
                 case Global.MHL_INPUT_SWITCHING_ENABLED:
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index e1bcd99..7670dcc 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -262,8 +262,7 @@
             return;
         }
 
-        // Turn off system audio mode and update settings.
-        tv().setSystemAudioMode(false, true);
+        tv().setSystemAudioMode(false);
         if (tv().isArcEstablished()) {
             tv().enableAudioReturnChannel(false);
             addAndStartAction(new RequestArcTerminationAction(localDevice(), address));
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index af1a85d..449b208 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -133,7 +133,7 @@
     }
 
     protected void setSystemAudioMode(boolean mode) {
-        tv().setSystemAudioMode(mode, true);
+        tv().setSystemAudioMode(mode);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
index 01063b7..d347a91 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
@@ -50,7 +50,7 @@
             @Override
             public void onSendCompleted(int error) {
                 if (error != SendMessageResult.SUCCESS) {
-                    tv().setSystemAudioMode(false, true);
+                    tv().setSystemAudioMode(false);
                     finish();
                 }
             }
@@ -71,18 +71,24 @@
         return false;
     }
 
-    private void handleSystemAudioModeStatusMessage(boolean isSystemAudioModeOn) {
+    private void handleSystemAudioModeStatusMessage(boolean currentSystemAudioMode) {
         if (!canChangeSystemAudio()) {
             HdmiLogger.debug("Cannot change system audio mode in auto initiation action.");
             finish();
             return;
         }
 
-        boolean systemAudioModeSetting = tv().getSystemAudioModeSetting();
-        if (systemAudioModeSetting && !isSystemAudioModeOn) {
-            addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress, systemAudioModeSetting, null));
+        // If System Audio Control feature is enabled, turn on system audio mode when new AVR is
+        // detected. Otherwise, turn off system audio mode.
+        boolean targetSystemAudioMode = tv().isSystemAudioControlFeatureEnabled();
+        if (currentSystemAudioMode != targetSystemAudioMode) {
+            // Start System Audio Control feature actions only if necessary.
+            addAndStartAction(
+                    new SystemAudioActionFromTv(tv(), mAvrAddress, targetSystemAudioMode, null));
         } else {
-            tv().setSystemAudioMode(isSystemAudioModeOn, true);
+            // If AVR already has correct system audio mode, update target system audio mode
+            // immediately rather than starting feature action.
+            tv().setSystemAudioMode(targetSystemAudioMode);
         }
         finish();
     }
@@ -101,13 +107,15 @@
     }
 
     private void handleSystemAudioModeStatusTimeout() {
-        if (tv().getSystemAudioModeSetting()) {
-            if (canChangeSystemAudio()) {
-                addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress, true, null));
-            }
-        } else {
-            tv().setSystemAudioMode(false, true);
+        if (!canChangeSystemAudio()) {
+            HdmiLogger.debug("Cannot change system audio mode in auto initiation action.");
+            finish();
+            return;
         }
+        // If we can't get the current system audio mode status, just try to turn on/off system
+        // audio mode according to the system audio control setting.
+        addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress,
+                tv().isSystemAudioControlFeatureEnabled(), null));
         finish();
     }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index be8aaf0..8280946 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -159,6 +159,8 @@
 
 import libcore.io.IoUtils;
 
+import com.google.android.collect.Lists;
+
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.xmlpull.v1.XmlPullParser;
@@ -1658,8 +1660,7 @@
         public NotificationChannel getNotificationChannelForPackage(String pkg, int uid,
                 String channelId, boolean includeDeleted) {
             checkCallerIsSystem();
-            return mRankingHelper.getNotificationChannel
-                    (pkg, uid, channelId, includeDeleted);
+            return mRankingHelper.getNotificationChannel(pkg, uid, channelId, includeDeleted);
         }
 
         @Override
@@ -1675,6 +1676,27 @@
         }
 
         @Override
+        public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(
+                String pkg) {
+            checkCallerIsSystemOrSameApp(pkg);
+            return new ParceledListSlice<>(new ArrayList(
+                    mRankingHelper.getNotificationChannelGroups(pkg, Binder.getCallingUid())));
+        }
+
+        @Override
+        public void deleteNotificationChannelGroup(String pkg, String channelGroupId) {
+            checkCallerIsSystemOrSameApp(pkg);
+
+            List<String> deletedChannelIds = mRankingHelper.deleteNotificationChannelGroup(
+                    pkg, Binder.getCallingUid(), channelGroupId);
+            for (int i = 0; i < deletedChannelIds.size(); i++) {
+                cancelAllNotificationsInt(MY_UID, MY_PID, pkg, deletedChannelIds.get(i), 0, 0, true,
+                        UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED, null);
+            }
+            savePolicyFile();
+        }
+
+        @Override
         public void updateNotificationChannelForPackage(String pkg, int uid,
                 NotificationChannel channel) {
             enforceSystemOrSystemUI("Caller not system or systemui");
@@ -1698,6 +1720,12 @@
         }
 
         @Override
+        public int getDeletedChannelCount(String pkg, int uid) {
+            enforceSystemOrSystemUI("getDeletedChannelCount");
+            return mRankingHelper.getDeletedChannelCount(pkg, uid);
+        }
+
+        @Override
         public ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroupsForPackage(
                 String pkg, int uid, boolean includeDeleted) {
             checkCallerIsSystem();
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 4b1804c..02f92fe 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -15,8 +15,6 @@
  */
 package com.android.server.notification;
 
-import static android.app.NotificationManager.IMPORTANCE_NONE;
-
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
@@ -69,7 +67,6 @@
 
     private static final String ATT_VERSION = "version";
     private static final String ATT_NAME = "name";
-    private static final String ATT_NAME_RES_ID = "name_res_id";
     private static final String ATT_UID = "uid";
     private static final String ATT_ID = "id";
     private static final String ATT_PRIORITY = "priority";
@@ -195,14 +192,9 @@
                             if (TAG_GROUP.equals(tagName)) {
                                 String id = parser.getAttributeValue(null, ATT_ID);
                                 CharSequence groupName = parser.getAttributeValue(null, ATT_NAME);
-                                int groupNameRes = safeInt(parser, ATT_NAME_RES_ID, 0);
                                 if (!TextUtils.isEmpty(id)) {
-                                    NotificationChannelGroup group = null;
-                                    if (groupName != null) {
-                                        group = new NotificationChannelGroup(id, groupName);
-                                    } else {
-                                        group = new NotificationChannelGroup(id, groupNameRes);
-                                    }
+                                    NotificationChannelGroup group
+                                            = new NotificationChannelGroup(id, groupName);
                                     r.groups.put(id, group);
                                 }
                             }
@@ -210,19 +202,12 @@
                             if (TAG_CHANNEL.equals(tagName)) {
                                 String id = parser.getAttributeValue(null, ATT_ID);
                                 CharSequence channelName = parser.getAttributeValue(null, ATT_NAME);
-                                int channelNameRes = safeInt(parser, ATT_NAME_RES_ID, 0);
                                 int channelImportance =
                                         safeInt(parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE);
 
-                                if (!TextUtils.isEmpty(id)) {
-                                    NotificationChannel channel;
-                                    if (channelName != null) {
-                                        channel = new NotificationChannel(id, channelName,
-                                                channelImportance);
-                                    } else {
-                                        channel = new NotificationChannel(id, channelNameRes,
-                                                channelImportance);
-                                    }
+                                if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
+                                    NotificationChannel channel = new NotificationChannel(id,
+                                            channelName, channelImportance);
                                     channel.populateFromXml(parser);
                                     r.channels.put(id, channel);
                                 }
@@ -302,7 +287,7 @@
             NotificationChannel channel;
             channel = new NotificationChannel(
                     NotificationChannel.DEFAULT_CHANNEL_ID,
-                    R.string.default_notification_channel_label,
+                    mContext.getString(R.string.default_notification_channel_label),
                     r.importance);
             channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
             channel.setLockscreenVisibility(r.visibility);
@@ -482,8 +467,7 @@
         Preconditions.checkNotNull(pkg);
         Preconditions.checkNotNull(group);
         Preconditions.checkNotNull(group.getId());
-        Preconditions.checkNotNull(!TextUtils.isEmpty(group.getName())
-                || group.getNameResId() != 0);
+        Preconditions.checkNotNull(!TextUtils.isEmpty(group.getName()));
         Record r = getOrCreateRecord(pkg, uid);
         if (r == null) {
             throw new IllegalArgumentException("Invalid package");
@@ -504,8 +488,7 @@
         Preconditions.checkNotNull(pkg);
         Preconditions.checkNotNull(channel);
         Preconditions.checkNotNull(channel.getId());
-        Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName())
-                || channel.getNameResId() != 0);
+        Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName()));
         Record r = getOrCreateRecord(pkg, uid);
         if (r == null) {
             throw new IllegalArgumentException("Invalid package");
@@ -524,7 +507,7 @@
                 existing.setDeleted(false);
             }
 
-            existing.setNameResId(channel.getNameResId());
+            existing.setName(channel.getName());
 
             MetricsLogger.action(getChannelLog(channel, pkg));
             updateConfig();
@@ -669,8 +652,6 @@
 
     @Override
     public void deleteNotificationChannel(String pkg, int uid, String channelId) {
-        Preconditions.checkNotNull(pkg);
-        Preconditions.checkNotNull(channelId);
         Record r = getRecord(pkg, uid);
         if (r == null) {
             return;
@@ -682,6 +663,7 @@
         LogMaker lm = getChannelLog(channel, pkg);
         lm.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);
         MetricsLogger.action(lm);
+        updateConfig();
     }
 
     @Override
@@ -694,6 +676,7 @@
             return;
         }
         r.channels.remove(channelId);
+        updateConfig();
     }
 
     @Override
@@ -710,6 +693,7 @@
                 r.channels.remove(key);
             }
         }
+        updateConfig();
     }
 
     public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
@@ -734,12 +718,15 @@
             final NotificationChannel nc = r.channels.valueAt(i);
             if (includeDeleted || !nc.isDeleted()) {
                 if (nc.getGroup() != null) {
-                    NotificationChannelGroup ncg = groups.get(nc.getGroup());
-                    if (ncg == null ) {
-                        ncg = r.groups.get(nc.getGroup()).clone();
-                        groups.put(nc.getGroup(), ncg);
+                    if (r.groups.get(nc.getGroup()) != null) {
+                        NotificationChannelGroup ncg = groups.get(nc.getGroup());
+                        if (ncg == null) {
+                            ncg = r.groups.get(nc.getGroup()).clone();
+                            groups.put(nc.getGroup(), ncg);
+
+                        }
+                        ncg.addChannel(nc);
                     }
-                    ncg.addChannel(nc);
                 } else {
                     nonGrouped.addChannel(nc);
                 }
@@ -751,8 +738,29 @@
         return new ParceledListSlice<>(new ArrayList<>(groups.values()));
     }
 
+    public List<String> deleteNotificationChannelGroup(String pkg, int uid,
+            String groupId) {
+        List<String> deletedChannelIds = new ArrayList<>();
+        Record r = getRecord(pkg, uid);
+        if (r == null || TextUtils.isEmpty(groupId)) {
+            return deletedChannelIds;
+        }
+
+        r.groups.remove(groupId);
+
+        int N = r.channels.size();
+        for (int i = 0; i < N; i++) {
+            final NotificationChannel nc = r.channels.valueAt(i);
+            if (groupId.equals(nc.getGroup())) {
+                nc.setDeleted(true);
+                deletedChannelIds.add(nc.getId());
+            }
+        }
+        updateConfig();
+        return deletedChannelIds;
+    }
+
     @Override
-    @VisibleForTesting
     public Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
             int uid) {
         Record r = getRecord(pkg, uid);
@@ -781,6 +789,23 @@
         return new ParceledListSlice<>(channels);
     }
 
+    public int getDeletedChannelCount(String pkg, int uid) {
+        Preconditions.checkNotNull(pkg);
+        int deletedCount = 0;
+        Record r = getRecord(pkg, uid);
+        if (r == null) {
+            return deletedCount;
+        }
+        int N = r.channels.size();
+        for (int i = 0; i < N; i++) {
+            final NotificationChannel nc = r.channels.valueAt(i);
+            if (nc.isDeleted()) {
+                deletedCount++;
+            }
+        }
+        return deletedCount;
+    }
+
     /**
      * Sets importance.
      */
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index b085179..6af1c3b 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -288,6 +288,10 @@
         if (overlayPackage == null) {
             return false;
         }
+        // Static overlay is always being enabled.
+        if (!enable && overlayPackage.isStaticOverlay) {
+            return false;
+        }
 
         try {
             final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
@@ -333,17 +337,28 @@
         }
     }
 
+    boolean isPackageUpdatableOverlay(@NonNull final String packageName, final int userId) {
+        final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
+        if (overlayPackage == null || overlayPackage.isStaticOverlay) {
+            return false;
+        }
+        return true;
+    }
+
     boolean setPriority(@NonNull final String packageName,
             @NonNull final String newParentPackageName, final int userId) {
-        return mSettings.setPriority(packageName, newParentPackageName, userId);
+        return isPackageUpdatableOverlay(packageName, userId) &&
+                mSettings.setPriority(packageName, newParentPackageName, userId);
     }
 
     boolean setHighestPriority(@NonNull final String packageName, final int userId) {
-        return mSettings.setHighestPriority(packageName, userId);
+        return isPackageUpdatableOverlay(packageName, userId) &&
+                mSettings.setHighestPriority(packageName, userId);
     }
 
     boolean setLowestPriority(@NonNull final String packageName, final int userId) {
-        return mSettings.setLowestPriority(packageName, userId);
+        return isPackageUpdatableOverlay(packageName, userId) &&
+                mSettings.setLowestPriority(packageName, userId);
     }
 
     void onDump(@NonNull final PrintWriter pw) {
@@ -368,7 +383,9 @@
     private void updateState(@Nullable final PackageInfo targetPackage,
             @NonNull final PackageInfo overlayPackage, final int userId)
         throws OverlayManagerSettings.BadKeyException {
-        if (targetPackage != null) {
+        // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
+        if (targetPackage != null &&
+                !("android".equals(targetPackage.packageName) && overlayPackage.isStaticOverlay)) {
             mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
         }
 
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 7aa96cf..d8900c0 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -49,8 +49,6 @@
 
     private static final boolean DEBUG = false;
 
-    private static final long RETRY_LATENCY = 4 * AlarmManager.INTERVAL_HOUR;
-
     private static final int JOB_IDLE_OPTIMIZE = 800;
     private static final int JOB_POST_BOOT_UPDATE = 801;
 
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 6365d15..bb7ffda 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -207,9 +207,35 @@
     }
 
     public void grantDefaultPermissions(int userId) {
-        grantPermissionsToSysComponentsAndPrivApps(userId);
-        grantDefaultSystemHandlerPermissions(userId);
-        grantDefaultPermissionExceptions(userId);
+        if (mService.hasSystemFeature(PackageManager.FEATURE_EMBEDDED, 0)) {
+            grantAllRuntimePermissions(userId);
+        } else {
+            grantPermissionsToSysComponentsAndPrivApps(userId);
+            grantDefaultSystemHandlerPermissions(userId);
+            grantDefaultPermissionExceptions(userId);
+        }
+    }
+
+    private void grantRuntimePermissionsForPackageLocked(int userId, PackageParser.Package pkg) {
+        Set<String> permissions = new ArraySet<>();
+        for (String permission :  pkg.requestedPermissions) {
+            BasePermission bp = mService.mSettings.mPermissions.get(permission);
+            if (bp != null && bp.isRuntime()) {
+                permissions.add(permission);
+            }
+        }
+        if (!permissions.isEmpty()) {
+            grantRuntimePermissionsLPw(pkg, permissions, true, userId);
+        }
+    }
+
+    private void grantAllRuntimePermissions(int userId) {
+        Log.i(TAG, "Granting all runtime permissions for user " + userId);
+        synchronized (mService.mPackages) {
+            for (PackageParser.Package pkg : mService.mPackages.values()) {
+                grantRuntimePermissionsForPackageLocked(userId, pkg);
+            }
+        }
     }
 
     public void scheduleReadDefaultPermissionExceptions() {
@@ -226,18 +252,7 @@
                         || pkg.requestedPermissions.isEmpty()) {
                     continue;
                 }
-                Set<String> permissions = new ArraySet<>();
-                final int permissionCount = pkg.requestedPermissions.size();
-                for (int i = 0; i < permissionCount; i++) {
-                    String permission = pkg.requestedPermissions.get(i);
-                    BasePermission bp = mService.mSettings.mPermissions.get(permission);
-                    if (bp != null && bp.isRuntime()) {
-                        permissions.add(permission);
-                    }
-                }
-                if (!permissions.isEmpty()) {
-                    grantRuntimePermissionsLPw(pkg, permissions, true, userId);
-                }
+                grantRuntimePermissionsForPackageLocked(userId, pkg);
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 71bfa64..e1426fd 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -245,9 +245,12 @@
 
                     // STOPSHIP Remove the whitelist.
                     if ("com.google.android.talk".equals(callingPackage)
-                            || "com.google.android.quicksearchbox".equals(callingPackage)) {
+                            || "com.google.android.quicksearchbox".equals(callingPackage)
+                            || "com.google.android.googlequicksearchbox".equals(callingPackage)
+                            ) {
                         return false;
                     }
+                    // STOPSHIP Change it to 'e'.
                     Slog.wtfStack(TAG, message + " by " + callingPackage + " for another profile "
                             + targetUserId + " from " + callingUserId);
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e8af310..4640d20 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -260,17 +260,18 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 import com.android.server.AttributeCache;
-import com.android.server.BackgroundDexOptJobService;
 import com.android.server.DeviceIdleController;
 import com.android.server.EventLogTags;
 import com.android.server.FgThread;
 import com.android.server.IntentResolver;
 import com.android.server.LocalServices;
+import com.android.server.LockGuard;
 import com.android.server.ServiceThread;
 import com.android.server.SystemConfig;
 import com.android.server.SystemServerInitThreadPool;
 import com.android.server.Watchdog;
 import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.pm.BackgroundDexOptService;
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.PermissionsState.PermissionState;
 import com.android.server.pm.Settings.DatabaseVersion;
@@ -398,7 +399,7 @@
     private static final boolean HIDE_EPHEMERAL_APIS = false;
 
     private static final boolean ENABLE_FREE_CACHE_V2 =
-            SystemProperties.getBoolean("fw.free_cache_v2", false);
+            SystemProperties.getBoolean("fw.free_cache_v2", true);
 
     private static final int RADIO_UID = Process.PHONE_UID;
     private static final int LOG_UID = Process.LOG_UID;
@@ -2212,6 +2213,7 @@
 
     public PackageManagerService(Context context, Installer installer,
             boolean factoryTest, boolean onlyCore) {
+        LockGuard.installLock(mPackages, LockGuard.INDEX_PACKAGES);
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "create package manager");
         EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
                 SystemClock.uptimeMillis());
@@ -10563,9 +10565,9 @@
                         ps.pkg.applicationInfo.primaryCpuAbi = adjustedAbi;
                         Slog.i(TAG, "Adjusting ABI for " + ps.name + " to " + adjustedAbi
                                 + " (requirer="
-                                + (requirer == null ? "null" : requirer.pkg.packageName)
+                                + (requirer != null ? requirer.pkg : "null")
                                 + ", scannedPackage="
-                                + (scannedPackage != null ? scannedPackage.packageName : "null")
+                                + (scannedPackage != null ? scannedPackage : "null")
                                 + ")");
                         try {
                             mInstaller.rmdex(ps.codePathString,
@@ -12188,6 +12190,11 @@
             if (!isInstantApp && userState.instantApp) {
                 return null;
             }
+            // throw out instant app filters if updates are available; will trigger
+            // instant app resolution
+            if (userState.instantApp && ps.isUpdateAvailable()) {
+                return null;
+            }
             final ResolveInfo res = new ResolveInfo();
             res.activityInfo = ai;
             if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
@@ -16843,11 +16850,11 @@
                     mDexManager.isUsedByOtherApps(pkg.packageName));
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
-            // Notify BackgroundDexOptJobService that the package has been changed.
+            // Notify BackgroundDexOptService that the package has been changed.
             // If this is an update of a package which used to fail to compile,
             // BDOS will remove it from its blacklist.
             // TODO: Layering violation
-            BackgroundDexOptJobService.notifyPackageChanged(pkg.packageName);
+            BackgroundDexOptService.notifyPackageChanged(pkg.packageName);
         }
 
         if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
@@ -16883,6 +16890,7 @@
             final PackageSetting ps = mSettings.mPackages.get(pkgName);
             if (ps != null) {
                 res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
+                ps.setUpdateAvailable(false /*updateAvailable*/);
             }
 
             final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
@@ -19672,6 +19680,17 @@
     }
 
     @Override
+    public void setUpdateAvailable(String packageName, boolean updateAvailable) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
+        synchronized (mPackages) {
+            final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
+            if (pkgSetting != null) {
+                pkgSetting.setUpdateAvailable(updateAvailable);
+            }
+        }
+    }
+
+    @Override
     public void setComponentEnabledSetting(ComponentName componentName,
             int newState, int flags, int userId) {
         if (!sUserManager.exists(userId)) return;
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index b9c43da..dfed72f 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -141,6 +141,8 @@
     String volumeUuid;
     /** The category of this app, as hinted by the installer */
     int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED;
+    /** Whether or not an update is available. Ostensibly only for instant apps. */
+    boolean updateAvailable;
 
     IntentFilterVerificationInfo verificationInfo;
 
@@ -219,6 +221,14 @@
         timeStamp = newStamp;
     }
 
+    public void setUpdateAvailable(boolean updateAvailable) {
+        this.updateAvailable = updateAvailable;
+    }
+
+    public boolean isUpdateAvailable() {
+        return updateAvailable;
+    }
+
     /**
      * Makes a shallow copy of the given package settings.
      *
@@ -268,6 +278,7 @@
         usesStaticLibrariesVersions = orig.usesStaticLibrariesVersions != null
                 ? Arrays.copyOf(orig.usesStaticLibrariesVersions,
                        orig.usesStaticLibrariesVersions.length) : null;
+        updateAvailable = orig.updateAvailable;
     }
 
     private PackageUserState modifyUserState(int userId) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 570b31f..7bd3424 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -2831,6 +2831,9 @@
         if (pkg.parentPackageName != null) {
             serializer.attribute(null, "parentPackageName", pkg.parentPackageName);
         }
+        if (pkg.updateAvailable) {
+            serializer.attribute(null, "updateAvailable", "true");
+        }
 
         writeChildPackagesLPw(serializer, pkg.childPackageNames);
 
@@ -3698,6 +3701,7 @@
         String isOrphaned = null;
         String volumeUuid = null;
         String categoryHintString = null;
+        String updateAvailable = null;
         int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED;
         String uidError = null;
         int pkgFlags = 0;
@@ -3726,6 +3730,7 @@
             primaryCpuAbiString = parser.getAttributeValue(null, "primaryCpuAbi");
             secondaryCpuAbiString = parser.getAttributeValue(null, "secondaryCpuAbi");
             cpuAbiOverrideString = parser.getAttributeValue(null, "cpuAbiOverride");
+            updateAvailable = parser.getAttributeValue(null, "updateAvailable");
 
             if (primaryCpuAbiString == null && legacyCpuAbiString != null) {
                 primaryCpuAbiString = legacyCpuAbiString;
@@ -3905,6 +3910,7 @@
             packageSetting.legacyNativeLibraryPathString = legacyNativeLibraryPathStr;
             packageSetting.primaryCpuAbiString = primaryCpuAbiString;
             packageSetting.secondaryCpuAbiString = secondaryCpuAbiString;
+            packageSetting.updateAvailable = "true".equals(updateAvailable);
             // Handle legacy string here for single-user mode
             final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
             if (enabledStr != null) {
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index 7885748..21fe5ba 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -720,7 +720,11 @@
 
                 // Disable dynamic shortcuts whose target activity is gone.
                 if (si.isDynamic()) {
-                    if (!s.injectIsMainActivity(si.getActivity(), getPackageUserId())) {
+                    if (si.getActivity() == null) {
+                        // Note if it's dynamic, it must have a target activity, but b/36228253.
+                        s.wtf("null activity detected.");
+                        // TODO Maybe remove it?
+                    } else if (!s.injectIsMainActivity(si.getActivity(), getPackageUserId())) {
                         Slog.w(TAG, String.format(
                                 "%s is no longer main activity. Disabling shorcut %s.",
                                 getPackageName(), si.getId()));
@@ -931,6 +935,10 @@
             }
 
             final ComponentName activity = si.getActivity();
+            if (activity == null) {
+                mShortcutUser.mService.wtf("null activity detected.");
+                continue;
+            }
 
             ArrayList<ShortcutInfo> list = activitiesToShortcuts.get(activity);
             if (list == null) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 8998212..ef46bae 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -3237,6 +3237,10 @@
     boolean injectIsMainActivity(@NonNull ComponentName activity, int userId) {
         final long start = injectElapsedRealtime();
         try {
+            if (activity == null) {
+                wtf("null activity detected");
+                return false;
+            }
             if (DUMMY_MAIN_ACTIVITY.equals(activity.getClassName())) {
                 return true;
             }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 627fa54..b9fcf4e 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -94,6 +94,7 @@
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.LocalServices;
+import com.android.server.LockGuard;
 import com.android.server.SystemService;
 import com.android.server.am.UserState;
 import com.android.server.storage.DeviceStorageMonitorInternal;
@@ -227,7 +228,7 @@
     private final Object mPackagesLock;
     private final UserDataPreparer mUserDataPreparer;
     // Short-term lock for internal state, when interaction/sync with PM is not required
-    private final Object mUsersLock = new Object();
+    private final Object mUsersLock = LockGuard.installNewLock(LockGuard.INDEX_USER);
     private final Object mRestrictionsLock = new Object();
 
     private final Handler mHandler;
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 755c486..83dd392 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -432,7 +432,7 @@
         // Ignore framework code.
         // TODO(calin): is there a better way to detect it?
         if (dexPath.startsWith("/system/framework/")) {
-            new DexSearchResult("framework", DEX_SEARCH_NOT_FOUND);
+            return new DexSearchResult("framework", DEX_SEARCH_NOT_FOUND);
         }
 
         // First, check if the package which loads the dex file actually owns it.
diff --git a/services/core/java/com/android/server/policy/AccessibilityShortcutController.java b/services/core/java/com/android/server/policy/AccessibilityShortcutController.java
index eec1fef..cd55f50 100644
--- a/services/core/java/com/android/server/policy/AccessibilityShortcutController.java
+++ b/services/core/java/com/android/server/policy/AccessibilityShortcutController.java
@@ -29,6 +29,7 @@
 import android.media.RingtoneManager;
 import android.os.Handler;
 import android.os.UserHandle;
+import android.os.Vibrator;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Slog;
@@ -48,6 +49,11 @@
  */
 public class AccessibilityShortcutController {
     private static final String TAG = "AccessibilityShortcutController";
+    private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
+            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+            .setUsage(AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY)
+            .build();
+
 
     private final Context mContext;
     private AlertDialog mAlertDialog;
@@ -100,6 +106,8 @@
         final int userId = ActivityManager.getCurrentUser();
         final int dialogAlreadyShown = Settings.Secure.getIntForUser(
                 cr, Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0, userId);
+
+        // Play a notification tone
         final Ringtone tone =
                 RingtoneManager.getRingtone(mContext, Settings.System.DEFAULT_NOTIFICATION_URI);
         if (tone != null) {
@@ -108,6 +116,18 @@
                 .build());
             tone.play();
         }
+
+        // Play a notification vibration
+        Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
+        if ((vibrator != null) && vibrator.hasVibrator()) {
+            // Don't check if haptics are disabled, as we need to alert the user that their
+            // way of interacting with the phone may change if they activate the shortcut
+            long[] vibePattern = PhoneWindowManager.getLongIntArray(mContext.getResources(),
+                    R.array.config_safeModeDisabledVibePattern);
+            vibrator.vibrate(vibePattern, -1, VIBRATION_ATTRIBUTES);
+        }
+
+
         if (dialogAlreadyShown == 0) {
             // The first time, we show a warning rather than toggle the service to give the user a
             // chance to turn off this feature before stuff gets enabled.
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 24f6f89..4f67e8c 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -76,6 +76,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.EventLogTags;
+import com.android.server.LockGuard;
 import com.android.server.RescueParty;
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
@@ -210,7 +211,7 @@
     private DreamManagerInternal mDreamManager;
     private Light mAttentionLight;
 
-    private final Object mLock = new Object();
+    private final Object mLock = LockGuard.installNewLock(LockGuard.INDEX_POWER);
 
     // A bitfield that indicates what parts of the power state have
     // changed and need to be recalculated.
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 8043c65..08eca73 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -17,6 +17,7 @@
 package com.android.server.tv;
 
 import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED;
+import static android.media.tv.TvInputManager.INPUT_STATE_CONNECTED_STANDBY;
 import static android.media.tv.TvInputManager.INPUT_STATE_DISCONNECTED;
 
 import android.content.BroadcastReceiver;
@@ -109,7 +110,6 @@
     private int mCurrentIndex = 0;
     private int mCurrentMaxIndex = 0;
 
-    // TODO: Should handle STANDBY case.
     private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
     private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
 
@@ -209,11 +209,13 @@
                         + deviceId);
                 return;
             }
+            int previousConfigsLength = connection.getConfigsLengthLocked();
             connection.updateConfigsLocked(configs);
             String inputId = mHardwareInputIdMap.get(deviceId);
-            if (inputId != null) {
+            if (inputId != null
+                    && (previousConfigsLength == 0) != (connection.getConfigsLengthLocked() == 0)) {
                 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
-                        convertConnectedToState(configs.length > 0), 0, inputId).sendToTarget();
+                    connection.getInputStateLocked(), 0, inputId).sendToTarget();
             }
             ITvInputHardwareCallback callback = connection.getCallbackLocked();
             if (callback != null) {
@@ -263,14 +265,6 @@
                 || connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId;
     }
 
-    private int convertConnectedToState(boolean connected) {
-        if (connected) {
-            return INPUT_STATE_CONNECTED;
-        } else {
-            return INPUT_STATE_DISCONNECTED;
-        }
-    }
-
     public void addHardwareInput(int deviceId, TvInputInfo info) {
         synchronized (mLock) {
             String oldInputId = mHardwareInputIdMap.get(deviceId);
@@ -293,18 +287,22 @@
                 }
                 String inputId = mHardwareInputIdMap.get(hardwareInfo.getDeviceId());
                 if (inputId != null && inputId.equals(info.getId())) {
-                    mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
-                            convertConnectedToState(mHdmiStateMap.valueAt(i)), 0,
-                            inputId).sendToTarget();
+                    // No HDMI hotplug does not necessarily mean disconnected, as old devices may
+                    // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to
+                    // denote unknown state.
+                    int state = mHdmiStateMap.valueAt(i)
+                            ? INPUT_STATE_CONNECTED
+                            : INPUT_STATE_CONNECTED_STANDBY;
+                    mHandler.obtainMessage(
+                        ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget();
                     return;
                 }
             }
-            // For the rest of the devices, we can tell by the number of available streams.
+            // For the rest of the devices, we can tell by the cable connection status.
             Connection connection = mConnections.get(deviceId);
             if (connection != null) {
                 mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
-                        convertConnectedToState(connection.getConfigsLocked().length > 0), 0,
-                        info.getId()).sendToTarget();
+                    connection.getInputStateLocked(), 0, info.getId()).sendToTarget();
             }
         }
     }
@@ -716,6 +714,26 @@
                     + ", mResolvedUserId: " + mResolvedUserId
                     + " }";
         }
+
+        private int getConfigsLengthLocked() {
+            return mConfigs == null ? 0 : mConfigs.length;
+        }
+
+        private int getInputStateLocked() {
+            int configsLength = getConfigsLengthLocked();
+            if (configsLength > 0) {
+                return INPUT_STATE_CONNECTED;
+            }
+            switch (mHardwareInfo.getCableConnectionStatus()) {
+                case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_CONNECTED:
+                    return INPUT_STATE_CONNECTED;
+                case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_DISCONNECTED:
+                    return INPUT_STATE_DISCONNECTED;
+                case TvInputHardwareInfo.CABLE_CONNECTION_STATUS_UNKNOWN:
+                default:
+                    return INPUT_STATE_CONNECTED_STANDBY;
+            }
+        }
     }
 
     private class TvInputHardwareImpl extends ITvInputHardware.Stub {
@@ -1199,8 +1217,14 @@
                 if (inputId == null) {
                     return;
                 }
-                mHandler.obtainMessage(ListenerHandler.STATE_CHANGED,
-                        convertConnectedToState(event.isConnected()), 0, inputId).sendToTarget();
+                // No HDMI hotplug does not necessarily mean disconnected, as old devices may
+                // not report hotplug state correctly. Using INPUT_STATE_CONNECTED_STANDBY to
+                // denote unknown state.
+                int state = event.isConnected()
+                        ? INPUT_STATE_CONNECTED
+                        : INPUT_STATE_CONNECTED_STANDBY;
+                mHandler.obtainMessage(
+                    ListenerHandler.STATE_CHANGED, state, 0, inputId).sendToTarget();
             }
         }
     }
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index e026130..52763a1 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -288,7 +288,7 @@
                     userState.serviceStateMap.put(component, serviceState);
                     updateServiceConnectionLocked(component, userId);
                 } else {
-                    inputList.addAll(serviceState.hardwareInputList);
+                    inputList.addAll(serviceState.hardwareInputMap.values());
                 }
             } else {
                 try {
@@ -2105,7 +2105,7 @@
         private final ServiceConnection connection;
         private final ComponentName component;
         private final boolean isHardware;
-        private final List<TvInputInfo> hardwareInputList = new ArrayList<>();
+        private final Map<String, TvInputInfo> hardwareInputMap = new HashMap<>();
 
         private ITvInputService service;
         private ServiceCallback callback;
@@ -2216,7 +2216,7 @@
                 }
 
                 if (serviceState.isHardware) {
-                    serviceState.hardwareInputList.clear();
+                    serviceState.hardwareInputMap.clear();
                     for (TvInputHardwareInfo hardware : mTvInputHardwareManager.getHardwareList()) {
                         try {
                             serviceState.service.notifyHardwareAdded(hardware);
@@ -2283,7 +2283,7 @@
 
         private void addHardwareInputLocked(TvInputInfo inputInfo) {
             ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
-            serviceState.hardwareInputList.add(inputInfo);
+            serviceState.hardwareInputMap.put(inputInfo.getId(), inputInfo);
             buildTvInputListLocked(mUserId, null);
         }
 
@@ -2309,15 +2309,7 @@
             ensureHardwarePermission();
             synchronized (mLock) {
                 ServiceState serviceState = getServiceStateLocked(mComponent, mUserId);
-                boolean removed = false;
-                for (Iterator<TvInputInfo> it = serviceState.hardwareInputList.iterator();
-                        it.hasNext(); ) {
-                    if (it.next().getId().equals(inputId)) {
-                        it.remove();
-                        removed = true;
-                        break;
-                    }
-                }
+                boolean removed = serviceState.hardwareInputMap.remove(inputId) != null;
                 if (removed) {
                     buildTvInputListLocked(mUserId, null);
                     mTvInputHardwareManager.removeHardwareInput(inputId);
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 1510dd1..5abc4e4 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -222,6 +222,14 @@
                 || mWindowsForAccessibilityObserver != null);
     }
 
+    /** NOTE: This has to be called within a surface transaction. */
+    public void setForceShowMagnifiableBoundsLocked(boolean show) {
+        if (mDisplayMagnifier != null) {
+            mDisplayMagnifier.setForceShowMagnifiableBoundsLocked(show);
+            mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
+        }
+    }
+
     private static void populateTransformationMatrixLocked(WindowState windowState,
             Matrix outMatrix) {
         sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
@@ -266,6 +274,8 @@
 
         private final long mLongAnimationDuration;
 
+        private boolean mForceShowMagnifiableBounds = false;
+
         public DisplayMagnifier(WindowManagerService windowManagerService,
                 MagnificationCallbacks callbacks) {
             mContext = windowManagerService.mContext;
@@ -283,6 +293,15 @@
             mWindowManagerService.scheduleAnimationLocked();
         }
 
+        public void setForceShowMagnifiableBoundsLocked(boolean show) {
+            mForceShowMagnifiableBounds = show;
+            mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true);
+        }
+
+        public boolean isForceShowingMagnifiableBoundsLocked() {
+            return mForceShowMagnifiableBounds;
+        }
+
         public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
             if (DEBUG_RECTANGLE_REQUESTED) {
                 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
@@ -488,7 +507,8 @@
                 // to show the border. We will do so when the pending message is handled.
                 if (!mHandler.hasMessages(
                         MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
-                    setMagnifiedRegionBorderShownLocked(isMagnifyingLocked(), true);
+                    setMagnifiedRegionBorderShownLocked(
+                            isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true);
                 }
             }
 
@@ -600,11 +620,11 @@
             }
 
             public void onRotationChangedLocked() {
-                // If we are magnifying, hide the magnified border window immediately so
+                // If we are showing the magnification border, hide it immediately so
                 // the user does not see strange artifacts during rotation. The screenshot
-                // used for rotation has already the border. After the rotation is complete
+                // used for rotation already has the border. After the rotation is complete
                 // we will show the border.
-                if (isMagnifyingLocked()) {
+                if (isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked()) {
                     setMagnifiedRegionBorderShownLocked(false, false);
                     final long delay = (long) (mLongAnimationDuration
                             * mWindowManagerService.getWindowAnimationScaleLocked());
@@ -926,7 +946,8 @@
 
                     case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
                         synchronized (mWindowManagerService.mWindowMap) {
-                            if (mMagnifedViewport.isMagnifyingLocked()) {
+                            if (mMagnifedViewport.isMagnifyingLocked()
+                                    || isForceShowingMagnifiableBoundsLocked()) {
                                 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
                                 mWindowManagerService.scheduleAnimationLocked();
                             }
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 4191d31..7a36da2 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -44,6 +44,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
 
@@ -692,7 +693,7 @@
     private void getDefaultNextAppTransitionStartRect(Rect rect) {
         if (mDefaultNextAppTransitionAnimationSpec == null ||
                 mDefaultNextAppTransitionAnimationSpec.rect == null) {
-            Slog.wtf(TAG, "Starting rect for app requested, but none available", new Throwable());
+            Slog.e(TAG, "Starting rect for app requested, but none available", new Throwable());
             rect.setEmpty();
         } else {
             rect.set(mDefaultNextAppTransitionAnimationSpec.rect);
@@ -705,7 +706,7 @@
             spec = mDefaultNextAppTransitionAnimationSpec;
         }
         if (spec == null || spec.rect == null) {
-            Slog.wtf(TAG, "Starting rect for task: " + taskId + " requested, but not available",
+            Slog.e(TAG, "Starting rect for task: " + taskId + " requested, but not available",
                     new Throwable());
             rect.setEmpty();
         } else {
@@ -1652,10 +1653,14 @@
     }
 
     int getAppStackClipMode() {
+        // When dismiss keyguard animation occurs, clip before the animation to prevent docked
+        // app from showing beyond the divider
+        if (mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY
+                || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER) {
+            return STACK_CLIP_BEFORE_ANIM;
+        }
         return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH
                 || mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS
-                || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY
-                || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER
                 || mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL
                 ? STACK_CLIP_NONE
                 : STACK_CLIP_AFTER_ANIM;
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 266ab4c..b90a82a 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -59,6 +59,15 @@
     private final IApplicationToken mToken;
     private final Handler mHandler;
 
+    private final Runnable mOnStartingWindowDrawn = () -> {
+        if (mListener == null) {
+            return;
+        }
+        if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in "
+                + AppWindowContainerController.this.mToken);
+        mListener.onStartingWindowDrawn();
+    };
+
     private final Runnable mOnWindowsDrawn = () -> {
         if (mListener == null) {
             return;
@@ -655,6 +664,9 @@
         }
     }
 
+    void reportStartingWindowDrawn() {
+        mHandler.post(mOnStartingWindowDrawn);
+    }
 
     void reportWindowsDrawn() {
         mHandler.post(mOnWindowsDrawn);
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerListener.java b/services/core/java/com/android/server/wm/AppWindowContainerListener.java
index 12d4b2f..9d459cf 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerListener.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerListener.java
@@ -24,6 +24,12 @@
     void onWindowsVisible();
     /** Called when the windows associated app window container are no longer visible. */
     void onWindowsGone();
+
+    /**
+     * Called when the starting window for this container is drawn.
+     */
+    void onStartingWindowDrawn();
+
     /**
      * Called when the key dispatching to a window associated with the app window container
      * timed-out.
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 4aa013a..c20ee97 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -42,7 +42,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
-import static com.android.server.wm.WindowManagerService.H.NOTIFY_STARTING_WINDOW_DRAWN;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.logWithStack;
@@ -164,9 +163,6 @@
     private boolean mLastContainsShowWhenLockedWindow;
     private boolean mLastContainsDismissKeyguardWindow;
 
-    private ArrayList<WindowSurfaceController.SurfaceControlWithBackground> mSurfaceViewBackgrounds =
-        new ArrayList<>();
-
     ArrayDeque<Rect> mFrozenBounds = new ArrayDeque<>();
     ArrayDeque<Configuration> mFrozenMergedConfig = new ArrayDeque<>();
 
@@ -668,6 +664,15 @@
         return (Task) getParent();
     }
 
+    TaskStack getStack() {
+        final Task task = getTask();
+        if (task != null) {
+            return task.mStack;
+        } else {
+            return null;
+        }
+    }
+
     @Override
     void onParentSet() {
         super.onParentSet();
@@ -962,36 +967,6 @@
         mService.mWindowPlacerLocked.performSurfacePlacement();
     }
 
-    void addSurfaceViewBackground(WindowSurfaceController.SurfaceControlWithBackground background) {
-        mSurfaceViewBackgrounds.add(background);
-    }
-
-    void removeSurfaceViewBackground(WindowSurfaceController.SurfaceControlWithBackground background) {
-        mSurfaceViewBackgrounds.remove(background);
-        updateSurfaceViewBackgroundVisibilities();
-    }
-
-    // We use DimLayers behind SurfaceViews to prevent holes while resizing and creating.
-    // However, we need to ensure one SurfaceView doesn't cover another when they are both placed
-    // below the main app window (as traditionally a SurfaceView which is never drawn
-    // to is totally translucent). So we look at all our SurfaceView backgrounds and only enable
-    // the background for the SurfaceView with lowest Z order
-    void updateSurfaceViewBackgroundVisibilities() {
-        WindowSurfaceController.SurfaceControlWithBackground bottom = null;
-        int bottomLayer = Integer.MAX_VALUE;
-        for (int i = 0; i < mSurfaceViewBackgrounds.size(); i++) {
-            WindowSurfaceController.SurfaceControlWithBackground sc = mSurfaceViewBackgrounds.get(i);
-            if (sc.mVisible && sc.mLayer < bottomLayer) {
-                bottomLayer = sc.mLayer;
-                bottom = sc;
-            }
-        }
-        for (int i = 0; i < mSurfaceViewBackgrounds.size(); i++) {
-            WindowSurfaceController.SurfaceControlWithBackground sc = mSurfaceViewBackgrounds.get(i);
-            sc.updateBackgroundVisibility(sc != bottom);
-        }
-    }
-
     void resetJustMovedInStack() {
         for (int i = mChildren.size() - 1; i >= 0; i--) {
             (mChildren.get(i)).resetJustMovedInStack();
@@ -1329,7 +1304,9 @@
                     }
                 }
             } else if (w.isDrawnLw()) {
-                mService.mH.sendEmptyMessage(NOTIFY_STARTING_WINDOW_DRAWN);
+                if (getController() != null) {
+                    getController().reportStartingWindowDrawn();
+                }
                 startingDisplayed = true;
             }
         }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index cb3a663..e5b00f3 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -73,18 +73,24 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
+import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_STACK_CRAWLS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.CUSTOM_SCREEN_ROTATION;
 import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
 import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER;
 import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT;
 import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
+import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
+import static com.android.server.wm.WindowManagerService.SEAMLESS_ROTATION_TIMEOUT_DURATION;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
 import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
+import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_ACTIVE;
 import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
+import static com.android.server.wm.WindowManagerService.WINDOW_FREEZE_TIMEOUT_DURATION;
 import static com.android.server.wm.WindowManagerService.dipToPixel;
 import static com.android.server.wm.WindowManagerService.logSurface;
 import static com.android.server.wm.WindowState.RESIZE_HANDLE_WIDTH_IN_DP;
@@ -94,6 +100,7 @@
 
 import android.annotation.NonNull;
 import android.app.ActivityManager.StackId;
+import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.GraphicBuffer;
@@ -113,6 +120,7 @@
 import android.util.Slog;
 import android.view.Display;
 import android.view.DisplayInfo;
+import android.view.InputDevice;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.WindowManagerPolicy;
@@ -181,12 +189,27 @@
     private final DisplayInfo mDisplayInfo = new DisplayInfo();
     private final Display mDisplay;
     private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+    /**
+     * For default display it contains real metrics, empty for others.
+     * @see WindowManagerService#createWatermarkInTransaction()
+     */
+    final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics();
+    /** @see #computeCompatSmallestWidth(boolean, int, int, int, int) */
+    private final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics();
+    /**
+     * Compat metrics computed based on {@link #mDisplayMetrics}.
+     * @see #updateDisplayAndOrientation(int)
+     */
+    private final DisplayMetrics mCompatDisplayMetrics = new DisplayMetrics();
+
+    /** The desired scaling factor for compatible apps. */
+    float mCompatibleScreenScale;
 
     /**
      * Current rotation of the display.
      * Constants as per {@link android.view.Surface.Rotation}.
      *
-     * @see WindowManagerService#updateRotationUncheckedLocked(boolean, int)
+     * @see #updateRotationUnchecked(boolean)
      */
     private int mRotation = 0;
     /**
@@ -200,7 +223,7 @@
      * Flag indicating that the application is receiving an orientation that has different metrics
      * than it expected. E.g. Portrait instead of Landscape.
      *
-     * @see WindowManagerService#updateRotationUncheckedLocked(boolean, int)
+     * @see #updateRotationUnchecked(boolean)
      */
     private boolean mAltOrientation = false;
     /**
@@ -218,7 +241,7 @@
      */
     private int mLastKeyguardForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
 
-    Rect mBaseDisplayRect = new Rect();
+    private Rect mBaseDisplayRect = new Rect();
     private Rect mContentRect = new Rect();
 
     // Accessed directly by all users.
@@ -828,6 +851,514 @@
         return mLastWindowForcedOrientation;
     }
 
+    /**
+     * Update rotation of the display.
+     *
+     * Returns true if the rotation has been changed.  In this case YOU MUST CALL
+     * {@link WindowManagerService#sendNewConfiguration(int)} TO UNFREEZE THE SCREEN.
+     */
+    boolean updateRotationUnchecked(boolean inTransaction) {
+        if (mService.mDeferredRotationPauseCount > 0) {
+            // Rotation updates have been paused temporarily.  Defer the update until
+            // updates have been resumed.
+            if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, rotation is paused.");
+            return false;
+        }
+
+        ScreenRotationAnimation screenRotationAnimation =
+                mService.mAnimator.getScreenRotationAnimationLocked(mDisplayId);
+        if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
+            // Rotation updates cannot be performed while the previous rotation change
+            // animation is still in progress.  Skip this update.  We will try updating
+            // again after the animation is finished and the display is unfrozen.
+            if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, animation in progress.");
+            return false;
+        }
+        if (mService.mDisplayFrozen) {
+            // Even if the screen rotation animation has finished (e.g. isAnimating
+            // returns false), there is still some time where we haven't yet unfrozen
+            // the display. We also need to abort rotation here.
+            if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
+                    "Deferring rotation, still finishing previous rotation");
+            return false;
+        }
+
+        if (!mService.mDisplayEnabled) {
+            // No point choosing a rotation if the display is not enabled.
+            if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, display is not enabled.");
+            return false;
+        }
+
+        final int oldRotation = mRotation;
+        final int lastOrientation = mLastOrientation;
+        final boolean oldAltOrientation = mAltOrientation;
+        int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation);
+        final boolean rotateSeamlessly;
+
+        if (mService.mPolicy.shouldRotateSeamlessly(oldRotation, rotation)) {
+            final WindowState seamlessRotated = getWindow((w) -> w.mSeamlesslyRotated);
+            if (seamlessRotated != null) {
+                // We can't rotate (seamlessly or not) while waiting for the last seamless rotation
+                // to complete (that is, waiting for windows to redraw). It's tempting to check
+                // w.mSeamlessRotationCount but that could be incorrect in the case of
+                // window-removal.
+                return false;
+            }
+
+            final WindowState cantSeamlesslyRotate = getWindow((w) ->
+                    w.isChildWindow() && w.isVisibleNow()
+                            && !w.mWinAnimator.mSurfaceController.getTransformToDisplayInverse());
+            if (cantSeamlesslyRotate != null) {
+                // In what can only be called an unfortunate workaround we require seamlessly
+                // rotated child windows to have the TRANSFORM_TO_DISPLAY_INVERSE flag. Due to
+                // limitations in the client API, there is no way for the client to set this flag in
+                // a race free fashion. If we seamlessly rotate a window which does not have this
+                // flag, but then gains it, we will get an incorrect visual result
+                // (rotated viewfinder). This means if we want to support seamlessly rotating
+                // windows which could gain this flag, we can't rotate windows without it. This
+                // limits seamless rotation in N to camera framework users, windows without
+                // children, and native code. This is unfortunate but having the camera work is our
+                // primary goal.
+                rotateSeamlessly = false;
+            } else {
+                rotateSeamlessly = true;
+            }
+        } else {
+            rotateSeamlessly = false;
+        }
+
+        // TODO: Implement forced rotation changes.
+        //       Set mAltOrientation to indicate that the application is receiving
+        //       an orientation that has different metrics than it expected.
+        //       eg. Portrait instead of Landscape.
+
+        final boolean altOrientation = !mService.mPolicy.rotationHasCompatibleMetricsLw(
+                lastOrientation, rotation);
+
+        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Selected orientation " + lastOrientation
+                + ", got rotation " + rotation + " which has "
+                + (altOrientation ? "incompatible" : "compatible") + " metrics");
+
+        if (oldRotation == rotation && oldAltOrientation == altOrientation) {
+            // No change.
+            return false;
+        }
+
+        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Rotation changed to " + rotation
+                + (altOrientation ? " (alt)" : "") + " from " + oldRotation
+                + (oldAltOrientation ? " (alt)" : "") + ", lastOrientation=" + lastOrientation);
+
+        if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) {
+            mService.mWaitingForConfig = true;
+        }
+
+        mRotation = rotation;
+        mAltOrientation = altOrientation;
+        if (isDefaultDisplay) {
+            mService.mPolicy.setRotationLw(rotation);
+        }
+
+        mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
+        mService.mH.removeMessages(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT);
+        mService.mH.sendEmptyMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
+                WINDOW_FREEZE_TIMEOUT_DURATION);
+
+        setLayoutNeeded();
+        final int[] anim = new int[2];
+        if (isDimming()) {
+            anim[0] = anim[1] = 0;
+        } else {
+            mService.mPolicy.selectRotationAnimationLw(anim);
+        }
+
+        if (!rotateSeamlessly) {
+            mService.startFreezingDisplayLocked(inTransaction, anim[0], anim[1]);
+            // startFreezingDisplayLocked can reset the ScreenRotationAnimation.
+            screenRotationAnimation = mService.mAnimator.getScreenRotationAnimationLocked(
+                    mDisplayId);
+        } else {
+            // The screen rotation animation uses a screenshot to freeze the screen
+            // while windows resize underneath.
+            // When we are rotating seamlessly, we allow the elements to transition
+            // to their rotated state independently and without a freeze required.
+            screenRotationAnimation = null;
+
+            // We have to reset this in case a window was removed before it
+            // finished seamless rotation.
+            mService.mSeamlessRotationCount = 0;
+        }
+
+        // We need to update our screen size information to match the new rotation. If the rotation
+        // has actually changed then this method will return true and, according to the comment at
+        // the top of the method, the caller is obligated to call computeNewConfigurationLocked().
+        // By updating the Display info here it will be available to
+        // #computeScreenConfiguration() later.
+        updateDisplayAndOrientation(getConfiguration().uiMode);
+
+        if (!inTransaction) {
+            if (SHOW_TRANSACTIONS) {
+                Slog.i(TAG_WM, ">>> OPEN TRANSACTION setRotationUnchecked");
+            }
+            mService.openSurfaceTransaction();
+        }
+        try {
+            // NOTE: We disable the rotation in the emulator because
+            //       it doesn't support hardware OpenGL emulation yet.
+            if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
+                    && screenRotationAnimation.hasScreenshot()) {
+                if (screenRotationAnimation.setRotationInTransaction(
+                        rotation, mService.mFxSession,
+                        MAX_ANIMATION_DURATION, mService.getTransitionAnimationScaleLocked(),
+                        mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight)) {
+                    mService.scheduleAnimationLocked();
+                }
+            }
+
+            if (rotateSeamlessly) {
+                forAllWindows(w -> {
+                    w.mWinAnimator.seamlesslyRotateWindow(oldRotation, rotation);
+                }, true /* traverseTopToBottom */);
+            }
+
+            mService.mDisplayManagerInternal.performTraversalInTransactionFromWindowManager();
+        } finally {
+            if (!inTransaction) {
+                mService.closeSurfaceTransaction();
+                if (SHOW_LIGHT_TRANSACTIONS) {
+                    Slog.i(TAG_WM, "<<< CLOSE TRANSACTION setRotationUnchecked");
+                }
+            }
+        }
+
+        forAllWindows(w -> {
+            // Discard surface after orientation change, these can't be reused.
+            if (w.mAppToken != null) {
+                w.mAppToken.destroySavedSurfaces();
+            }
+            if (w.mHasSurface && !rotateSeamlessly) {
+                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Set mOrientationChanging of " + w);
+                w.mOrientationChanging = true;
+                mService.mRoot.mOrientationChangeComplete = false;
+                w.mLastFreezeDuration = 0;
+            }
+            w.mReportOrientationChanged = true;
+        }, true /* traverseTopToBottom */);
+
+        if (rotateSeamlessly) {
+            mService.mH.removeMessages(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT);
+            mService.mH.sendEmptyMessageDelayed(WindowManagerService.H.SEAMLESS_ROTATION_TIMEOUT,
+                    SEAMLESS_ROTATION_TIMEOUT_DURATION);
+        }
+
+        for (int i = mService.mRotationWatchers.size() - 1; i >= 0; i--) {
+            final WindowManagerService.RotationWatcher rotationWatcher
+                    = mService.mRotationWatchers.get(i);
+            if (rotationWatcher.mDisplayId == mDisplayId) {
+                try {
+                    rotationWatcher.mWatcher.onRotationChanged(rotation);
+                } catch (RemoteException e) {
+                    // Ignore
+                }
+            }
+        }
+
+        // TODO (multi-display): Magnification is supported only for the default display.
+        // Announce rotation only if we will not animate as we already have the
+        // windows in final state. Otherwise, we make this call at the rotation end.
+        if (screenRotationAnimation == null && mService.mAccessibilityController != null
+                && isDefaultDisplay) {
+            mService.mAccessibilityController.onRotationChangedLocked(this);
+        }
+
+        return true;
+    }
+
+    /**
+     * Update {@link #mDisplayInfo} and other internal variables when display is rotated or config
+     * changed.
+     * Do not call if {@link WindowManagerService#mDisplayReady} == false.
+     */
+    private DisplayInfo updateDisplayAndOrientation(int uiMode) {
+        // Use the effective "visual" dimensions based on current rotation
+        final boolean rotated = (mRotation == ROTATION_90 || mRotation == ROTATION_270);
+        final int realdw = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;
+        final int realdh = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;
+        int dw = realdw;
+        int dh = realdh;
+
+        if (mAltOrientation) {
+            if (realdw > realdh) {
+                // Turn landscape into portrait.
+                int maxw = (int)(realdh/1.3f);
+                if (maxw < realdw) {
+                    dw = maxw;
+                }
+            } else {
+                // Turn portrait into landscape.
+                int maxh = (int)(realdw/1.3f);
+                if (maxh < realdh) {
+                    dh = maxh;
+                }
+            }
+        }
+
+        // Update application display metrics.
+        final int appWidth = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation, uiMode,
+                mDisplayId);
+        final int appHeight = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation, uiMode,
+                mDisplayId);
+        mDisplayInfo.rotation = mRotation;
+        mDisplayInfo.logicalWidth = dw;
+        mDisplayInfo.logicalHeight = dh;
+        mDisplayInfo.logicalDensityDpi = mBaseDisplayDensity;
+        mDisplayInfo.appWidth = appWidth;
+        mDisplayInfo.appHeight = appHeight;
+        if (isDefaultDisplay) {
+            mDisplayInfo.getLogicalMetrics(mRealDisplayMetrics,
+                    CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
+        }
+        mDisplayInfo.getAppMetrics(mDisplayMetrics);
+        if (mDisplayScalingDisabled) {
+            mDisplayInfo.flags |= Display.FLAG_SCALING_DISABLED;
+        } else {
+            mDisplayInfo.flags &= ~Display.FLAG_SCALING_DISABLED;
+        }
+
+        mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
+                mDisplayInfo);
+
+        mBaseDisplayRect.set(0, 0, dw, dh);
+
+        if (isDefaultDisplay) {
+            mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(mDisplayMetrics,
+                    mCompatDisplayMetrics);
+        }
+        return mDisplayInfo;
+    }
+
+    /**
+     * Compute display configuration based on display properties and policy settings.
+     * Do not call if mDisplayReady == false.
+     */
+    void computeScreenConfiguration(Configuration config) {
+        final DisplayInfo displayInfo = updateDisplayAndOrientation(config.uiMode);
+
+        final int dw = displayInfo.logicalWidth;
+        final int dh = displayInfo.logicalHeight;
+        config.orientation = (dw <= dh) ? Configuration.ORIENTATION_PORTRAIT :
+                Configuration.ORIENTATION_LANDSCAPE;
+        config.screenWidthDp =
+                (int)(mService.mPolicy.getConfigDisplayWidth(dw, dh, displayInfo.rotation,
+                        config.uiMode, mDisplayId) / mDisplayMetrics.density);
+        config.screenHeightDp =
+                (int)(mService.mPolicy.getConfigDisplayHeight(dw, dh, displayInfo.rotation,
+                        config.uiMode, mDisplayId) / mDisplayMetrics.density);
+        final boolean rotated = (displayInfo.rotation == Surface.ROTATION_90
+                || displayInfo.rotation == Surface.ROTATION_270);
+
+        computeSizeRangesAndScreenLayout(displayInfo, mDisplayId, rotated, config.uiMode, dw, dh,
+                mDisplayMetrics.density, config);
+
+        config.screenLayout = (config.screenLayout & ~Configuration.SCREENLAYOUT_ROUND_MASK)
+                | ((displayInfo.flags & Display.FLAG_ROUND) != 0
+                ? Configuration.SCREENLAYOUT_ROUND_YES
+                : Configuration.SCREENLAYOUT_ROUND_NO);
+
+        config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale);
+        config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale);
+        config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, config.uiMode, dw,
+                dh, mDisplayId);
+        config.densityDpi = displayInfo.logicalDensityDpi;
+
+        config.colorMode =
+                (displayInfo.isHdr()
+                        ? Configuration.COLOR_MODE_HDR_YES
+                        : Configuration.COLOR_MODE_HDR_NO)
+                        | (displayInfo.isWideColorGamut()
+                        ? Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES
+                        : Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_NO);
+
+        // Update the configuration based on available input devices, lid switch,
+        // and platform configuration.
+        config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
+        config.keyboard = Configuration.KEYBOARD_NOKEYS;
+        config.navigation = Configuration.NAVIGATION_NONAV;
+
+        int keyboardPresence = 0;
+        int navigationPresence = 0;
+        final InputDevice[] devices = mService.mInputManager.getInputDevices();
+        final int len = devices != null ? devices.length : 0;
+        for (int i = 0; i < len; i++) {
+            InputDevice device = devices[i];
+            if (!device.isVirtual()) {
+                final int sources = device.getSources();
+                final int presenceFlag = device.isExternal() ?
+                        WindowManagerPolicy.PRESENCE_EXTERNAL :
+                        WindowManagerPolicy.PRESENCE_INTERNAL;
+
+                // TODO(multi-display): Configure on per-display basis.
+                if (mService.mIsTouchDevice) {
+                    if ((sources & InputDevice.SOURCE_TOUCHSCREEN) ==
+                            InputDevice.SOURCE_TOUCHSCREEN) {
+                        config.touchscreen = Configuration.TOUCHSCREEN_FINGER;
+                    }
+                } else {
+                    config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
+                }
+
+                if ((sources & InputDevice.SOURCE_TRACKBALL) == InputDevice.SOURCE_TRACKBALL) {
+                    config.navigation = Configuration.NAVIGATION_TRACKBALL;
+                    navigationPresence |= presenceFlag;
+                } else if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD
+                        && config.navigation == Configuration.NAVIGATION_NONAV) {
+                    config.navigation = Configuration.NAVIGATION_DPAD;
+                    navigationPresence |= presenceFlag;
+                }
+
+                if (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
+                    config.keyboard = Configuration.KEYBOARD_QWERTY;
+                    keyboardPresence |= presenceFlag;
+                }
+            }
+        }
+
+        if (config.navigation == Configuration.NAVIGATION_NONAV && mService.mHasPermanentDpad) {
+            config.navigation = Configuration.NAVIGATION_DPAD;
+            navigationPresence |= WindowManagerPolicy.PRESENCE_INTERNAL;
+        }
+
+        // Determine whether a hard keyboard is available and enabled.
+        // TODO(multi-display): Should the hardware keyboard be tied to a display or to a device?
+        boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
+        if (hardKeyboardAvailable != mService.mHardKeyboardAvailable) {
+            mService.mHardKeyboardAvailable = hardKeyboardAvailable;
+            mService.mH.removeMessages(WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
+            mService.mH.sendEmptyMessage(WindowManagerService.H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
+        }
+
+        // Let the policy update hidden states.
+        config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
+        config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
+        config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO;
+        mService.mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
+    }
+
+    private int computeCompatSmallestWidth(boolean rotated, int uiMode, int dw, int dh,
+            int displayId) {
+        mTmpDisplayMetrics.setTo(mDisplayMetrics);
+        final DisplayMetrics tmpDm = mTmpDisplayMetrics;
+        final int unrotDw, unrotDh;
+        if (rotated) {
+            unrotDw = dh;
+            unrotDh = dw;
+        } else {
+            unrotDw = dw;
+            unrotDh = dh;
+        }
+        int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw, unrotDh,
+                displayId);
+        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh, unrotDw,
+                displayId);
+        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw, unrotDh,
+                displayId);
+        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh, unrotDw,
+                displayId);
+        return sw;
+    }
+
+    private int reduceCompatConfigWidthSize(int curSize, int rotation, int uiMode,
+            DisplayMetrics dm, int dw, int dh, int displayId) {
+        dm.noncompatWidthPixels = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
+                displayId);
+        dm.noncompatHeightPixels = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, rotation,
+                uiMode, displayId);
+        float scale = CompatibilityInfo.computeCompatibleScaling(dm, null);
+        int size = (int)(((dm.noncompatWidthPixels / scale) / dm.density) + .5f);
+        if (curSize == 0 || size < curSize) {
+            curSize = size;
+        }
+        return curSize;
+    }
+
+    private void computeSizeRangesAndScreenLayout(DisplayInfo displayInfo, int displayId,
+            boolean rotated, int uiMode, int dw, int dh, float density, Configuration outConfig) {
+
+        // We need to determine the smallest width that will occur under normal
+        // operation.  To this, start with the base screen size and compute the
+        // width under the different possible rotations.  We need to un-rotate
+        // the current screen dimensions before doing this.
+        int unrotDw, unrotDh;
+        if (rotated) {
+            unrotDw = dh;
+            unrotDh = dw;
+        } else {
+            unrotDw = dw;
+            unrotDh = dh;
+        }
+        displayInfo.smallestNominalAppWidth = 1<<30;
+        displayInfo.smallestNominalAppHeight = 1<<30;
+        displayInfo.largestNominalAppWidth = 0;
+        displayInfo.largestNominalAppHeight = 0;
+        adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_0, uiMode, unrotDw,
+                unrotDh);
+        adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_90, uiMode, unrotDh,
+                unrotDw);
+        adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_180, uiMode, unrotDw,
+                unrotDh);
+        adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_270, uiMode, unrotDh,
+                unrotDw);
+        int sl = Configuration.resetScreenLayout(outConfig.screenLayout);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode,
+                displayId);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode,
+                displayId);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode,
+                displayId);
+        sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode,
+                displayId);
+        outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density);
+        outConfig.screenLayout = sl;
+    }
+
+    private int reduceConfigLayout(int curLayout, int rotation, float density, int dw, int dh,
+            int uiMode, int displayId) {
+        // Get the app screen size at this rotation.
+        int w = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayId);
+        int h = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, displayId);
+
+        // Compute the screen layout size class for this rotation.
+        int longSize = w;
+        int shortSize = h;
+        if (longSize < shortSize) {
+            int tmp = longSize;
+            longSize = shortSize;
+            shortSize = tmp;
+        }
+        longSize = (int)(longSize/density);
+        shortSize = (int)(shortSize/density);
+        return Configuration.reduceScreenLayout(curLayout, longSize, shortSize);
+    }
+
+    private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int displayId, int rotation,
+            int uiMode, int dw, int dh) {
+        final int width = mService.mPolicy.getConfigDisplayWidth(dw, dh, rotation, uiMode,
+                displayId);
+        if (width < displayInfo.smallestNominalAppWidth) {
+            displayInfo.smallestNominalAppWidth = width;
+        }
+        if (width > displayInfo.largestNominalAppWidth) {
+            displayInfo.largestNominalAppWidth = width;
+        }
+        final int height = mService.mPolicy.getConfigDisplayHeight(dw, dh, rotation, uiMode,
+                displayId);
+        if (height < displayInfo.smallestNominalAppHeight) {
+            displayInfo.smallestNominalAppHeight = height;
+        }
+        if (height > displayInfo.largestNominalAppHeight) {
+            displayInfo.largestNominalAppHeight = height;
+        }
+    }
+
     DockedStackDividerController getDockedDividerController() {
         return mDividerControllerLocked;
     }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 0222b3d..68d0f24 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -55,13 +55,11 @@
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEEP_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
@@ -740,7 +738,7 @@
             if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation");
             // TODO(multi-display): Update rotation for different displays separately.
             final int displayId = defaultDisplay.getDisplayId();
-            if (mService.updateRotationUncheckedLocked(false, displayId)) {
+            if (defaultDisplay.updateRotationUnchecked(false /* inTransaction */)) {
                 mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
             } else {
                 mUpdateRotation = false;
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b7a9e66..4df513e 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -208,13 +208,6 @@
     }
 
     @Override
-    public void repositionChild(IWindow window, int left, int top, int right, int bottom,
-            long deferTransactionUntilFrame, Rect outFrame) {
-        mService.repositionChild(this, window, left, top, right, bottom,
-                deferTransactionUntilFrame, outFrame);
-    }
-
-    @Override
     public void prepareToReplaceWindows(IBinder appToken, boolean childrenOnly) {
         mService.setWillReplaceWindows(appToken, childrenOnly);
     }
@@ -235,10 +228,6 @@
         return res;
     }
 
-    public void performDeferredDestroy(IWindow window) {
-        mService.performDeferredDestroyWindow(this, window);
-    }
-
     public boolean outOfMemory(IWindow window) {
         return mService.outOfMemoryWindow(this, window);
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 47c62f9..5551afe 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -111,7 +111,6 @@
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
@@ -213,6 +212,7 @@
 import com.android.server.EventLogTags;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
+import com.android.server.LockGuard;
 import com.android.server.UiThread;
 import com.android.server.Watchdog;
 import com.android.server.input.InputManagerService;
@@ -532,13 +532,17 @@
     }
 
     class RotationWatcher {
-        IRotationWatcher watcher;
-        IBinder.DeathRecipient deathRecipient;
-        RotationWatcher(IRotationWatcher w, IBinder.DeathRecipient d) {
-            watcher = w;
-            deathRecipient = d;
+        IRotationWatcher mWatcher;
+        IBinder.DeathRecipient mDeathRecipient;
+        int mDisplayId;
+        RotationWatcher(IRotationWatcher watcher, IBinder.DeathRecipient deathRecipient,
+                int displayId) {
+            mWatcher = watcher;
+            mDeathRecipient = deathRecipient;
+            mDisplayId = displayId;
         }
     }
+
     ArrayList<RotationWatcher> mRotationWatchers = new ArrayList<>();
     int mDeferredRotationPauseCount;
 
@@ -587,11 +591,6 @@
 
     boolean mIsTouchDevice;
 
-    final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
-    final DisplayMetrics mRealDisplayMetrics = new DisplayMetrics();
-    final DisplayMetrics mTmpDisplayMetrics = new DisplayMetrics();
-    final DisplayMetrics mCompatDisplayMetrics = new DisplayMetrics();
-
     final H mH = new H();
 
     final Choreographer mChoreographer = Choreographer.getInstance();
@@ -836,9 +835,6 @@
 
     final Configuration mTempConfiguration = new Configuration();
 
-    // The desired scaling factor for compatible apps.
-    float mCompatibleScreenScale;
-
     // If true, only the core apps and services are being launched because the device
     // is in a special boot mode, such as being encrypted or waiting for a decryption password.
     // For example, when this flag is true, there will be no wallpaper service.
@@ -936,6 +932,7 @@
     private WindowManagerService(Context context, InputManagerService inputManager,
             boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
             WindowManagerPolicy policy) {
+        LockGuard.installLock(this, LockGuard.INDEX_WINDOW);
         mRoot = new RootWindowContainer(this);
         mContext = context;
         mHaveInputMethods = haveInputMethods;
@@ -1811,64 +1808,6 @@
         }
     }
 
-    void repositionChild(Session session, IWindow client,
-            int left, int top, int right, int bottom,
-            long frameNumber, Rect outFrame) {
-        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "repositionChild");
-        long origId = Binder.clearCallingIdentity();
-
-        try {
-            synchronized(mWindowMap) {
-                WindowState win = windowForClientLocked(session, client, false);
-                if (win == null) {
-                    return;
-                }
-                if (!win.isChildWindow()) {
-                    throw new IllegalArgumentException(
-                            "repositionChild called but window is not"
-                            + "attached to a parent win=" + win);
-                }
-
-                win.mAttrs.x = left;
-                win.mAttrs.y = top;
-                win.mAttrs.width = right - left;
-                win.mAttrs.height = bottom - top;
-                win.setWindowScale(win.mRequestedWidth, win.mRequestedHeight);
-
-                if (win.mHasSurface) {
-                    if (SHOW_TRANSACTIONS) {
-                        Slog.i(TAG_WM, ">>> OPEN TRANSACTION repositionChild");
-                    }
-
-                    openSurfaceTransaction();
-
-                    try {
-
-                        win.applyGravityAndUpdateFrame(win.mContainingFrame, win.mDisplayFrame);
-                        win.mWinAnimator.computeShownFrameLocked();
-
-                        win.mWinAnimator.setSurfaceBoundariesLocked(false);
-
-                        if (frameNumber > 0) {
-                            win.mWinAnimator.deferTransactionUntilParentFrame(frameNumber);
-                        }
-
-                    } finally {
-                        closeSurfaceTransaction();
-                        if (SHOW_TRANSACTIONS) {
-                            Slog.i(TAG_WM, "<<< CLOSE TRANSACTION repositionChild");
-                        }
-                    }
-                }
-
-                outFrame = win.mCompatFrame;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
-        }
-    }
-
     public int relayoutWindow(Session session, IWindow client, int seq,
             WindowManager.LayoutParams attrs, int requestedWidth,
             int requestedHeight, int viewVisibility, int flags,
@@ -2213,23 +2152,6 @@
         return result;
     }
 
-    public void performDeferredDestroyWindow(Session session, IWindow client) {
-        long origId = Binder.clearCallingIdentity();
-
-        try {
-            synchronized (mWindowMap) {
-                WindowState win = windowForClientLocked(session, client, false);
-                if (win == null || win.mWillReplaceWindow) {
-                    return;
-                }
-
-                win.mWinAnimator.destroyDeferredSurfaceLocked();
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
     public boolean outOfMemoryWindow(Session session, IWindow client) {
         final long origId = Binder.clearCallingIdentity();
 
@@ -2455,10 +2377,10 @@
             // to keep override configs clear of non-empty values (e.g. fontSize).
             mTempConfiguration.unset();
             mTempConfiguration.updateFrom(currentConfig);
-            computeScreenConfigurationLocked(mTempConfiguration, displayId);
+            final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+            displayContent.computeScreenConfiguration(mTempConfiguration);
             if (currentConfig.diff(mTempConfiguration) != 0) {
                 mWaitingForConfig = true;
-                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
                 displayContent.setLayoutNeeded();
                 int anim[] = new int[2];
                 if (displayContent.isDimming()) {
@@ -2499,7 +2421,7 @@
                 if (dc.isDefaultDisplay) {
                     mPolicy.setCurrentOrientationLw(req);
                 }
-                if (updateRotationUncheckedLocked(inTransaction, displayId)) {
+                if (dc.updateRotationUnchecked(inTransaction)) {
                     // changed
                     return true;
                 }
@@ -3837,10 +3759,12 @@
             mDeferredRotationPauseCount -= 1;
             if (mDeferredRotationPauseCount == 0) {
                 // TODO(multi-display): Update rotation for different displays separately.
-                final int displayId = DEFAULT_DISPLAY;
-                final boolean changed = updateRotationUncheckedLocked(false, displayId);
+                final DisplayContent displayContent = getDefaultDisplayContentLocked();
+                final boolean changed = displayContent.updateRotationUnchecked(
+                        false /* inTransaction */);
                 if (changed) {
-                    mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayId).sendToTarget();
+                    mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayContent.getDisplayId())
+                            .sendToTarget();
                 }
             }
         }
@@ -3856,9 +3780,10 @@
         try {
             final boolean rotationChanged;
             // TODO(multi-display): Update rotation for different displays separately.
-            int displayId = DEFAULT_DISPLAY;
+            final DisplayContent displayContent = getDefaultDisplayContentLocked();
             synchronized (mWindowMap) {
-                rotationChanged = updateRotationUncheckedLocked(false, displayId);
+                rotationChanged = displayContent.updateRotationUnchecked(
+                        false /* inTransaction */);
                 if (!rotationChanged || forceRelayout) {
                     getDefaultDisplayContentLocked().setLayoutNeeded();
                     mWindowPlacerLocked.performSurfacePlacement();
@@ -3866,231 +3791,13 @@
             }
 
             if (rotationChanged || alwaysSendConfiguration) {
-                sendNewConfiguration(displayId);
+                sendNewConfiguration(displayContent.getDisplayId());
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
     }
 
-
-    // TODO(multidisplay): Rotate any display? Move to DisplayContent
-    /**
-     * Updates the current rotation of the specified display.
-     *
-     * Returns true if the rotation has been changed.  In this case YOU MUST CALL
-     * {@link #sendNewConfiguration(int)} TO UNFREEZE THE SCREEN.
-     */
-    boolean updateRotationUncheckedLocked(boolean inTransaction, int displayId) {
-        if (mDeferredRotationPauseCount > 0) {
-            // Rotation updates have been paused temporarily.  Defer the update until
-            // updates have been resumed.
-            if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, rotation is paused.");
-            return false;
-        }
-
-        ScreenRotationAnimation screenRotationAnimation =
-                mAnimator.getScreenRotationAnimationLocked(displayId);
-        if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
-            // Rotation updates cannot be performed while the previous rotation change
-            // animation is still in progress.  Skip this update.  We will try updating
-            // again after the animation is finished and the display is unfrozen.
-            if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, animation in progress.");
-            return false;
-        }
-        if (mDisplayFrozen) {
-            // Even if the screen rotation animation has finished (e.g. isAnimating
-            // returns false), there is still some time where we haven't yet unfrozen
-            // the display. We also need to abort rotation here.
-            if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                    "Deferring rotation, still finishing previous rotation");
-            return false;
-        }
-
-        if (!mDisplayEnabled) {
-            // No point choosing a rotation if the display is not enabled.
-            if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Deferring rotation, display is not enabled.");
-            return false;
-        }
-
-        final DisplayContent dc = mRoot.getDisplayContent(displayId);
-
-        final int oldRotation = dc.getRotation();
-        final int lastOrientation = dc.getLastOrientation();
-        final boolean oldAltOrientation = dc.getAltOrientation();
-        int rotation = mPolicy.rotationForOrientationLw(lastOrientation, oldRotation);
-        final boolean rotateSeamlessly;
-
-        if (mPolicy.shouldRotateSeamlessly(oldRotation, rotation)) {
-            final WindowState seamlessRotated = dc.getWindow((w) -> w.mSeamlesslyRotated);
-            if (seamlessRotated != null) {
-                // We can't rotate (seamlessly or not) while waiting for the last seamless rotation
-                // to complete (that is, waiting for windows to redraw). It's tempting to check
-                // w.mSeamlessRotationCount but that could be incorrect in the case of
-                // window-removal.
-                return false;
-            }
-
-            final WindowState cantSeamlesslyRotate = dc.getWindow((w) ->
-                    w.isChildWindow() && w.isVisibleNow()
-                            && !w.mWinAnimator.mSurfaceController.getTransformToDisplayInverse());
-            if (cantSeamlesslyRotate != null) {
-                // In what can only be called an unfortunate workaround we require seamlessly
-                // rotated child windows to have the TRANSFORM_TO_DISPLAY_INVERSE flag. Due to
-                // limitations in the client API, there is no way for the client to set this flag in
-                // a race free fashion. If we seamlessly rotate a window which does not have this
-                // flag, but then gains it, we will get an incorrect visual result
-                // (rotated viewfinder). This means if we want to support seamlessly rotating
-                // windows which could gain this flag, we can't rotate windows without it. This
-                // limits seamless rotation in N to camera framework users, windows without
-                // children, and native code. This is unfortunate but having the camera work is our
-                // primary goal.
-                rotateSeamlessly = false;
-            } else {
-                rotateSeamlessly = true;
-            }
-        } else {
-            rotateSeamlessly = false;
-        }
-
-        // TODO: Implement forced rotation changes.
-        //       Set mAltOrientation to indicate that the application is receiving
-        //       an orientation that has different metrics than it expected.
-        //       eg. Portrait instead of Landscape.
-
-        boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw(lastOrientation, rotation);
-
-        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Selected orientation " + lastOrientation
-                + ", got rotation " + rotation + " which has "
-                + (altOrientation ? "incompatible" : "compatible") + " metrics");
-
-        if (oldRotation == rotation && oldAltOrientation == altOrientation) {
-            // No change.
-            return false;
-        }
-
-        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Rotation changed to " + rotation
-                + (altOrientation ? " (alt)" : "") + " from " + oldRotation
-                + (oldAltOrientation ? " (alt)" : "") + ", lastOrientation=" + lastOrientation);
-
-        if (DisplayContent.deltaRotation(rotation, oldRotation) != 2) {
-            mWaitingForConfig = true;
-        }
-
-        dc.setRotation(rotation);
-        dc.setAltOrientation(altOrientation);
-        if (dc.isDefaultDisplay) {
-            mPolicy.setRotationLw(rotation);
-        }
-
-        mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
-        mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT);
-        mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, WINDOW_FREEZE_TIMEOUT_DURATION);
-
-        dc.setLayoutNeeded();
-        final int[] anim = new int[2];
-        if (dc.isDimming()) {
-            anim[0] = anim[1] = 0;
-        } else {
-            mPolicy.selectRotationAnimationLw(anim);
-        }
-
-        if (!rotateSeamlessly) {
-            startFreezingDisplayLocked(inTransaction, anim[0], anim[1]);
-            // startFreezingDisplayLocked can reset the ScreenRotationAnimation.
-            screenRotationAnimation = mAnimator.getScreenRotationAnimationLocked(displayId);
-        } else {
-            // The screen rotation animation uses a screenshot to freeze the screen
-            // while windows resize underneath.
-            // When we are rotating seamlessly, we allow the elements to transition
-            // to their rotated state independently and without a freeze required.
-            screenRotationAnimation = null;
-
-            // We have to reset this in case a window was removed before it
-            // finished seamless rotation.
-            mSeamlessRotationCount = 0;
-        }
-
-        // We need to update our screen size information to match the new rotation. If the rotation
-        // has actually changed then this method will return true and, according to the comment at
-        // the top of the method, the caller is obligated to call computeNewConfigurationLocked().
-        // By updating the Display info here it will be available to
-        // computeScreenConfigurationLocked later.
-        updateDisplayAndOrientationLocked(dc.getConfiguration().uiMode, displayId);
-
-        final DisplayInfo displayInfo = dc.getDisplayInfo();
-        if (!inTransaction) {
-            if (SHOW_TRANSACTIONS) {
-                Slog.i(TAG_WM, ">>> OPEN TRANSACTION setRotationUnchecked");
-            }
-            openSurfaceTransaction();
-        }
-        try {
-            // NOTE: We disable the rotation in the emulator because
-            //       it doesn't support hardware OpenGL emulation yet.
-            if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null
-                    && screenRotationAnimation.hasScreenshot()) {
-                if (screenRotationAnimation.setRotationInTransaction(
-                        rotation, mFxSession,
-                        MAX_ANIMATION_DURATION, getTransitionAnimationScaleLocked(),
-                        displayInfo.logicalWidth, displayInfo.logicalHeight)) {
-                    scheduleAnimationLocked();
-                }
-            }
-
-            if (rotateSeamlessly) {
-                dc.forAllWindows(w -> {
-                        w.mWinAnimator.seamlesslyRotateWindow(oldRotation, rotation);
-                }, true /* traverseTopToBottom */);
-            }
-
-            mDisplayManagerInternal.performTraversalInTransactionFromWindowManager();
-        } finally {
-            if (!inTransaction) {
-                closeSurfaceTransaction();
-                if (SHOW_LIGHT_TRANSACTIONS) {
-                    Slog.i(TAG_WM, "<<< CLOSE TRANSACTION setRotationUnchecked");
-                }
-            }
-        }
-
-        dc.forAllWindows(w -> {
-            // Discard surface after orientation change, these can't be reused.
-            if (w.mAppToken != null) {
-                w.mAppToken.destroySavedSurfaces();
-            }
-            if (w.mHasSurface && !rotateSeamlessly) {
-                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Set mOrientationChanging of " + w);
-                w.mOrientationChanging = true;
-                mRoot.mOrientationChangeComplete = false;
-                w.mLastFreezeDuration = 0;
-            }
-            w.mReportOrientationChanged = true;
-        }, true /* traverseTopToBottom */);
-
-        if (rotateSeamlessly) {
-            mH.removeMessages(H.SEAMLESS_ROTATION_TIMEOUT);
-            mH.sendEmptyMessageDelayed(H.SEAMLESS_ROTATION_TIMEOUT, SEAMLESS_ROTATION_TIMEOUT_DURATION);
-        }
-
-        for (int i=mRotationWatchers.size()-1; i>=0; i--) {
-            try {
-                mRotationWatchers.get(i).watcher.onRotationChanged(rotation);
-            } catch (RemoteException e) {
-            }
-        }
-
-        // TODO (multidisplay): Magnification is supported only for the default display.
-        // Announce rotation only if we will not animate as we already have the
-        // windows in final state. Otherwise, we make this call at the rotation end.
-        if (screenRotationAnimation == null && mAccessibilityController != null
-                && dc.getDisplayId() == DEFAULT_DISPLAY) {
-            mAccessibilityController.onRotationChangedLocked(getDefaultDisplayContentLocked());
-        }
-
-        return true;
-    }
-
     @Override
     public int getDefaultDisplayRotation() {
         synchronized (mWindowMap) {
@@ -4104,16 +3811,16 @@
     }
 
     @Override
-    public int watchRotation(IRotationWatcher watcher) {
+    public int watchRotation(IRotationWatcher watcher, int displayId) {
         final IBinder watcherBinder = watcher.asBinder();
         IBinder.DeathRecipient dr = new IBinder.DeathRecipient() {
             @Override
             public void binderDied() {
                 synchronized (mWindowMap) {
                     for (int i=0; i<mRotationWatchers.size(); i++) {
-                        if (watcherBinder == mRotationWatchers.get(i).watcher.asBinder()) {
+                        if (watcherBinder == mRotationWatchers.get(i).mWatcher.asBinder()) {
                             RotationWatcher removed = mRotationWatchers.remove(i);
-                            IBinder binder = removed.watcher.asBinder();
+                            IBinder binder = removed.mWatcher.asBinder();
                             if (binder != null) {
                                 binder.unlinkToDeath(this, 0);
                             }
@@ -4127,12 +3834,11 @@
         synchronized (mWindowMap) {
             try {
                 watcher.asBinder().linkToDeath(dr, 0);
-                mRotationWatchers.add(new RotationWatcher(watcher, dr));
+                mRotationWatchers.add(new RotationWatcher(watcher, dr, displayId));
             } catch (RemoteException e) {
                 // Client died, no cleanup needed.
             }
 
-            // TODO(multi-display): Modify rotation watchers to include display id.
             return getDefaultDisplayRotation();
         }
     }
@@ -4143,11 +3849,11 @@
         synchronized (mWindowMap) {
             for (int i=0; i<mRotationWatchers.size(); i++) {
                 RotationWatcher rotationWatcher = mRotationWatchers.get(i);
-                if (watcherBinder == rotationWatcher.watcher.asBinder()) {
+                if (watcherBinder == rotationWatcher.mWatcher.asBinder()) {
                     RotationWatcher removed = mRotationWatchers.remove(i);
-                    IBinder binder = removed.watcher.asBinder();
+                    IBinder binder = removed.mWatcher.asBinder();
                     if (binder != null) {
-                        binder.unlinkToDeath(removed.deathRecipient, 0);
+                        binder.unlinkToDeath(removed.mDeathRecipient, 0);
                     }
                     i--;
                 }
@@ -4564,7 +4270,7 @@
                 // Something changed (E.g. device rotation), but no configuration update is needed.
                 // E.g. changing device rotation by 180 degrees. Go ahead and perform surface
                 // placement to unfreeze the display since we froze it when the rotation was updated
-                // in updateRotationUncheckedLocked.
+                // in DisplayContent#updateRotationUnchecked.
                 synchronized (mWindowMap) {
                     if (mWaitingForConfig) {
                         mWaitingForConfig = false;
@@ -4589,297 +4295,9 @@
             return null;
         }
         final Configuration config = new Configuration();
-        computeScreenConfigurationLocked(config, displayId);
-        return config;
-    }
-
-    private void adjustDisplaySizeRanges(DisplayInfo displayInfo, int displayId, int rotation,
-            int uiMode, int dw, int dh) {
-        final int width = mPolicy.getConfigDisplayWidth(dw, dh, rotation, uiMode, displayId);
-        if (width < displayInfo.smallestNominalAppWidth) {
-            displayInfo.smallestNominalAppWidth = width;
-        }
-        if (width > displayInfo.largestNominalAppWidth) {
-            displayInfo.largestNominalAppWidth = width;
-        }
-        final int height = mPolicy.getConfigDisplayHeight(dw, dh, rotation, uiMode, displayId);
-        if (height < displayInfo.smallestNominalAppHeight) {
-            displayInfo.smallestNominalAppHeight = height;
-        }
-        if (height > displayInfo.largestNominalAppHeight) {
-            displayInfo.largestNominalAppHeight = height;
-        }
-    }
-
-    private int reduceConfigLayout(int curLayout, int rotation, float density,
-            int dw, int dh, int uiMode, int displayId) {
-        // Get the app screen size at this rotation.
-        int w = mPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode, displayId);
-        int h = mPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode, displayId);
-
-        // Compute the screen layout size class for this rotation.
-        int longSize = w;
-        int shortSize = h;
-        if (longSize < shortSize) {
-            int tmp = longSize;
-            longSize = shortSize;
-            shortSize = tmp;
-        }
-        longSize = (int)(longSize/density);
-        shortSize = (int)(shortSize/density);
-        return Configuration.reduceScreenLayout(curLayout, longSize, shortSize);
-    }
-
-    private void computeSizeRangesAndScreenLayout(DisplayInfo displayInfo, int displayId,
-            boolean rotated, int uiMode, int dw, int dh, float density, Configuration outConfig) {
-
-        // We need to determine the smallest width that will occur under normal
-        // operation.  To this, start with the base screen size and compute the
-        // width under the different possible rotations.  We need to un-rotate
-        // the current screen dimensions before doing this.
-        int unrotDw, unrotDh;
-        if (rotated) {
-            unrotDw = dh;
-            unrotDh = dw;
-        } else {
-            unrotDw = dw;
-            unrotDh = dh;
-        }
-        displayInfo.smallestNominalAppWidth = 1<<30;
-        displayInfo.smallestNominalAppHeight = 1<<30;
-        displayInfo.largestNominalAppWidth = 0;
-        displayInfo.largestNominalAppHeight = 0;
-        adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_0, uiMode, unrotDw,
-                unrotDh);
-        adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_90, uiMode, unrotDh,
-                unrotDw);
-        adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_180, uiMode, unrotDw,
-                unrotDh);
-        adjustDisplaySizeRanges(displayInfo, displayId, Surface.ROTATION_270, uiMode, unrotDh,
-                unrotDw);
-        int sl = Configuration.resetScreenLayout(outConfig.screenLayout);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_0, density, unrotDw, unrotDh, uiMode,
-                displayId);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode,
-                displayId);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode,
-                displayId);
-        sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode,
-                displayId);
-        outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density);
-        outConfig.screenLayout = sl;
-    }
-
-    private int reduceCompatConfigWidthSize(int curSize, int rotation, int uiMode,
-            DisplayMetrics dm, int dw, int dh, int displayId) {
-        dm.noncompatWidthPixels = mPolicy.getNonDecorDisplayWidth(dw, dh, rotation, uiMode,
-                displayId);
-        dm.noncompatHeightPixels = mPolicy.getNonDecorDisplayHeight(dw, dh, rotation, uiMode,
-                displayId);
-        float scale = CompatibilityInfo.computeCompatibleScaling(dm, null);
-        int size = (int)(((dm.noncompatWidthPixels / scale) / dm.density) + .5f);
-        if (curSize == 0 || size < curSize) {
-            curSize = size;
-        }
-        return curSize;
-    }
-
-    private int computeCompatSmallestWidth(boolean rotated, int uiMode, DisplayMetrics dm, int dw,
-            int dh, int displayId) {
-        mTmpDisplayMetrics.setTo(dm);
-        final DisplayMetrics tmpDm = mTmpDisplayMetrics;
-        final int unrotDw, unrotDh;
-        if (rotated) {
-            unrotDw = dh;
-            unrotDh = dw;
-        } else {
-            unrotDw = dw;
-            unrotDh = dh;
-        }
-        int sw = reduceCompatConfigWidthSize(0, Surface.ROTATION_0, uiMode, tmpDm, unrotDw, unrotDh,
-                displayId);
-        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_90, uiMode, tmpDm, unrotDh, unrotDw,
-                displayId);
-        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_180, uiMode, tmpDm, unrotDw, unrotDh,
-                displayId);
-        sw = reduceCompatConfigWidthSize(sw, Surface.ROTATION_270, uiMode, tmpDm, unrotDh, unrotDw,
-                displayId);
-        return sw;
-    }
-
-    /** Do not call if mDisplayReady == false */
-    private DisplayInfo updateDisplayAndOrientationLocked(int uiMode, int displayId) {
         final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-
-        // TODO(multi-display): Implement rotation for secondary displays.
-        final boolean isDefaultDisplay = displayContent.isDefaultDisplay;
-        final int displayRotation = displayContent.getRotation();
-        final boolean altDisplayOrientation = displayContent.getAltOrientation();
-
-        // Use the effective "visual" dimensions based on current rotation
-        final boolean rotated = (displayRotation == Surface.ROTATION_90
-                || displayRotation == Surface.ROTATION_270);
-        final int realdw = rotated ?
-                displayContent.mBaseDisplayHeight : displayContent.mBaseDisplayWidth;
-        final int realdh = rotated ?
-                displayContent.mBaseDisplayWidth : displayContent.mBaseDisplayHeight;
-        int dw = realdw;
-        int dh = realdh;
-
-        if (altDisplayOrientation) {
-            if (realdw > realdh) {
-                // Turn landscape into portrait.
-                int maxw = (int)(realdh/1.3f);
-                if (maxw < realdw) {
-                    dw = maxw;
-                }
-            } else {
-                // Turn portrait into landscape.
-                int maxh = (int)(realdw/1.3f);
-                if (maxh < realdh) {
-                    dh = maxh;
-                }
-            }
-        }
-
-        // Update application display metrics.
-        final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, displayRotation, uiMode,
-                displayId);
-        final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, displayRotation, uiMode,
-                displayId);
-        final DisplayInfo displayInfo = displayContent.getDisplayInfo();
-        displayInfo.rotation = displayRotation;
-        displayInfo.logicalWidth = dw;
-        displayInfo.logicalHeight = dh;
-        displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity;
-        displayInfo.appWidth = appWidth;
-        displayInfo.appHeight = appHeight;
-        if (isDefaultDisplay) {
-            displayInfo.getLogicalMetrics(mRealDisplayMetrics,
-                    CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
-        }
-        displayInfo.getAppMetrics(mDisplayMetrics);
-        if (displayContent.mDisplayScalingDisabled) {
-            displayInfo.flags |= Display.FLAG_SCALING_DISABLED;
-        } else {
-            displayInfo.flags &= ~Display.FLAG_SCALING_DISABLED;
-        }
-
-        mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
-                displayContent.getDisplayId(), displayInfo);
-
-        displayContent.mBaseDisplayRect.set(0, 0, dw, dh);
-        if (false) {
-            Slog.i(TAG_WM, "Set app display size: " + appWidth + " x " + appHeight);
-        }
-
-        if (isDefaultDisplay) {
-            mCompatibleScreenScale = CompatibilityInfo.computeCompatibleScaling(mDisplayMetrics,
-                    mCompatDisplayMetrics);
-        }
-        return displayInfo;
-    }
-
-    /** Do not call if mDisplayReady == false */
-    private void computeScreenConfigurationLocked(Configuration config, int displayId) {
-        final DisplayInfo displayInfo = updateDisplayAndOrientationLocked(config.uiMode, displayId);
-
-        final int dw = displayInfo.logicalWidth;
-        final int dh = displayInfo.logicalHeight;
-        config.orientation = (dw <= dh) ? Configuration.ORIENTATION_PORTRAIT :
-                Configuration.ORIENTATION_LANDSCAPE;
-        config.screenWidthDp =
-                (int)(mPolicy.getConfigDisplayWidth(dw, dh, displayInfo.rotation, config.uiMode,
-                        displayId) / mDisplayMetrics.density);
-        config.screenHeightDp =
-                (int)(mPolicy.getConfigDisplayHeight(dw, dh, displayInfo.rotation, config.uiMode,
-                        displayId) / mDisplayMetrics.density);
-        final boolean rotated = (displayInfo.rotation == Surface.ROTATION_90
-                || displayInfo.rotation == Surface.ROTATION_270);
-
-        computeSizeRangesAndScreenLayout(displayInfo, displayId, rotated, config.uiMode, dw, dh,
-                mDisplayMetrics.density, config);
-
-        config.screenLayout = (config.screenLayout & ~Configuration.SCREENLAYOUT_ROUND_MASK)
-                | ((displayInfo.flags & Display.FLAG_ROUND) != 0
-                        ? Configuration.SCREENLAYOUT_ROUND_YES
-                        : Configuration.SCREENLAYOUT_ROUND_NO);
-
-        config.compatScreenWidthDp = (int)(config.screenWidthDp / mCompatibleScreenScale);
-        config.compatScreenHeightDp = (int)(config.screenHeightDp / mCompatibleScreenScale);
-        config.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, config.uiMode,
-                mDisplayMetrics, dw, dh, displayId);
-        config.densityDpi = displayInfo.logicalDensityDpi;
-
-        config.colorMode =
-                (displayInfo.isHdr()
-                        ? Configuration.COLOR_MODE_HDR_YES
-                        : Configuration.COLOR_MODE_HDR_NO)
-                | (displayInfo.isWideColorGamut()
-                        ? Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_YES
-                        : Configuration.COLOR_MODE_WIDE_COLOR_GAMUT_NO);
-
-        // Update the configuration based on available input devices, lid switch,
-        // and platform configuration.
-        config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
-        config.keyboard = Configuration.KEYBOARD_NOKEYS;
-        config.navigation = Configuration.NAVIGATION_NONAV;
-
-        int keyboardPresence = 0;
-        int navigationPresence = 0;
-        final InputDevice[] devices = mInputManager.getInputDevices();
-        final int len = devices != null ? devices.length : 0;
-        for (int i = 0; i < len; i++) {
-            InputDevice device = devices[i];
-            if (!device.isVirtual()) {
-                final int sources = device.getSources();
-                final int presenceFlag = device.isExternal() ?
-                        WindowManagerPolicy.PRESENCE_EXTERNAL :
-                                WindowManagerPolicy.PRESENCE_INTERNAL;
-
-                if (mIsTouchDevice) {
-                    if ((sources & InputDevice.SOURCE_TOUCHSCREEN) ==
-                            InputDevice.SOURCE_TOUCHSCREEN) {
-                        config.touchscreen = Configuration.TOUCHSCREEN_FINGER;
-                    }
-                } else {
-                    config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
-                }
-
-                if ((sources & InputDevice.SOURCE_TRACKBALL) == InputDevice.SOURCE_TRACKBALL) {
-                    config.navigation = Configuration.NAVIGATION_TRACKBALL;
-                    navigationPresence |= presenceFlag;
-                } else if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD
-                        && config.navigation == Configuration.NAVIGATION_NONAV) {
-                    config.navigation = Configuration.NAVIGATION_DPAD;
-                    navigationPresence |= presenceFlag;
-                }
-
-                if (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) {
-                    config.keyboard = Configuration.KEYBOARD_QWERTY;
-                    keyboardPresence |= presenceFlag;
-                }
-            }
-        }
-
-        if (config.navigation == Configuration.NAVIGATION_NONAV && mHasPermanentDpad) {
-            config.navigation = Configuration.NAVIGATION_DPAD;
-            navigationPresence |= WindowManagerPolicy.PRESENCE_INTERNAL;
-        }
-
-        // Determine whether a hard keyboard is available and enabled.
-        boolean hardKeyboardAvailable = config.keyboard != Configuration.KEYBOARD_NOKEYS;
-        if (hardKeyboardAvailable != mHardKeyboardAvailable) {
-            mHardKeyboardAvailable = hardKeyboardAvailable;
-            mH.removeMessages(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
-            mH.sendEmptyMessage(H.REPORT_HARD_KEYBOARD_STATUS_CHANGE);
-        }
-
-        // Let the policy update hidden states.
-        config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO;
-        config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO;
-        config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO;
-        mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence);
+        displayContent.computeScreenConfiguration(config);
+        return config;
     }
 
     void notifyHardKeyboardStatusChange() {
@@ -5245,7 +4663,6 @@
         public static final int NOTIFY_APP_TRANSITION_STARTING = 47;
         public static final int NOTIFY_APP_TRANSITION_CANCELLED = 48;
         public static final int NOTIFY_APP_TRANSITION_FINISHED = 49;
-        public static final int NOTIFY_STARTING_WINDOW_DRAWN = 50;
         public static final int UPDATE_ANIMATION_SCALE = 51;
         public static final int WINDOW_HIDE_TIMEOUT = 52;
         public static final int NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED = 53;
@@ -5674,7 +5091,7 @@
                     }
                 }
                 case NOTIFY_APP_TRANSITION_STARTING: {
-                    mAmInternal.notifyAppTransitionStarting(msg.arg1);
+                    mAmInternal.notifyAppTransitionStarting((SparseIntArray) msg.obj);
                 }
                 break;
                 case NOTIFY_APP_TRANSITION_CANCELLED: {
@@ -5685,10 +5102,6 @@
                     mAmInternal.notifyAppTransitionFinished();
                 }
                 break;
-                case NOTIFY_STARTING_WINDOW_DRAWN: {
-                    mAmInternal.notifyStartingWindowDrawn();
-                }
-                break;
                 case WINDOW_HIDE_TIMEOUT: {
                     final WindowState window = (WindowState) msg.obj;
                     synchronized(mWindowMap) {
@@ -6096,7 +5509,7 @@
                 displayId);
         final Configuration currentDisplayConfig = displayContent.getConfiguration();
         mTempConfiguration.setTo(currentDisplayConfig);
-        computeScreenConfigurationLocked(mTempConfiguration, displayId);
+        displayContent.computeScreenConfiguration(mTempConfiguration);
         configChanged |= currentDisplayConfig.diff(mTempConfiguration) != 0;
 
         if (configChanged) {
@@ -6559,7 +5972,8 @@
 
         if (updateRotation) {
             if (DEBUG_ORIENTATION) Slog.d(TAG_WM, "Performing post-rotate rotation");
-            configChanged |= updateRotationUncheckedLocked(false, displayId);
+            configChanged |= displayContent.updateRotationUnchecked(
+                    false /* inTransaction */);
         }
 
         if (configChanged) {
@@ -6602,8 +6016,9 @@
                 String[] toks = line.split("%");
                 if (toks != null && toks.length > 0) {
                     // TODO(multi-display): Show watermarks on secondary displays.
-                    mWatermark = new Watermark(getDefaultDisplayContentLocked().getDisplay(),
-                            mRealDisplayMetrics, mFxSession, toks);
+                    final DisplayContent displayContent = getDefaultDisplayContentLocked();
+                    mWatermark = new Watermark(displayContent.getDisplay(),
+                            displayContent.mRealDisplayMetrics, mFxSession, toks);
                 }
             }
         } catch (FileNotFoundException e) {
@@ -7639,9 +7054,10 @@
             if (DEBUG_ORIENTATION) {
                 Slog.i(TAG, "Performing post-rotate rotation after seamless rotation");
             }
-            final int displayId = w.getDisplayId();
-            if (updateRotationUncheckedLocked(false, displayId)) {
-                mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayId).sendToTarget();
+            final DisplayContent displayContent = w.getDisplayContent();
+            if (displayContent.updateRotationUnchecked(false /* inTransaction */)) {
+                mH.obtainMessage(H.SEND_NEW_CONFIGURATION, displayContent.getDisplayId())
+                        .sendToTarget();
             }
         }
     }
@@ -7667,6 +7083,17 @@
         }
 
         @Override
+        public void setForceShowMagnifiableBounds(boolean show) {
+            synchronized (mWindowMap) {
+                if (mAccessibilityController != null) {
+                    mAccessibilityController.setForceShowMagnifiableBoundsLocked(show);
+                } else {
+                    throw new IllegalStateException("Magnification callbacks not set!");
+                }
+            }
+        }
+
+        @Override
         public void getMagnificationRegion(@NonNull Region magnificationRegion) {
             synchronized (mWindowMap) {
                 if (mAccessibilityController != null) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ccbc5ba..4e593d8 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1273,8 +1273,8 @@
 
     void prelayout() {
         if (mEnforceSizeCompat) {
-            mGlobalScale = mService.mCompatibleScreenScale;
-            mInvGlobalScale = 1/mGlobalScale;
+            mGlobalScale = getDisplayContent().mCompatibleScreenScale;
+            mInvGlobalScale = 1 / mGlobalScale;
         } else {
             mGlobalScale = mInvGlobalScale = 1;
         }
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index f7d3343..b08bb70 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -97,13 +97,7 @@
         mWindowType = windowType;
         mWindowSession = win.mSession;
 
-        // For opaque child windows placed under parent windows, we use a special SurfaceControl
-        // which mirrors commands to a black-out layer placed one Z-layer below the surface.
-        // This prevents holes to whatever app/wallpaper is underneath.
-        if (win.isChildWindow() && win.mSubLayer < 0 && win.mAppToken != null) {
-            mSurfaceControl = new SurfaceControlWithBackground(
-                    s, name, w, h, format, flags, win.mAppToken, windowType, ownerUid);
-        } else if (DEBUG_SURFACE_TRACE) {
+        if (DEBUG_SURFACE_TRACE) {
             mSurfaceControl = new SurfaceTrace(
                     s, name, w, h, format, flags, windowType, ownerUid);
         } else {
@@ -566,7 +560,10 @@
         pw.print(" rect=("); pw.print(mSurfaceX);
         pw.print(","); pw.print(mSurfaceY);
         pw.print(") "); pw.print(mSurfaceW);
-        pw.print(" x "); pw.println(mSurfaceH);
+        pw.print(" x "); pw.print(mSurfaceH);
+        pw.print(" transform=("); pw.print(mLastDsdx); pw.print(", ");
+        pw.print(mLastDtdx); pw.print(", "); pw.print(mLastDsdy);
+        pw.print(", "); pw.print(mLastDtdy); pw.println(")");
     }
 
     @Override
@@ -831,141 +828,4 @@
                     + " (" + mDsdx + "," + mDtdx + "," + mDsdy + "," + mDtdy + ")";
         }
     }
-
-    class SurfaceControlWithBackground extends SurfaceControl {
-        private SurfaceControl mBackgroundControl;
-        private boolean mOpaque = true;
-        private boolean mAppForcedInvisible = false;
-        private AppWindowToken mAppToken;
-        public boolean mVisible = false;
-        public int mLayer = -1;
-
-        public SurfaceControlWithBackground(SurfaceSession s, String name, int w, int h, int format,
-                    int flags, AppWindowToken token, int windowType, int ownerUid)
-                throws OutOfResourcesException {
-            super(s, name, w, h, format, flags, windowType, ownerUid);
-            mBackgroundControl = new SurfaceControl(s, name, w, h,
-                    PixelFormat.OPAQUE, flags | SurfaceControl.FX_SURFACE_DIM);
-            mOpaque = (flags & SurfaceControl.OPAQUE) != 0;
-            mAppToken = token;
-
-            mAppToken.addSurfaceViewBackground(this);
-        }
-
-        @Override
-        public void setAlpha(float alpha) {
-            super.setAlpha(alpha);
-            mBackgroundControl.setAlpha(alpha);
-        }
-
-        @Override
-        public void setLayer(int zorder) {
-            super.setLayer(zorder);
-            mBackgroundControl.setLayer(zorder - 1);
-            if (mLayer != zorder) {
-                mLayer = zorder;
-                mAppToken.updateSurfaceViewBackgroundVisibilities();
-            }
-        }
-
-        @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(mAppForcedInvisible);
-        }
-
-        @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();
-            if (mVisible) {
-                mVisible = false;
-                mAppToken.updateSurfaceViewBackgroundVisibilities();
-            }
-        }
-
-        @Override
-        public void show() {
-            super.show();
-            if (!mVisible) {
-                mVisible = true;
-                mAppToken.updateSurfaceViewBackgroundVisibilities();
-            }
-        }
-
-        @Override
-        public void destroy() {
-            super.destroy();
-            mBackgroundControl.destroy();
-            mAppToken.removeSurfaceViewBackground(this);
-         }
-
-        @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);
-        }
-
-        void updateBackgroundVisibility(boolean forcedInvisible) {
-            mAppForcedInvisible = forcedInvisible;
-            if (mOpaque && mVisible && !mAppForcedInvisible) {
-                mBackgroundControl.show();
-            } else {
-                mBackgroundControl.hide();
-            }
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index f247ebe..3cb96a1 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -1,9 +1,10 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManagerInternal.APP_TRANSITION_SAVED_SURFACE;
-import static android.app.ActivityManagerInternal.APP_TRANSITION_STARTING_WINDOW;
-import static android.app.ActivityManagerInternal.APP_TRANSITION_TIMEOUT;
+import static android.app.ActivityManagerInternal.APP_TRANSITION_SNAPSHOT;
+import static android.app.ActivityManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
 import static android.app.ActivityManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
@@ -45,6 +46,7 @@
 import android.os.Trace;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.util.SparseIntArray;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.Surface;
@@ -93,6 +95,7 @@
     private final LayerAndToken mTmpLayerAndToken = new LayerAndToken();
 
     private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
+    private final SparseIntArray mTempTransitionReasons = new SparseIntArray();
 
     public WindowSurfacePlacer(WindowManagerService service) {
         mService = service;
@@ -495,7 +498,7 @@
             mService.mAnimator.getScreenRotationAnimationLocked(
                     Display.DEFAULT_DISPLAY);
 
-        int reason = APP_TRANSITION_TIMEOUT;
+        final SparseIntArray reasons = mTempTransitionReasons;
         if (!mService.mAppTransition.isTimeout()) {
             // Imagine the case where we are changing orientation due to an app transition, but a previous
             // orientation change is still in progress. We won't process the orientation change
@@ -526,11 +529,15 @@
                 if (!allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
                     return false;
                 }
+                final TaskStack stack = wtoken.getStack();
+                final int stackId = stack != null ? stack.mStackId : INVALID_STACK_ID;
                 if (allDrawn) {
-                    reason = drawnBeforeRestoring ? APP_TRANSITION_WINDOWS_DRAWN
-                            : APP_TRANSITION_SAVED_SURFACE;
+                    reasons.put(stackId, drawnBeforeRestoring ? APP_TRANSITION_WINDOWS_DRAWN
+                            : APP_TRANSITION_SAVED_SURFACE);
                 } else {
-                    reason = APP_TRANSITION_STARTING_WINDOW;
+                    reasons.put(stackId, wtoken.startingData instanceof SplashScreenStartingData
+                            ? APP_TRANSITION_SPLASH_SCREEN
+                            : APP_TRANSITION_SNAPSHOT);
                 }
             }
 
@@ -552,12 +559,13 @@
             boolean wallpaperReady = !mWallpaperControllerLocked.isWallpaperVisible() ||
                     mWallpaperControllerLocked.wallpaperTransitionReady();
             if (wallpaperReady) {
-                mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, reason, 0).sendToTarget();
+                mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, reasons.clone())
+                        .sendToTarget();
                 return true;
             }
             return false;
         }
-        mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, reason, 0).sendToTarget();
+        mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING, reasons.clone()).sendToTarget();
         return true;
     }
 
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 9f528b1..b433350 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -81,6 +81,7 @@
     jmethodID deviceId;
     jmethodID type;
     jmethodID hdmiPortId;
+    jmethodID cableConnectionStatus;
     jmethodID audioType;
     jmethodID audioAddress;
     jmethodID build;
@@ -469,6 +470,9 @@
                 builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, info.portId);
     }
     env->CallObjectMethod(
+            builder, gTvInputHardwareInfoBuilderClassInfo.cableConnectionStatus,
+            info.cableConnectionStatus);
+    env->CallObjectMethod(
             builder, gTvInputHardwareInfoBuilderClassInfo.audioType, info.audioType);
     if (info.audioType != AudioDevice::NONE) {
         uint8_t buffer[info.audioAddress.size() + 1];
@@ -743,6 +747,10 @@
             gTvInputHardwareInfoBuilderClassInfo.clazz,
             "hdmiPortId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
     GET_METHOD_ID(
+            gTvInputHardwareInfoBuilderClassInfo.cableConnectionStatus,
+            gTvInputHardwareInfoBuilderClassInfo.clazz,
+            "cableConnectionStatus", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
+    GET_METHOD_ID(
             gTvInputHardwareInfoBuilderClassInfo.audioType,
             gTvInputHardwareInfoBuilderClassInfo.clazz,
             "audioType", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7ad0292..0e6a542 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3141,6 +3141,10 @@
             throw new IllegalArgumentException("Only apps in internal storage can be active admin: "
                     + adminReceiver);
         }
+        if (info.getActivityInfo().applicationInfo.isInstantApp()) {
+            throw new IllegalArgumentException("Instant apps cannot be device admins: "
+                    + adminReceiver);
+        }
         synchronized (this) {
             long ident = mInjector.binderClearCallingIdentity();
             try {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 1a0aff7..f84bfef 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -86,6 +86,7 @@
 import com.android.server.om.OverlayManagerService;
 import com.android.server.os.DeviceIdentifiersPolicyService;
 import com.android.server.os.SchedulingPolicyService;
+import com.android.server.pm.BackgroundDexOptService;
 import com.android.server.pm.Installer;
 import com.android.server.pm.LauncherAppsService;
 import com.android.server.pm.OtaDexoptService;
@@ -195,7 +196,7 @@
     private static final String WALLPAPER_SERVICE_CLASS =
             "com.android.server.wallpaper.WallpaperManagerService$Lifecycle";
     private static final String AUTO_FILL_MANAGER_SERVICE_CLASS =
-            "com.android.server.autofill.AutoFillManagerService";
+            "com.android.server.autofill.AutofillManagerService";
 
     private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
 
@@ -1428,11 +1429,11 @@
                     traceEnd();
                 }
 
-                traceBeginAndSlog("StartBackgroundDexOptJobService");
+                traceBeginAndSlog("StartBackgroundDexOptService");
                 try {
-                    BackgroundDexOptJobService.schedule(context);
+                    BackgroundDexOptService.schedule(context);
                 } catch (Throwable e) {
-                    reportWtf("starting StartBackgroundDexOptJobService", e);
+                    reportWtf("starting StartBackgroundDexOptService", e);
                 }
                 traceEnd();
 
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 0a90749..a0a9310 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -93,17 +93,10 @@
     class ReceiveThread extends Thread {
         private final byte[] mPacket = new byte[1514];
         private final FileDescriptor mSocket;
-        private volatile boolean mStopped;
-
-        // Starting time of the RA receiver thread.
         private final long mStart = SystemClock.elapsedRealtime();
+        private final ApfStats mStats = new ApfStats();
 
-        private int mReceivedRas;     // Number of received RAs
-        private int mMatchingRas;     // Number of received RAs matching a known RA
-        private int mDroppedRas;      // Number of received RAs ignored due to the MAX_RAS limit
-        private int mParseErrors;     // Number of received RAs that could not be parsed
-        private int mZeroLifetimeRas; // Number of received RAs with a 0 lifetime
-        private int mProgramUpdates;  // Number of APF program updates triggered by receiving a RA
+        private volatile boolean mStopped;
 
         public ReceiveThread(FileDescriptor socket) {
             mSocket = socket;
@@ -134,35 +127,40 @@
         }
 
         private void updateStats(ProcessRaResult result) {
-            mReceivedRas++;
+            mStats.receivedRas++;
             switch(result) {
                 case MATCH:
-                    mMatchingRas++;
+                    mStats.matchingRas++;
                     return;
                 case DROPPED:
-                    mDroppedRas++;
+                    mStats.droppedRas++;
                     return;
                 case PARSE_ERROR:
-                    mParseErrors++;
+                    mStats.parseErrors++;
                     return;
                 case ZERO_LIFETIME:
-                    mZeroLifetimeRas++;
+                    mStats.zeroLifetimeRas++;
                     return;
                 case UPDATE_EXPIRY:
-                    mMatchingRas++;
-                    mProgramUpdates++;
+                    mStats.matchingRas++;
+                    mStats.programUpdates++;
                     return;
                 case UPDATE_NEW_RA:
-                    mProgramUpdates++;
+                    mStats.programUpdates++;
                     return;
             }
         }
 
         private void logStats() {
-            long durationMs = SystemClock.elapsedRealtime() - mStart;
-            int maxSize = mApfCapabilities.maximumApfProgramSize;
-            mMetricsLog.log(new ApfStats(durationMs, mReceivedRas, mMatchingRas, mDroppedRas,
-                     mZeroLifetimeRas, mParseErrors, mProgramUpdates, maxSize));
+            final long nowMs = SystemClock.elapsedRealtime();
+            synchronized (this) {
+                mStats.durationMs = nowMs - mStart;
+                mStats.maxProgramSize = mApfCapabilities.maximumApfProgramSize;
+                mStats.programUpdatesAll = mNumProgramUpdates;
+                mStats.programUpdatesAllowingMulticast = mNumProgramUpdatesAllowingMulticast;
+                mMetricsLog.log(mStats);
+                logApfProgramEventLocked(nowMs / DateUtils.SECOND_IN_MILLIS);
+            }
         }
     }
 
@@ -218,6 +216,8 @@
             4,    // Protocol size: 4
     };
     private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
+    // Do not log ApfProgramEvents whose actual lifetimes was less than this.
+    private static final int APF_PROGRAM_EVENT_LIFETIME_THRESHOLD = 2;
 
     private final ApfCapabilities mApfCapabilities;
     private final IpManager.Callback mIpManagerCallback;
@@ -247,6 +247,7 @@
         mMulticastFilter = multicastFilter;
         mMetricsLog = log;
 
+        // TODO: ApfFilter should not generate programs until IpManager sends provisioning success.
         maybeStartFilter();
     }
 
@@ -661,14 +662,19 @@
     // How long should the last installed filter program live for? In seconds.
     @GuardedBy("this")
     private long mLastInstalledProgramMinLifetime;
+    @GuardedBy("this")
+    private ApfProgramEvent mLastInstallEvent;
 
     // For debugging only. The last program installed.
     @GuardedBy("this")
     private byte[] mLastInstalledProgram;
 
-    // For debugging only. How many times the program was updated since we started.
+    // How many times the program was updated since we started.
     @GuardedBy("this")
-    private int mNumProgramUpdates;
+    private int mNumProgramUpdates = 0;
+    // How many times the program was updated since we started for allowing multicast traffic.
+    @GuardedBy("this")
+    private int mNumProgramUpdatesAllowingMulticast = 0;
 
     /**
      * Generate filter code to process ARP packets. Execution of this code ends in either the
@@ -947,7 +953,8 @@
             Log.e(TAG, "Failed to generate APF program.", e);
             return;
         }
-        mLastTimeInstalledProgram = currentTimeSeconds();
+        final long now = currentTimeSeconds();
+        mLastTimeInstalledProgram = now;
         mLastInstalledProgramMinLifetime = programMinLifetime;
         mLastInstalledProgram = program;
         mNumProgramUpdates++;
@@ -956,9 +963,26 @@
             hexDump("Installing filter: ", program, program.length);
         }
         mIpManagerCallback.installPacketFilter(program);
-        int flags = ApfProgramEvent.flagsFor(mIPv4Address != null, mMulticastFilter);
-        mMetricsLog.log(new ApfProgramEvent(
-                programMinLifetime, rasToFilter.size(), mRas.size(), program.length, flags));
+        logApfProgramEventLocked(now);
+        mLastInstallEvent = new ApfProgramEvent();
+        mLastInstallEvent.lifetime = programMinLifetime;
+        mLastInstallEvent.filteredRas = rasToFilter.size();
+        mLastInstallEvent.currentRas = mRas.size();
+        mLastInstallEvent.programLength = program.length;
+        mLastInstallEvent.flags = ApfProgramEvent.flagsFor(mIPv4Address != null, mMulticastFilter);
+    }
+
+    private void logApfProgramEventLocked(long now) {
+        if (mLastInstallEvent == null) {
+            return;
+        }
+        ApfProgramEvent ev = mLastInstallEvent;
+        mLastInstallEvent = null;
+        ev.actualLifetime = now - mLastTimeInstalledProgram;
+        if (ev.actualLifetime < APF_PROGRAM_EVENT_LIFETIME_THRESHOLD) {
+            return;
+        }
+        mMetricsLog.log(ev);
     }
 
     /**
@@ -1078,11 +1102,15 @@
         mRas.clear();
     }
 
-    public synchronized void setMulticastFilter(boolean enabled) {
-        if (mMulticastFilter != enabled) {
-            mMulticastFilter = enabled;
-            installNewProgramLocked();
+    public synchronized void setMulticastFilter(boolean isEnabled) {
+        if (mMulticastFilter == isEnabled) {
+            return;
         }
+        mMulticastFilter = isEnabled;
+        if (!isEnabled) {
+            mNumProgramUpdatesAllowingMulticast++;
+        }
+        installNewProgramLocked();
     }
 
     /** Find the single IPv4 LinkAddress if there is one, otherwise return null. */
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 76b1c90..3e3a19b 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -23,6 +23,7 @@
 import android.net.apf.ApfCapabilities;
 import android.net.apf.ApfFilter;
 import android.net.DhcpResults;
+import android.net.INetd;
 import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
@@ -34,10 +35,12 @@
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.IpManagerEvent;
 import android.net.util.MultinetworkPolicyTracker;
+import android.net.util.NetdService;
 import android.os.INetworkManagementService;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
 import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.LocalLog;
@@ -631,6 +634,13 @@
 
         pw.println();
         pw.println(mTag + " connectivity packet log:");
+        pw.println();
+        pw.println("Debug with python and scapy via:");
+        pw.println("shell$ python");
+        pw.println(">>> from scapy import all as scapy");
+        pw.println(">>> scapy.Ether(\"<paste_hex_string>\".decode(\"hex\")).show2()");
+        pw.println();
+
         pw.increaseIndent();
         mConnectivityPacketLog.readOnlyLocalLog().dump(fd, pw, args);
         pw.decreaseIndent();
@@ -1020,14 +1030,16 @@
 
     private boolean startIPv6() {
         // Set privacy extensions.
+        final String PREFER_TEMPADDRS = "2";
         try {
-            mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
+            NetdService.run((INetd netd) -> {
+                netd.setProcSysNet(
+                        INetd.IPV6, INetd.CONF, mInterfaceName, "use_tempaddr",
+                        PREFER_TEMPADDRS);
+            });
             mNwService.enableIpv6(mInterfaceName);
-        } catch (RemoteException re) {
-            logError("Unable to change interface settings: %s", re);
-            return false;
-        } catch (IllegalStateException ie) {
-            logError("Unable to change interface settings: %s", ie);
+        } catch (IllegalStateException|RemoteException|ServiceSpecificException e) {
+            logError("Unable to change interface settings: %s", e);
             return false;
         }
 
diff --git a/services/net/java/android/net/util/NetdService.java b/services/net/java/android/net/util/NetdService.java
index 153cb50..6e69ff5 100644
--- a/services/net/java/android/net/util/NetdService.java
+++ b/services/net/java/android/net/util/NetdService.java
@@ -17,7 +17,10 @@
 package android.net.util;
 
 import android.net.INetd;
+import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
+import android.os.SystemClock;
 import android.util.Log;
 
 
@@ -27,15 +30,24 @@
 public class NetdService {
     private static final String TAG = NetdService.class.getSimpleName();
     private static final String NETD_SERVICE_NAME = "netd";
+    private static final long BASE_TIMEOUT_MS = 100;
+    private static final long MAX_TIMEOUT_MS = 1000;
+
 
     /**
+     * Return an INetd instance, or null if not available.
+     *
      * It is the caller's responsibility to check for a null return value
      * and to handle RemoteException errors from invocations on the returned
      * interface if, for example, netd dies and is restarted.
      *
+     * Returned instances of INetd should not be cached.
+     *
      * @return an INetd instance or null.
      */
     public static INetd getInstance() {
+        // NOTE: ServiceManager does no caching for the netd service,
+        // because netd is not one of the defined common services.
         final INetd netdInstance = INetd.Stub.asInterface(
                 ServiceManager.getService(NETD_SERVICE_NAME));
         if (netdInstance == null) {
@@ -43,4 +55,82 @@
         }
         return netdInstance;
     }
+
+    /**
+     * Blocks for a specified time until an INetd instance is available.
+     *
+     * It is the caller's responsibility to handle RemoteException errors
+     * from invocations on the returned interface if, for example, netd
+     * dies after this interface was returned.
+     *
+     * Returned instances of INetd should not be cached.
+     *
+     * Special values of maxTimeoutMs include: 0, meaning try to obtain an
+     * INetd instance only once, and -1 (or any value less than 0), meaning
+     * try to obtain an INetd instance indefinitely.
+     *
+     * @param maxTimeoutMs the maximum time to spend getting an INetd instance
+     * @return an INetd instance or null if no instance is available
+     * within |maxTimeoutMs| milliseconds.
+     */
+    public static INetd get(long maxTimeoutMs) {
+        if (maxTimeoutMs == 0) return getInstance();
+
+        final long stop = (maxTimeoutMs > 0)
+                ? SystemClock.elapsedRealtime() + maxTimeoutMs
+                : Long.MAX_VALUE;
+
+        long timeoutMs = 0;
+        while (true) {
+            final INetd netdInstance = getInstance();
+            if (netdInstance != null) {
+                return netdInstance;
+            }
+
+            final long remaining = stop - SystemClock.elapsedRealtime();
+            if (remaining <= 0) break;
+
+            // No netdInstance was received; sleep and retry.
+            timeoutMs = Math.min(timeoutMs + BASE_TIMEOUT_MS, MAX_TIMEOUT_MS);
+            timeoutMs = Math.min(timeoutMs, remaining);
+            try {
+                Thread.sleep(timeoutMs);
+            } catch (InterruptedException e) {}
+        }
+        return null;
+    }
+
+    /**
+     * Blocks until an INetd instance is available.
+     *
+     * It is the caller's responsibility to handle RemoteException errors
+     * from invocations on the returned interface if, for example, netd
+     * dies after this interface was returned.
+     *
+     * Returned instances of INetd should not be cached.
+     *
+     * @return an INetd instance.
+     */
+    public static INetd get() {
+        return get(-1);
+    }
+
+    public static interface NetdCommand {
+        void run(INetd netd) throws RemoteException;
+    }
+
+    /**
+     * Blocks until an INetd instance is availabe, and retries until either
+     * the command succeeds or a runtime exception is thrown.
+     */
+    public static void run(NetdCommand cmd) {
+        while (true) {
+            try {
+                cmd.run(get());
+                return;
+            } catch (RemoteException re) {
+                Log.e(TAG, "error communicating with netd: " + re);
+            }
+        }
+    }
 }
diff --git a/services/tests/notification/Android.mk b/services/tests/notification/Android.mk
index de9553a..a5d5570 100644
--- a/services/tests/notification/Android.mk
+++ b/services/tests/notification/Android.mk
@@ -30,6 +30,7 @@
 LOCAL_JACK_FLAGS := --multi-dex native
 
 LOCAL_PACKAGE_NAME := FrameworksNotificationTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
 
 LOCAL_CERTIFICATE := platform
 
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 5f215f9..27b9a88 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -203,7 +203,6 @@
     private void compareChannels(NotificationChannel expected, NotificationChannel actual) {
         assertEquals(expected.getId(), actual.getId());
         assertEquals(expected.getName(), actual.getName());
-        assertEquals(expected.getNameResId(), actual.getNameResId());
         assertEquals(expected.shouldVibrate(), actual.shouldVibrate());
         assertEquals(expected.shouldShowLights(), actual.shouldShowLights());
         assertEquals(expected.getImportance(), actual.getImportance());
@@ -219,7 +218,6 @@
     private void compareGroups(NotificationChannelGroup expected, NotificationChannelGroup actual) {
         assertEquals(expected.getId(), actual.getId());
         assertEquals(expected.getName(), actual.getName());
-        assertEquals(expected.getNameResId(), actual.getNameResId());
     }
 
     @Test
@@ -274,15 +272,12 @@
 
     @Test
     public void testChannelXml() throws Exception {
-        int nameResId = 924896;
-        int groupNameResId = 426272;
-
-        NotificationChannelGroup ncg = new NotificationChannelGroup("1", groupNameResId);
+        NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
         NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
         NotificationChannel channel1 =
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
         NotificationChannel channel2 =
-                new NotificationChannel("id2", nameResId, IMPORTANCE_LOW);
+                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
         channel2.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
         channel2.enableLights(true);
         channel2.setBypassDnd(true);
@@ -728,6 +723,25 @@
     }
 
     @Test
+    public void testGetDeletedChannelCount() throws Exception {
+        NotificationChannel channel =
+                new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
+        NotificationChannel channel2 =
+                new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
+        NotificationChannel channel3 =
+                new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(pkg, uid, channel, true);
+        mHelper.createNotificationChannel(pkg, uid, channel2, true);
+        mHelper.createNotificationChannel(pkg, uid, channel3, true);
+
+        mHelper.deleteNotificationChannel(pkg, uid, channel.getId());
+        mHelper.deleteNotificationChannel(pkg, uid, channel3.getId());
+
+        assertEquals(2, mHelper.getDeletedChannelCount(pkg, uid));
+        assertEquals(0, mHelper.getDeletedChannelCount(pkg2, uid2));
+    }
+
+    @Test
     public void testUpdateDeletedChannels() throws Exception {
         NotificationChannel channel =
                 new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
@@ -840,6 +854,43 @@
     }
 
     @Test
+    public void testDeleteGroup() throws Exception {
+        NotificationChannelGroup notDeleted = new NotificationChannelGroup("not", "deleted");
+        NotificationChannelGroup deleted = new NotificationChannelGroup("totally", "deleted");
+        NotificationChannel nonGroupedNonDeletedChannel =
+                new NotificationChannel("no group", "so not deleted", IMPORTANCE_HIGH);
+        NotificationChannel groupedButNotDeleted =
+                new NotificationChannel("not deleted", "belongs to notDeleted", IMPORTANCE_DEFAULT);
+        groupedButNotDeleted.setGroup("not");
+        NotificationChannel groupedAndDeleted =
+                new NotificationChannel("deleted", "belongs to deleted", IMPORTANCE_DEFAULT);
+        groupedAndDeleted.setGroup("totally");
+
+        mHelper.createNotificationChannelGroup(pkg, uid, notDeleted, true);
+        mHelper.createNotificationChannelGroup(pkg, uid, deleted, true);
+        mHelper.createNotificationChannel(pkg, uid, nonGroupedNonDeletedChannel, true);
+        mHelper.createNotificationChannel(pkg, uid, groupedAndDeleted, true);
+        mHelper.createNotificationChannel(pkg, uid, groupedButNotDeleted, true);
+
+        mHelper.deleteNotificationChannelGroup(pkg, uid, deleted.getId());
+
+        assertNull(mHelper.getNotificationChannelGroup(deleted.getId(), pkg, uid));
+        assertNotNull(mHelper.getNotificationChannelGroup(notDeleted.getId(), pkg, uid));
+
+        assertNull(mHelper.getNotificationChannel(pkg, uid, groupedAndDeleted.getId(), false));
+        compareChannels(groupedAndDeleted,
+                mHelper.getNotificationChannel(pkg, uid, groupedAndDeleted.getId(), true));
+
+        compareChannels(groupedButNotDeleted,
+                mHelper.getNotificationChannel(pkg, uid, groupedButNotDeleted.getId(), false));
+        compareChannels(nonGroupedNonDeletedChannel, mHelper.getNotificationChannel(
+                pkg, uid, nonGroupedNonDeletedChannel.getId(), false));
+
+        // notDeleted
+        assertEquals(1, mHelper.getNotificationChannelGroups(pkg, uid).size());
+    }
+
+    @Test
     public void testOnPackageChanged_packageRemoval() throws Exception {
         // Deleted
         NotificationChannel channel1 =
@@ -875,7 +926,7 @@
 
         mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{pkg}, new int[]{uid});
 
-        assertEquals(0, mHelper.getNotificationChannelGroups(pkg, uid, true).getList().size());
+        assertEquals(0, mHelper.getNotificationChannelGroups(pkg, uid).size());
     }
 
     @Test
@@ -994,14 +1045,18 @@
     }
 
     @Test
-    public void testCreateChannel_updateNameResId() throws Exception {
-        NotificationChannel nc = new NotificationChannel("id", 1, IMPORTANCE_DEFAULT);
+    public void testCreateChannel_updateName() throws Exception {
+        NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(pkg, uid, nc, true);
+        NotificationChannel actual = mHelper.getNotificationChannel(pkg, uid, "id", false);
+        assertEquals("hello", actual.getName());
+
+        nc = new NotificationChannel("id", "goodbye", IMPORTANCE_HIGH);
         mHelper.createNotificationChannel(pkg, uid, nc, true);
 
-        nc = new NotificationChannel("id", 2, IMPORTANCE_DEFAULT);
-        mHelper.createNotificationChannel(pkg, uid, nc, true);
-
-        assertEquals(2, mHelper.getNotificationChannel(pkg, uid, "id", false).getNameResId());
+        actual = mHelper.getNotificationChannel(pkg, uid, "id", false);
+        assertEquals("goodbye", actual.getName());
+        assertEquals(IMPORTANCE_DEFAULT, actual.getImportance());
     }
 
     @Test
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 15c61f6..2a8f4a3 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -30,6 +30,7 @@
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
 LOCAL_PACKAGE_NAME := FrameworksServicesTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
 
 LOCAL_CERTIFICATE := platform
 
diff --git a/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java
index 60842a6..340c624 100644
--- a/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/AccessibilityManagerServiceTest.java
@@ -34,6 +34,8 @@
 import android.view.accessibility.IAccessibilityManager;
 import android.view.accessibility.IAccessibilityManagerClient;
 
+import com.android.internal.util.IntPair;
+
 /**
  * This test exercises the
  * {@link com.android.server.accessibility.AccessibilityManagerService} by mocking the
@@ -109,7 +111,7 @@
 
         // invoke the method under test
         final int stateFlagsDisabled =
-                mManagerService.addClient(mockClient, UserHandle.USER_CURRENT);
+                IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT));
         boolean enabledAccessibilityDisabled =
             (stateFlagsDisabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
 
@@ -122,7 +124,7 @@
 
         // invoke the method under test
         final int stateFlagsEnabled =
-                mManagerService.addClient(mockClient, UserHandle.USER_CURRENT);
+                IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT));
         boolean enabledAccessibilityEnabled =
             (stateFlagsEnabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
 
@@ -144,7 +146,7 @@
 
         // invoke the method under test
         final int stateFlagsEnabled =
-                mManagerService.addClient(mockClient, UserHandle.USER_CURRENT);
+                IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT));
         boolean enabledAccessibilityEnabled =
             (stateFlagsEnabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
 
@@ -157,7 +159,7 @@
 
         // invoke the method under test
         final int stateFlagsDisabled =
-                mManagerService.addClient(mockClient, UserHandle.USER_CURRENT);
+                IntPair.first(mManagerService.addClient(mockClient, UserHandle.USER_CURRENT));
         boolean enabledAccessibilityDisabled =
             (stateFlagsDisabled & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
 
@@ -572,8 +574,9 @@
 
         public void notifyServicesStateChanged() {}
 
-        public void setTouchExplorationEnabled(boolean enabled) {
-        }
+        public void setRelevantEventTypes(int eventTypes) {}
+
+        public void setTouchExplorationEnabled(boolean enabled) {}
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java b/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java
index 6e3e6c6..9261771 100644
--- a/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/AccessibilityManagerTest.java
@@ -18,7 +18,6 @@
 
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -32,6 +31,8 @@
 import android.view.accessibility.IAccessibilityManager;
 import android.view.accessibility.IAccessibilityManagerClient;
 
+import com.android.internal.util.IntPair;
+
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -60,10 +61,12 @@
     private AccessibilityManager createManager(boolean enabled) throws Exception {
         if (enabled) {
             when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt()))
-                    .thenReturn(AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED);
+                    .thenReturn(
+                            IntPair.of(AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED,
+                                    AccessibilityEvent.TYPES_ALL_MASK));
         } else {
             when(mMockService.addClient(any(IAccessibilityManagerClient.class), anyInt()))
-                    .thenReturn(0);
+                    .thenReturn(IntPair.of(0, AccessibilityEvent.TYPES_ALL_MASK));
         }
 
         AccessibilityManager manager =
diff --git a/services/tests/servicestests/src/com/android/server/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/BaseLockSettingsServiceTests.java
index c6265bc..a2a4019 100644
--- a/services/tests/servicestests/src/com/android/server/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/BaseLockSettingsServiceTests.java
@@ -25,6 +25,8 @@
 
 import android.app.IActivityManager;
 import android.app.NotificationManager;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.database.sqlite.SQLiteDatabase;
@@ -76,7 +78,7 @@
     UserManager mUserManager;
     MockStorageManager mStorageManager;
     IActivityManager mActivityManager;
-
+    DevicePolicyManager mDevicePolicyManager;
     KeyStore mKeyStore;
 
     @Override
@@ -89,7 +91,9 @@
         mUserManager = mock(UserManager.class);
         mStorageManager = new MockStorageManager();
         mActivityManager = mock(IActivityManager.class);
-        mContext = new MockLockSettingsContext(getContext(), mUserManager, mNotificationManager);
+        mDevicePolicyManager = mock(DevicePolicyManager.class);
+        mContext = new MockLockSettingsContext(getContext(), mUserManager, mNotificationManager,
+                mDevicePolicyManager);
         mStorage = new LockSettingsStorageTestable(mContext,
                 new File(getContext().getFilesDir(), "locksettings"));
         File storageDir = mStorage.mStorageDir;
@@ -122,6 +126,10 @@
         });
 
         when(mLockPatternUtils.getLockSettings()).thenReturn(mService);
+
+        // Adding a fake Device Owner app which will enable escrow token support in LSS.
+        when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(
+                new ComponentName("com.dummy.package", ".FakeDeviceOwner"));
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
index c68fbdc..4677904 100644
--- a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java
@@ -21,6 +21,7 @@
 import static org.mockito.Mockito.when;
 
 import android.app.NotificationManager;
+import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.pm.UserInfo;
@@ -68,7 +69,7 @@
         when(mockUserManager.getProfileParent(eq(3))).thenReturn(new UserInfo(0, "name", 0));
 
         MockLockSettingsContext context = new MockLockSettingsContext(getContext(), mockUserManager,
-                mock(NotificationManager.class));
+                mock(NotificationManager.class), mock(DevicePolicyManager.class));
         mStorage = new LockSettingsStorageTestable(context,
                 new File(getContext().getFilesDir(), "locksettings"));
         mStorage.setDatabaseOnCreateCallback(new LockSettingsStorage.Callback() {
diff --git a/services/tests/servicestests/src/com/android/server/MockLockSettingsContext.java b/services/tests/servicestests/src/com/android/server/MockLockSettingsContext.java
index b63936f..8bceed4 100644
--- a/services/tests/servicestests/src/com/android/server/MockLockSettingsContext.java
+++ b/services/tests/servicestests/src/com/android/server/MockLockSettingsContext.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.app.NotificationManager;
+import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.os.UserManager;
@@ -25,12 +26,14 @@
 
     private UserManager mUserManager;
     private NotificationManager mNotificationManager;
+    private DevicePolicyManager mDevicePolicyManager;
 
     public MockLockSettingsContext(Context base, UserManager userManager,
-            NotificationManager notificationManager) {
+            NotificationManager notificationManager, DevicePolicyManager devicePolicyManager) {
         super(base);
         mUserManager = userManager;
         mNotificationManager = notificationManager;
+        mDevicePolicyManager = devicePolicyManager;
     }
 
     @Override
@@ -39,6 +42,8 @@
             return mUserManager;
         } else if (NOTIFICATION_SERVICE.equals(name)) {
             return mNotificationManager;
+        } else if (DEVICE_POLICY_SERVICE.equals(name)) {
+            return mDevicePolicyManager;
         } else {
             throw new RuntimeException("System service not mocked: " + name);
         }
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index fa0bd39..72fb78e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -61,7 +61,6 @@
 
     @Before
     public void setup() {
-
         mUser0 = 0;
         mUser1 = 1;
 
@@ -352,6 +351,15 @@
         assertNull(pui);
     }
 
+    @Test
+    public void testNotifyFrameworkLoad() {
+        String frameworkDex = "/system/framework/com.android.location.provider.jar";
+        // Load a dex file from framework.
+        notifyDexLoad(mFooUser0, Arrays.asList(frameworkDex), mUser0);
+        // The dex file should not be recognized as a package.
+        assertNull(mDexManager.getPackageUseInfo(frameworkDex));
+    }
+
     private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
             List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) {
         for (String dex : secondaries) {
diff --git a/services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java b/services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java
index 8329d68..4d5f783 100644
--- a/services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/AccessibilityShortcutControllerTest.java
@@ -21,9 +21,11 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.os.Handler;
+import android.os.Vibrator;
 import android.provider.Settings;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -54,6 +56,7 @@
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
+import static org.mockito.AdditionalMatchers.aryEq;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyObject;
@@ -67,6 +70,11 @@
 @RunWith(AndroidJUnit4.class)
 public class AccessibilityShortcutControllerTest {
     private static final String SERVICE_NAME_STRING = "fake.package/fake.service.name";
+    private static final long VIBRATOR_PATTERN_1 = 100L;
+    private static final long VIBRATOR_PATTERN_2 = 150L;
+    private static final int[] VIBRATOR_PATTERN_INT = {(int) VIBRATOR_PATTERN_1,
+            (int) VIBRATOR_PATTERN_2};
+    private static final long[] VIBRATOR_PATTERN_LONG = {VIBRATOR_PATTERN_1, VIBRATOR_PATTERN_2};
 
     private @Mock Context mContext;
     private @Mock FrameworkObjectProvider mFrameworkObjectProvider;
@@ -77,6 +85,8 @@
     private @Mock AccessibilityServiceInfo mServiceInfo;
     private @Mock Resources mResources;
     private @Mock Toast mToast;
+    private @Mock Vibrator mVibrator;
+    private @Mock ApplicationInfo mApplicationInfo;
 
     private MockContentResolver mContentResolver;
     private WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
@@ -85,10 +95,15 @@
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
+        when(mVibrator.hasVibrator()).thenReturn(true);
+
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo);
+        when(mContext.getSystemService(Context.VIBRATOR_SERVICE)).thenReturn(mVibrator);
+
         mContentResolver = new MockContentResolver(mContext);
         mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
         when(mContext.getContentResolver()).thenReturn(mContentResolver);
-        when(mContext.getResources()).thenReturn(mResources);
 
         when(mAccessibilityManagerService.getInstalledAccessibilityServiceList(anyInt()))
                 .thenReturn(Collections.singletonList(mServiceInfo));
@@ -104,6 +119,8 @@
                 .thenReturn(mToast);
 
         when(mResources.getString(anyInt())).thenReturn("Howdy %s");
+        when(mResources.getIntArray(anyInt())).thenReturn(VIBRATOR_PATTERN_INT);
+
         ResolveInfo resolveInfo = mock(ResolveInfo.class);
         when(resolveInfo.loadLabel(anyObject())).thenReturn("Service name");
         when(mServiceInfo.getResolveInfo()).thenReturn(resolveInfo);
@@ -171,6 +188,14 @@
     }
 
     @Test
+    public void testOnAccessibilityShortcut_vibrates() {
+        configureShortcutEnabled();
+        AccessibilityShortcutController accessibilityShortcutController = getController();
+        accessibilityShortcutController.performAccessibilityShortcut();
+        verify(mVibrator).vibrate(aryEq(VIBRATOR_PATTERN_LONG), eq(-1), anyObject());
+    }
+
+    @Test
     public void testOnAccessibilityShortcut_firstTime_showsWarningDialog()
             throws Exception {
         configureShortcutEnabled();
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index ebd4b01..169cf4d 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -105,7 +105,7 @@
         invalidateMounts();
 
         mHandler = new H(IoThread.get().getLooper());
-        mHandler.sendEmptyMessageDelayed(H.MSG_LOAD_CACHED_QUOTAS_FROM_FILE, DELAY_IN_MILLIS);
+        mHandler.sendEmptyMessage(H.MSG_LOAD_CACHED_QUOTAS_FROM_FILE);
 
         mStorage.registerListener(new StorageEventListener() {
             @Override
@@ -137,7 +137,8 @@
                         android.Manifest.permission.PACKAGE_USAGE_STATS, TAG);
                 return;
             default:
-                throw new SecurityException("Blocked by mode " + mode);
+                throw new SecurityException("Package " + callingPackage + " from UID " + callingUid
+                        + " blocked by mode " + mode);
         }
     }
 
@@ -169,16 +170,21 @@
         enforcePermission(Binder.getCallingUid(), callingPackage);
 
         long cacheBytes = 0;
-        for (UserInfo user : mUser.getUsers()) {
-            final StorageStats stats = queryStatsForUser(volumeUuid, user.id, null);
-            cacheBytes += stats.cacheBytes;
+        final long token = Binder.clearCallingIdentity();
+        try {
+            for (UserInfo user : mUser.getUsers()) {
+                final StorageStats stats = queryStatsForUser(volumeUuid, user.id, null);
+                cacheBytes += stats.cacheBytes;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
 
         if (volumeUuid == StorageManager.UUID_PRIVATE_INTERNAL) {
-            return Environment.getDataDirectory().getFreeSpace() + cacheBytes;
+            return Environment.getDataDirectory().getUsableSpace() + cacheBytes;
         } else {
             final VolumeInfo vol = mStorage.findVolumeByUuid(volumeUuid);
-            return vol.getPath().getFreeSpace() + cacheBytes;
+            return vol.getPath().getUsableSpace() + cacheBytes;
         }
     }
 
@@ -208,7 +214,8 @@
 
         final ApplicationInfo appInfo;
         try {
-            appInfo = mPackage.getApplicationInfoAsUser(packageName, 0, userId);
+            appInfo = mPackage.getApplicationInfoAsUser(packageName,
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
         } catch (NameNotFoundException e) {
             throw new IllegalStateException(e);
         }
@@ -251,8 +258,8 @@
 
         for (int i = 0; i < packageNames.length; i++) {
             try {
-                codePaths[i] = mPackage.getApplicationInfoAsUser(packageNames[i], 0,
-                        userId).getCodePath();
+                codePaths[i] = mPackage.getApplicationInfoAsUser(packageNames[i],
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES, userId).getCodePath();
             } catch (NameNotFoundException e) {
                 throw new IllegalStateException(e);
             }
@@ -284,7 +291,8 @@
         }
 
         int[] appIds = null;
-        for (ApplicationInfo app : mPackage.getInstalledApplicationsAsUser(0, userId)) {
+        for (ApplicationInfo app : mPackage.getInstalledApplicationsAsUser(
+                PackageManager.MATCH_UNINSTALLED_PACKAGES, userId)) {
             final int appId = UserHandle.getAppId(app.uid);
             if (!ArrayUtils.contains(appIds, appId)) {
                 appIds = ArrayUtils.appendInt(appIds, appId);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 632a1d6..7a226a0 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1312,6 +1312,84 @@
     public static final String KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY =
             "call_forwarding_blocks_while_roaming_string_array";
 
+    /**
+     * The day of the month (1-31) on which the data cycle rolls over.
+     * <p>
+     * If the current month does not have this day, the cycle will roll over at the start of the
+     * next month.
+     * <p>
+     * This setting may be still overridden by explicit user choice. By default, the platform value
+     * will be used.
+     */
+    public static final String KEY_MONTHLY_DATA_CYCLE_DAY_INT =
+            "monthly_data_cycle_day_int";
+
+    /**
+     * When {@link #KEY_MONTHLY_DATA_CYCLE_DAY_INT}, {@link #KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG},
+     * or {@link #KEY_DATA_WARNING_THRESHOLD_BYTES_LONG} are set to this value, the platform default
+     * value will be used for that key.
+     *
+     * @hide
+     */
+    public static final int DATA_CYCLE_USE_PLATFORM_DEFAULT = -1;
+
+    /**
+     * Flag indicating that a data cycle threshold should be disabled.
+     * <p>
+     * If {@link #KEY_DATA_WARNING_THRESHOLD_BYTES_LONG} is set to this value, the platform's
+     * default data warning, if one exists, will be disabled. A user selected data warning will not
+     * be overridden.
+     * <p>
+     * If {@link #KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG} is set to this value, the platform's
+     * default data limit, if one exists, will be disabled. A user selected data limit will not be
+     * overridden.
+     */
+    public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2;
+
+    /**
+     * Controls the data usage warning.
+     * <p>
+     * If the user uses more than this amount of data in their billing cycle, as defined by
+     * {@link #KEY_MONTHLY_DATA_CYCLE_DAY_INT}, the user will be alerted about the usage.
+     * If the value is set to {@link #DATA_CYCLE_THRESHOLD_DISABLED}, the data usage warning will
+     * be disabled.
+     * <p>
+     * This setting may be overridden by explicit user choice. By default, the platform value
+     * will be used.
+     */
+    public static final String KEY_DATA_WARNING_THRESHOLD_BYTES_LONG =
+            "data_warning_threshold_bytes_long";
+
+    /**
+     * Controls the cellular data limit.
+     * <p>
+     * If the user uses more than this amount of data in their billing cycle, as defined by
+     * {@link #KEY_MONTHLY_DATA_CYCLE_DAY_INT}, cellular data will be turned off by the user's
+     * phone. If the value is set to {@link #DATA_CYCLE_THRESHOLD_DISABLED}, the data limit will be
+     * disabled.
+     * <p>
+     * This setting may be overridden by explicit user choice. By default, the platform value
+     * will be used.
+     */
+    public static final String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG =
+            "data_limit_threshold_bytes_long";
+
+    /**
+     * Offset to be reduced from rsrp threshold while calculating signal strength level.
+     * @hide
+     */
+    public static final String KEY_LTE_EARFCNS_RSRP_BOOST_INT = "lte_earfcns_rsrp_boost_int";
+
+    /**
+     * List of EARFCN (E-UTRA Absolute Radio Frequency Channel Number,
+     * Reference: 3GPP TS 36.104 5.4.3) inclusive ranges on which lte_rsrp_boost_int
+     * will be applied. Format of the String array is expected to be {"erafcn1_start-earfcn1_end",
+     * "earfcn2_start-earfcn2_end" ... }
+     * @hide
+     */
+    public static final String KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY =
+            "boosted_lte_earfcns_string_array";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -1515,6 +1593,10 @@
                 });
         sDefaults.putStringArray(KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY, null);
 
+        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.putLong(KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG, DATA_CYCLE_USE_PLATFORM_DEFAULT);
+
         // Rat families: {GPRS, EDGE}, {EVDO, EVDO_A, EVDO_B}, {UMTS, HSPA, HSDPA, HSUPA, HSPAP},
         // {LTE, LTE_CA}
         // Order is important - lowest precidence first
@@ -1541,6 +1623,8 @@
         sDefaults.putBoolean(KEY_EDITABLE_TETHER_APN_BOOL, false);
         sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY,
                 null);
+        sDefaults.putInt(KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
+        sDefaults.putStringArray(KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY, null);
     }
 
     /**
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index a3e11c8..199a12a 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1620,7 +1620,7 @@
     //
     // Australia: Short codes are six or eight digits in length, starting with the prefix "19"
     //            followed by an additional four or six digits and two.
-    // Czech Republic: Codes are seven digits in length for MO and five (not billed) or
+    // Czechia: Codes are seven digits in length for MO and five (not billed) or
     //            eight (billed) for MT direction
     //
     // see http://en.wikipedia.org/wiki/Short_code#Regional_differences for reference
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 7a83979..5fb83ab 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -243,6 +243,10 @@
 
     private boolean mIsUsingCarrierAggregation;
 
+    /* EARFCN stands for E-UTRA Absolute Radio Frequency Channel Number,
+     * Reference: 3GPP TS 36.104 5.4.3 */
+    private int mLteEarfcnRsrpBoost = 0;
+
     /**
      * get String description of roaming type
      * @hide
@@ -322,6 +326,7 @@
         mIsEmergencyOnly = s.mIsEmergencyOnly;
         mIsDataRoamingFromRegistration = s.mIsDataRoamingFromRegistration;
         mIsUsingCarrierAggregation = s.mIsUsingCarrierAggregation;
+        mLteEarfcnRsrpBoost = s.mLteEarfcnRsrpBoost;
     }
 
     /**
@@ -351,6 +356,7 @@
         mIsEmergencyOnly = in.readInt() != 0;
         mIsDataRoamingFromRegistration = in.readInt() != 0;
         mIsUsingCarrierAggregation = in.readInt() != 0;
+        mLteEarfcnRsrpBoost = in.readInt();
     }
 
     public void writeToParcel(Parcel out, int flags) {
@@ -377,6 +383,7 @@
         out.writeInt(mIsEmergencyOnly ? 1 : 0);
         out.writeInt(mIsDataRoamingFromRegistration ? 1 : 0);
         out.writeInt(mIsUsingCarrierAggregation ? 1 : 0);
+        out.writeInt(mLteEarfcnRsrpBoost);
     }
 
     public int describeContents() {
@@ -814,7 +821,8 @@
                 + " DefRoamInd=" + mCdmaDefaultRoamingIndicator
                 + " EmergOnly=" + mIsEmergencyOnly
                 + " IsDataRoamingFromRegistration=" + mIsDataRoamingFromRegistration
-                + " IsUsingCarrierAggregation=" + mIsUsingCarrierAggregation);
+                + " IsUsingCarrierAggregation=" + mIsUsingCarrierAggregation
+                + " mLteEarfcnRsrpBoost=" + mLteEarfcnRsrpBoost);
     }
 
     private void setNullState(int state) {
@@ -842,6 +850,7 @@
         mIsEmergencyOnly = false;
         mIsDataRoamingFromRegistration = false;
         mIsUsingCarrierAggregation = false;
+        mLteEarfcnRsrpBoost = 0;
     }
 
     public void setStateOutOfService() {
@@ -1016,6 +1025,7 @@
         mIsEmergencyOnly = m.getBoolean("emergencyOnly");
         mIsDataRoamingFromRegistration = m.getBoolean("isDataRoamingFromRegistration");
         mIsUsingCarrierAggregation = m.getBoolean("isUsingCarrierAggregation");
+        mLteEarfcnRsrpBoost = m.getInt("LteEarfcnRsrpBoost");
     }
 
     /**
@@ -1046,6 +1056,7 @@
         m.putBoolean("emergencyOnly", mIsEmergencyOnly);
         m.putBoolean("isDataRoamingFromRegistration", mIsDataRoamingFromRegistration);
         m.putBoolean("isUsingCarrierAggregation", mIsUsingCarrierAggregation);
+        m.putInt("LteEarfcnRsrpBoost", mLteEarfcnRsrpBoost);
     }
 
     /** @hide */
@@ -1081,6 +1092,16 @@
     }
 
     /** @hide */
+    public int getLteEarfcnRsrpBoost() {
+        return mLteEarfcnRsrpBoost;
+    }
+
+    /** @hide */
+    public void setLteEarfcnRsrpBoost(int LteEarfcnRsrpBoost) {
+        mLteEarfcnRsrpBoost = LteEarfcnRsrpBoost;
+    }
+
+    /** @hide */
     public void setCssIndicator(int css) {
         this.mCssIndicator = (css != 0);
     }
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index c484fd3..9e02399 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -64,6 +64,8 @@
     private int mLteRsrq;
     private int mLteRssnr;
     private int mLteCqi;
+    private int mLteRsrpBoost; // offset to be reduced from the rsrp threshold while calculating
+                                // signal strength level
     private int mTdScdmaRscp;
 
     private boolean isGsm; // This value is set by the ServiceStateTracker onSignalStrengthResult
@@ -104,6 +106,7 @@
         mLteRsrq = INVALID;
         mLteRssnr = INVALID;
         mLteCqi = INVALID;
+        mLteRsrpBoost = 0;
         mTdScdmaRscp = INVALID;
         isGsm = true;
     }
@@ -129,6 +132,7 @@
         mLteRsrq = INVALID;
         mLteRssnr = INVALID;
         mLteCqi = INVALID;
+        mLteRsrpBoost = 0;
         mTdScdmaRscp = INVALID;
         isGsm = gsmFlag;
     }
@@ -142,10 +146,26 @@
             int cdmaDbm, int cdmaEcio,
             int evdoDbm, int evdoEcio, int evdoSnr,
             int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
+            int lteRsrpBoost, int tdScdmaRscp, boolean gsmFlag) {
+        initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
+                evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
+                lteRsrq, lteRssnr, lteCqi, lteRsrpBoost, gsmFlag);
+        mTdScdmaRscp = tdScdmaRscp;
+    }
+
+    /**
+     * Constructor
+     *
+     * @hide
+     */
+    public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
+            int cdmaDbm, int cdmaEcio,
+            int evdoDbm, int evdoEcio, int evdoSnr,
+            int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
             int tdScdmaRscp, boolean gsmFlag) {
         initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
                 evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
-                lteRsrq, lteRssnr, lteCqi, gsmFlag);
+                lteRsrq, lteRssnr, lteCqi, 0, gsmFlag);
         mTdScdmaRscp = tdScdmaRscp;
     }
 
@@ -161,7 +181,7 @@
             boolean gsmFlag) {
         initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
                 evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
-                lteRsrq, lteRssnr, lteCqi, gsmFlag);
+                lteRsrq, lteRssnr, lteCqi, 0, gsmFlag);
     }
 
     /**
@@ -175,7 +195,7 @@
             boolean gsmFlag) {
         initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
                 evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
-                INVALID, INVALID, INVALID, gsmFlag);
+                INVALID, INVALID, INVALID, 0, gsmFlag);
     }
 
     /**
@@ -209,7 +229,7 @@
             boolean gsm) {
         initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
                 evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
-                INVALID, INVALID, INVALID, gsm);
+                INVALID, INVALID, INVALID, 0, gsm);
     }
 
     /**
@@ -227,6 +247,7 @@
      * @param lteRsrq
      * @param lteRssnr
      * @param lteCqi
+     * @param lteRsrpBoost
      * @param gsm
      *
      * @hide
@@ -235,7 +256,7 @@
             int cdmaDbm, int cdmaEcio,
             int evdoDbm, int evdoEcio, int evdoSnr,
             int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
-            boolean gsm) {
+            int lteRsrpBoost, boolean gsm) {
         mGsmSignalStrength = gsmSignalStrength;
         mGsmBitErrorRate = gsmBitErrorRate;
         mCdmaDbm = cdmaDbm;
@@ -248,6 +269,7 @@
         mLteRsrq = lteRsrq;
         mLteRssnr = lteRssnr;
         mLteCqi = lteCqi;
+        mLteRsrpBoost = lteRsrpBoost;
         mTdScdmaRscp = INVALID;
         isGsm = gsm;
         if (DBG) log("initialize: " + toString());
@@ -269,6 +291,7 @@
         mLteRsrq = s.mLteRsrq;
         mLteRssnr = s.mLteRssnr;
         mLteCqi = s.mLteCqi;
+        mLteRsrpBoost = s.mLteRsrpBoost;
         mTdScdmaRscp = s.mTdScdmaRscp;
         isGsm = s.isGsm;
     }
@@ -293,6 +316,7 @@
         mLteRsrq = in.readInt();
         mLteRssnr = in.readInt();
         mLteCqi = in.readInt();
+        mLteRsrpBoost = in.readInt();
         mTdScdmaRscp = in.readInt();
         isGsm = (in.readInt() != 0);
     }
@@ -340,6 +364,7 @@
         out.writeInt(mLteRsrq);
         out.writeInt(mLteRssnr);
         out.writeInt(mLteCqi);
+        out.writeInt(mLteRsrpBoost);
         out.writeInt(mTdScdmaRscp);
         out.writeInt(isGsm ? 1 : 0);
     }
@@ -416,6 +441,18 @@
     }
 
     /**
+     * @param lteRsrpBoost - signal strength offset
+     *
+     * Used by phone to set the lte signal strength offset which will be
+     * reduced from rsrp threshold while calculating signal strength level
+     *
+     * @hide
+     */
+    public void setLteRsrpBoost(int lteRsrpBoost) {
+        mLteRsrpBoost = lteRsrpBoost;
+    }
+
+    /**
      * Get the GSM Signal Strength, valid values are (0-31, 99) as defined in TS
      * 27.007 8.5
      */
@@ -490,6 +527,11 @@
         return mLteCqi;
     }
 
+    /** @hide */
+    public int getLteRsrpBoost() {
+        return mLteRsrpBoost;
+    }
+
     /**
      * Retrieve an abstract level value for the overall signal strength.
      *
@@ -793,12 +835,19 @@
             Log.wtf(LOG_TAG, "getLteLevel - config_lteDbmThresholds has invalid num of elements."
                     + " Cannot evaluate RSRP signal.");
         } else {
-            if (mLteRsrp > threshRsrp[5]) rsrpIconLevel = -1;
-            else if (mLteRsrp >= threshRsrp[4]) rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
-            else if (mLteRsrp >= threshRsrp[3]) rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
-            else if (mLteRsrp >= threshRsrp[2]) rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
-            else if (mLteRsrp >= threshRsrp[1]) rsrpIconLevel = SIGNAL_STRENGTH_POOR;
-            else if (mLteRsrp >= threshRsrp[0]) rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+            if (mLteRsrp > threshRsrp[5]) {
+                rsrpIconLevel = -1;
+            } else if (mLteRsrp >= (threshRsrp[4] - mLteRsrpBoost)) {
+                rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
+            } else if (mLteRsrp >= (threshRsrp[3] - mLteRsrpBoost)) {
+                rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
+            } else if (mLteRsrp >= (threshRsrp[2] - mLteRsrpBoost)) {
+                rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
+            } else if (mLteRsrp >= (threshRsrp[1] - mLteRsrpBoost)) {
+                rsrpIconLevel = SIGNAL_STRENGTH_POOR;
+            } else if (mLteRsrp >= threshRsrp[0]) {
+                rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+            }
         }
 
         /*
@@ -816,7 +865,8 @@
             snrIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
 
         if (DBG) log("getLTELevel - rsrp:" + mLteRsrp + " snr:" + mLteRssnr + " rsrpIconLevel:"
-                + rsrpIconLevel + " snrIconLevel:" + snrIconLevel);
+                + rsrpIconLevel + " snrIconLevel:" + snrIconLevel
+                + " lteRsrpBoost:" + mLteRsrpBoost);
 
         /* Choose a measurement type to use for notification */
         if (snrIconLevel != -1 && rsrpIconLevel != -1) {
@@ -939,7 +989,7 @@
                 + (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum)
                 + (mLteSignalStrength * primeNum) + (mLteRsrp * primeNum)
                 + (mLteRsrq * primeNum) + (mLteRssnr * primeNum) + (mLteCqi * primeNum)
-                + (mTdScdmaRscp * primeNum) + (isGsm ? 1 : 0));
+                + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (isGsm ? 1 : 0));
     }
 
     /**
@@ -971,6 +1021,7 @@
                 && mLteRsrq == s.mLteRsrq
                 && mLteRssnr == s.mLteRssnr
                 && mLteCqi == s.mLteCqi
+                && mLteRsrpBoost == s.mLteRsrpBoost
                 && mTdScdmaRscp == s.mTdScdmaRscp
                 && isGsm == s.isGsm);
     }
@@ -993,6 +1044,7 @@
                 + " " + mLteRsrq
                 + " " + mLteRssnr
                 + " " + mLteCqi
+                + " " + mLteRsrpBoost
                 + " " + mTdScdmaRscp
                 + " " + (isGsm ? "gsm|lte" : "cdma"));
     }
@@ -1016,6 +1068,7 @@
         mLteRsrq = m.getInt("LteRsrq");
         mLteRssnr = m.getInt("LteRssnr");
         mLteCqi = m.getInt("LteCqi");
+        mLteRsrpBoost = m.getInt("lteRsrpBoost");
         mTdScdmaRscp = m.getInt("TdScdma");
         isGsm = m.getBoolean("isGsm");
     }
@@ -1039,6 +1092,7 @@
         m.putInt("LteRsrq", mLteRsrq);
         m.putInt("LteRssnr", mLteRssnr);
         m.putInt("LteCqi", mLteCqi);
+        m.putInt("lteRsrpBoost", mLteRsrpBoost);
         m.putInt("TdScdma", mTdScdmaRscp);
         m.putBoolean("isGsm", isGsm);
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 51b91f4..7862523 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5276,23 +5276,42 @@
         }
     }
 
+
+    /**
+     * @deprecated use {@link #isDataEnabled()} instead.
+     * @hide
+     */
+    @SystemApi
+    @Deprecated
+    public boolean getDataEnabled() {
+        return isDataEnabled();
+    }
+
     /**
      * Returns whether mobile data is enabled or not.
      *
-     * <p>Requires Permission:
-     *     {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
-     *     {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
-     *     calling app has carrier privileges.
+     * <p>Requires one of the following permissions:
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
+     * calling app has carrier privileges.
+     *
+     * <p>Note that this does not take into account any data restrictions that may be present on the
+     * calling app. Such restrictions may be inspected with
+     * {@link ConnectivityManager#getRestrictBackgroundStatus}.
      *
      * @return true if mobile data is enabled.
      *
      * @see #hasCarrierPrivileges
      */
-    public boolean getDataEnabled() {
+    @SuppressWarnings("deprecation")
+    public boolean isDataEnabled() {
         return getDataEnabled(getSubId());
     }
 
-    /** @hide */
+    /**
+     * @deprecated use {@link #isDataEnabled(int)} instead.
+     * @hide
+     */
     @SystemApi
     public boolean getDataEnabled(int subId) {
         boolean retVal = false;
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 29ba776..506f406 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -643,6 +643,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public void setUpdateAvailable(String packageName, boolean updateAvailable) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public String getInstallerPackageName(String packageName) {
         throw new UnsupportedOperationException();
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index 79f6e4d..504d54e 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -24,6 +24,7 @@
     android.test.runner
 
 LOCAL_PACKAGE_NAME := FrameworksNetTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
 
 LOCAL_CERTIFICATE := platform
 
diff --git a/tests/net/java/android/net/ip/IpManagerTest.java b/tests/net/java/android/net/ip/IpManagerTest.java
new file mode 100644
index 0000000..025b017
--- /dev/null
+++ b/tests/net/java/android/net/ip/IpManagerTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ip;
+
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.INetworkManagementService;
+import android.provider.Settings;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.mock.MockContentResolver;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.internal.R;
+
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for IpManager.
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IpManagerTest {
+    private static final int DEFAULT_AVOIDBADWIFI_CONFIG_VALUE = 1;
+
+    @Mock private Context mContext;
+    @Mock private INetworkManagementService mNMService;
+    @Mock private Resources mResources;
+    private MockContentResolver mContentResolver;
+
+    @Before public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mResources.getInteger(R.integer.config_networkAvoidBadWifi))
+                .thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE);
+
+        mContentResolver = new MockContentResolver();
+        mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        when(mContext.getContentResolver()).thenReturn(mContentResolver);
+    }
+
+    @Test
+    public void testNullCallbackDoesNotThrow() throws Exception {
+        final IpManager ipm = new IpManager(mContext, "lo", null, mNMService);
+    }
+
+    @Test
+    public void testInvalidInterfaceDoesNotThrow() throws Exception {
+        final IpManager.Callback cb = new IpManager.Callback();
+        final IpManager ipm = new IpManager(mContext, "test_wlan0", cb, mNMService);
+    }
+}
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index 11105d6..48861bd 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -304,6 +304,7 @@
         ConnectivityMetricsEvent ev = describeIpEvent(
                 aType(ApfProgramEvent.class),
                 aLong(200),
+                aLong(18),
                 anInt(7),
                 anInt(9),
                 anInt(2048),
@@ -320,7 +321,7 @@
                 "  apf_program_event <",
                 "    current_ras: 9",
                 "    drop_multicast: true",
-                "    effective_lifetime: 0",
+                "    effective_lifetime: 18",
                 "    filtered_ras: 7",
                 "    has_ipv4_addr: true",
                 "    lifetime: 200",
@@ -343,6 +344,8 @@
                 anInt(1),
                 anInt(2),
                 anInt(4),
+                anInt(7),
+                anInt(3),
                 anInt(2048));
 
         String want = joinLines(
@@ -360,8 +363,8 @@
                 "    max_program_size: 2048",
                 "    parse_errors: 2",
                 "    program_updates: 4",
-                "    program_updates_all: 0",
-                "    program_updates_allowing_multicast: 0",
+                "    program_updates_all: 7",
+                "    program_updates_allowing_multicast: 3",
                 "    received_ras: 10",
                 "    zero_lifetime_ras: 1",
                 "  >",
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 1f7c5f4..785e1ce 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -85,7 +85,7 @@
             new Thread() {
                 public void run() {
                     for (int j = 0; j < nEvents; j++) {
-                        assertTrue(logger.log(i * 100 + j, FAKE_EV));
+                        assertTrue(logger.log(1 + i * 100 + j, FAKE_EV));
                     }
                 }
             }.start();
@@ -96,7 +96,7 @@
         Iterator<ConnectivityMetricsEvent> iter = got.iterator();
         for (int i = 0; i < nCallers; i++) {
             for (int j = 0; j < nEvents; j++) {
-                int expectedTimestamp = i * 100 + j;
+                int expectedTimestamp = 1 + i * 100 + j;
                 assertEventsEqual(expectedEvent(expectedTimestamp), iter.next());
             }
         }
@@ -118,7 +118,7 @@
     @SmallTest
     public void testRateLimiting() {
         final IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
-        final ApfProgramEvent ev = new ApfProgramEvent(0, 0, 0, 0, 0);
+        final ApfProgramEvent ev = new ApfProgramEvent();
         final long fakeTimestamp = 1;
 
         int attempt = 100; // More than burst quota, but less than buffer size.
@@ -142,13 +142,24 @@
         // TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
         IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
 
+        ApfStats apfStats = new ApfStats();
+        apfStats.durationMs = 45000;
+        apfStats.receivedRas = 10;
+        apfStats.matchingRas = 2;
+        apfStats.droppedRas = 2;
+        apfStats.parseErrors = 2;
+        apfStats.zeroLifetimeRas = 1;
+        apfStats.programUpdates = 4;
+        apfStats.programUpdatesAll = 7;
+        apfStats.programUpdatesAllowingMulticast = 3;
+        apfStats.maxProgramSize = 2048;
         Parcelable[] events = {
             new IpReachabilityEvent("wlan0", IpReachabilityEvent.NUD_FAILED),
             new DhcpClientEvent("wlan0", "SomeState", 192),
             new DefaultNetworkEvent(102, new int[]{1,2,3}, 101, true, false),
             new IpManagerEvent("wlan0", IpManagerEvent.PROVISIONING_OK, 5678),
             new ValidationProbeEvent(120, 40730, ValidationProbeEvent.PROBE_HTTP, 204),
-            new ApfStats(45000, 10, 2, 2, 1, 2, 4, 2048),
+            apfStats,
             new RaEvent(2000, 400, 300, -1, 1000, -1)
         };
 
@@ -240,8 +251,8 @@
                 "    max_program_size: 2048",
                 "    parse_errors: 2",
                 "    program_updates: 4",
-                "    program_updates_all: 0",
-                "    program_updates_allowing_multicast: 0",
+                "    program_updates_all: 7",
+                "    program_updates_allowing_multicast: 3",
                 "    received_ras: 10",
                 "    zero_lifetime_ras: 1",
                 "  >",
@@ -304,14 +315,15 @@
     }
 
     static ConnectivityMetricsEvent expectedEvent(int timestamp) {
-        return new ConnectivityMetricsEvent((long)timestamp, 0, 0, FAKE_EV);
+        ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent();
+        ev.timestamp = timestamp;
+        ev.data = FAKE_EV;
+        return ev;
     }
 
     /** Outer equality for ConnectivityMetricsEvent to avoid overriding equals() and hashCode(). */
     static void assertEventsEqual(ConnectivityMetricsEvent expected, ConnectivityMetricsEvent got) {
         assertEquals(expected.timestamp, got.timestamp);
-        assertEquals(expected.componentTag, got.componentTag);
-        assertEquals(expected.eventTag, got.eventTag);
         assertEquals(expected.data, got.data);
     }
 
diff --git a/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java b/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java
index c5965e8..5064b9b 100644
--- a/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java
+++ b/tests/net/java/com/android/server/connectivity/MetricsTestUtil.java
@@ -28,7 +28,10 @@
     }
 
     static ConnectivityMetricsEvent ev(Parcelable p) {
-        return new ConnectivityMetricsEvent(1L, 0, 0, p);
+        ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent();
+        ev.timestamp = 1L;
+        ev.data = p;
+        return ev;
     }
 
     static ConnectivityMetricsEvent describeIpEvent(Consumer<Parcel>... fs) {
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index 9e0f3213..0ab4406 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -23,6 +23,7 @@
 import android.net.metrics.DnsEvent;
 import android.net.metrics.INetdEventListener;
 import android.net.metrics.IpConnectivityLog;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.system.OsConstants;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -165,8 +166,8 @@
         // call onLost() asynchronously to logDnsAsync's onDnsEvent() calls.
         mCallbackCaptor.getValue().onLost(new Network(105));
 
-        // do not verify unpredictable batch
-        verify(mLog, timeout(500).times(1)).log(any());
+        // do not verify batch with unpredictable length
+        verify(mLog, timeout(500).times(1)).log(any(Parcelable.class));
     }
 
     @SmallTest
@@ -279,11 +280,7 @@
     }
 
     void logDnsAsync(int netId, int[] latencies) {
-        new Thread() {
-            public void run() {
-                log(netId, latencies);
-            }
-        }.start();
+        new Thread(() -> log(netId, latencies)).start();
     }
 
     void verifyLoggedDnsEvents(DnsEvent... expected) {
diff --git a/tests/testables/Android.mk b/tests/testables/Android.mk
new file mode 100644
index 0000000..58399fd
--- /dev/null
+++ b/tests/testables/Android.mk
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := testables
+LOCAL_MODULE_TAG := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    mockito-updated-target-minus-junit4 \
+    legacy-android-test
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysUIRunner.java b/tests/testables/src/android/testing/AndroidTestingRunner.java
similarity index 89%
rename from packages/SystemUI/tests/src/com/android/systemui/SysUIRunner.java
rename to tests/testables/src/android/testing/AndroidTestingRunner.java
index fd99d1d..816ed03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysUIRunner.java
+++ b/tests/testables/src/android/testing/AndroidTestingRunner.java
@@ -12,14 +12,14 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui;
+package android.testing;
 
 import android.support.test.internal.runner.junit4.statement.RunAfters;
 import android.support.test.internal.runner.junit4.statement.RunBefores;
 import android.support.test.internal.runner.junit4.statement.UiThreadStatement;
 
-import com.android.systemui.utils.TestableLooper.LooperStatement;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.TestableLooper.LooperStatement;
+import android.testing.TestableLooper.RunWithLooper;
 
 import org.junit.After;
 import org.junit.Before;
@@ -32,12 +32,15 @@
 
 import java.util.List;
 
-public class SysUIRunner extends BlockJUnit4ClassRunner {
+/**
+ * A runner with support for extra annotations provided by the Testables library.
+ */
+public class AndroidTestingRunner extends BlockJUnit4ClassRunner {
 
     private final long mTimeout;
     private final Class<?> mKlass;
 
-    public SysUIRunner(Class<?> klass) throws InitializationError {
+    public AndroidTestingRunner(Class<?> klass) throws InitializationError {
         super(klass);
         mKlass = klass;
         // Can't seem to get reference to timeout parameter from here, so set default to 10 mins.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java b/tests/testables/src/android/testing/BaseFragmentTest.java
similarity index 86%
rename from packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java
rename to tests/testables/src/android/testing/BaseFragmentTest.java
index 1678d92..53841d5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/FragmentTestCase.java
+++ b/tests/testables/src/android/testing/BaseFragmentTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
  * except in compliance with the License. You may obtain a copy of the License at
@@ -12,7 +12,9 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui;
+package android.testing;
+
+import static org.junit.Assert.assertNotNull;
 
 import android.annotation.Nullable;
 import android.app.Fragment;
@@ -21,20 +23,17 @@
 import android.app.FragmentManagerNonConfig;
 import android.graphics.PixelFormat;
 import android.os.Handler;
-import android.os.Looper;
 import android.os.Parcelable;
+import android.support.test.InstrumentationRegistry;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.widget.FrameLayout;
 
-import com.android.systemui.utils.TestableLooper;
-import com.android.systemui.utils.ViewUtils;
-import com.android.systemui.utils.leaks.LeakCheckedTest;
-
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 
 import java.io.FileDescriptor;
@@ -46,7 +45,7 @@
  * the host for subclasses, so they can push it into desired states and do any unit testing
  * required.
  */
-public abstract class FragmentTestCase extends LeakCheckedTest {
+public abstract class BaseFragmentTest {
 
     private static final int VIEW_ID = 42;
     private final Class<? extends Fragment> mCls;
@@ -55,7 +54,10 @@
     protected FragmentController mFragments;
     protected Fragment mFragment;
 
-    public FragmentTestCase(Class<? extends Fragment> cls) {
+    @Rule
+    public final TestableContext mContext = getContext();
+
+    public BaseFragmentTest(Class<? extends Fragment> cls) {
         mCls = cls;
     }
 
@@ -64,6 +66,8 @@
         mView = new FrameLayout(mContext);
         mView.setId(VIEW_ID);
 
+        assertNotNull("BaseFragmentTest must be tagged with @RunWithLooper",
+                TestableLooper.get(this));
         TestableLooper.get(this).runWithLooper(() -> {
             mHandler = new Handler();
 
@@ -76,8 +80,8 @@
         });
     }
 
-    private String hex(Looper looper) {
-        return Integer.toHexString(System.identityHashCode(looper));
+    protected TestableContext getContext() {
+        return new TestableContext(InstrumentationRegistry.getContext());
     }
 
     @After
@@ -174,14 +178,14 @@
         return mView.findViewById(id);
     }
 
-    private class HostCallbacks extends FragmentHostCallback<FragmentTestCase> {
+    private class HostCallbacks extends FragmentHostCallback<BaseFragmentTest> {
         public HostCallbacks() {
-            super(mContext, FragmentTestCase.this.mHandler, 0);
+            super(mContext, BaseFragmentTest.this.mHandler, 0);
         }
 
         @Override
-        public FragmentTestCase onGetHost() {
-            return FragmentTestCase.this;
+        public BaseFragmentTest onGetHost() {
+            return BaseFragmentTest.this;
         }
 
         @Override
@@ -220,7 +224,7 @@
         @Nullable
         @Override
         public View onFindViewById(int id) {
-            return FragmentTestCase.this.findViewById(id);
+            return BaseFragmentTest.this.findViewById(id);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java b/tests/testables/src/android/testing/LayoutInflaterBuilder.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java
rename to tests/testables/src/android/testing/LayoutInflaterBuilder.java
index 5cfe677..098302e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/LayoutInflaterBuilder.java
+++ b/tests/testables/src/android/testing/LayoutInflaterBuilder.java
@@ -1,20 +1,18 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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
  *
- * 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.
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
  */
 
-package com.android.systemui.util;
+package android.testing;
 
 import android.annotation.NonNull;
 import android.content.Context;
diff --git a/tests/testables/src/android/testing/LeakCheck.java b/tests/testables/src/android/testing/LeakCheck.java
new file mode 100644
index 0000000..8daaa8f
--- /dev/null
+++ b/tests/testables/src/android/testing/LeakCheck.java
@@ -0,0 +1,101 @@
+/*
+ * 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.testing;
+
+import android.util.ArrayMap;
+import android.util.Log;
+
+import org.junit.Assert;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class LeakCheck extends TestWatcher {
+
+    private final Map<String, Tracker> mTrackers = new HashMap<>();
+
+    public LeakCheck() {
+    }
+
+    @Override
+    protected void succeeded(Description description) {
+        verify();
+    }
+
+    public Tracker getTracker(String tag) {
+        Tracker t = mTrackers.get(tag);
+        if (t == null) {
+            t = new Tracker();
+            mTrackers.put(tag, t);
+        }
+        return t;
+    }
+
+    public void verify() {
+        mTrackers.values().forEach(Tracker::verify);
+    }
+
+    public static class LeakInfo {
+        private static final String TAG = "LeakInfo";
+        private List<Throwable> mThrowables = new ArrayList<>();
+
+        LeakInfo() {
+        }
+
+        public void addAllocation(Throwable t) {
+            // TODO: Drop off the first element in the stack trace here to have a cleaner stack.
+            mThrowables.add(t);
+        }
+
+        public void clearAllocations() {
+            mThrowables.clear();
+        }
+
+        void verify() {
+            if (mThrowables.size() == 0) return;
+            Log.e(TAG, "Listener or binding not properly released");
+            for (Throwable t : mThrowables) {
+                Log.e(TAG, "Allocation found", t);
+            }
+            StringWriter writer = new StringWriter();
+            mThrowables.get(0).printStackTrace(new PrintWriter(writer));
+            Assert.fail("Listener or binding not properly released\n"
+                    + writer.toString());
+        }
+    }
+
+    public static class Tracker {
+        private Map<Object, LeakInfo> mObjects = new ArrayMap<>();
+
+        public LeakInfo getLeakInfo(Object object) {
+            LeakInfo leakInfo = mObjects.get(object);
+            if (leakInfo == null) {
+                leakInfo = new LeakInfo();
+                mObjects.put(object, leakInfo);
+            }
+            return leakInfo;
+        }
+
+        void verify() {
+            mObjects.values().forEach(LeakInfo::verify);
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeContentResolver.java b/tests/testables/src/android/testing/TestableContentResolver.java
similarity index 94%
rename from packages/SystemUI/tests/src/com/android/systemui/utils/FakeContentResolver.java
rename to tests/testables/src/android/testing/TestableContentResolver.java
index 34f2e01..bfafbe0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeContentResolver.java
+++ b/tests/testables/src/android/testing/TestableContentResolver.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
  * except in compliance with the License. You may obtain a copy of the License at
@@ -12,7 +12,7 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.utils;
+package android.testing;
 
 import android.content.ContentProvider;
 import android.content.ContentResolver;
@@ -29,14 +29,14 @@
 /**
  * Alternative to a MockContentResolver that falls back to real providers.
  */
-public class FakeContentResolver extends ContentResolver {
+public class TestableContentResolver extends ContentResolver {
 
     private final Map<String, ContentProvider> mProviders = Maps.newHashMap();
     private final ContentResolver mParent;
     private final ArraySet<ContentProvider> mInUse = new ArraySet<>();
     private boolean mFallbackToExisting;
 
-    public FakeContentResolver(Context context) {
+    public TestableContentResolver(Context context) {
         super(context);
         mParent = context.getContentResolver();
         mFallbackToExisting = true;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java b/tests/testables/src/android/testing/TestableContext.java
similarity index 70%
rename from packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java
rename to tests/testables/src/android/testing/TestableContext.java
index 1429390..cb5d4cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableContext.java
+++ b/tests/testables/src/android/testing/TestableContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
  * except in compliance with the License. You may obtain a copy of the License at
@@ -12,7 +12,7 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.utils;
+package android.testing;
 
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks;
@@ -32,36 +32,59 @@
 import android.util.ArrayMap;
 import android.view.LayoutInflater;
 
-import com.android.systemui.SysUiServiceProvider;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.utils.leaks.Tracker;
+import org.junit.rules.TestRule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
 
-public class TestableContext extends ContextWrapper implements SysUiServiceProvider {
+/**
+ * A ContextWrapper with utilities specifically designed to make Testing easier.
+ *
+ * <ul>
+ * <li>System services can be mocked out with {@link #addMockSystemService}</li>
+ * <li>Service binding can be mocked out with {@link #addMockService}</li>
+ * <li>Settings support {@link TestableSettings}</li>
+ * <li>Has support for {@link LeakCheck} for services and receivers</li>
+ * </ul>
+ *
+ * <p>TestableContext should be defined as a rule on your test so it can clean up after itself.
+ * Like the following:</p>
+ * <pre class="prettyprint">
+ * {@literal
+ * @Rule
+ * private final TestableContext mContext = new TestableContext(InstrumentationRegister.getContext());
+ * }
+ * </pre>
+ */
+public class TestableContext extends ContextWrapper implements TestRule {
 
-    private final FakeContentResolver mFakeContentResolver;
-    private final FakeSettingsProvider mSettingsProvider;
+    private final TestableContentResolver mTestableContentResolver;
+    private final TestableSettings mSettingsProvider;
 
     private ArrayMap<String, Object> mMockSystemServices;
     private ArrayMap<ComponentName, IBinder> mMockServices;
     private ArrayMap<ServiceConnection, ComponentName> mActiveServices;
-    private ArrayMap<Class<?>, Object> mComponents;
 
     private PackageManager mMockPackageManager;
-    private Tracker mReceiver;
-    private Tracker mService;
-    private Tracker mComponent;
+    private LeakCheck.Tracker mReceiver;
+    private LeakCheck.Tracker mService;
+    private LeakCheck.Tracker mComponent;
 
-    public TestableContext(Context base, SysuiTestCase test) {
+    public TestableContext(Context base) {
+        this(base, null);
+    }
+
+    public TestableContext(Context base, LeakCheck check) {
         super(base);
-        mFakeContentResolver = new FakeContentResolver(base);
+        mTestableContentResolver = new TestableContentResolver(base);
         ContentProviderClient settings = base.getContentResolver()
                 .acquireContentProviderClient(Settings.AUTHORITY);
-        mSettingsProvider = FakeSettingsProvider.getFakeSettingsProvider(settings,
-                mFakeContentResolver);
-        mFakeContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider);
-        mReceiver = test.getTracker("receiver");
-        mService = test.getTracker("service");
-        mComponent = test.getTracker("component");
+        mSettingsProvider = TestableSettings.getFakeSettingsProvider(settings,
+                mTestableContentResolver);
+        mTestableContentResolver.addProvider(Settings.AUTHORITY, mSettingsProvider.getProvider());
+        mReceiver = check != null ? check.getTracker("receiver") : null;
+        mService = check != null ? check.getTracker("service") : null;
+        mComponent = check != null ? check.getTracker("component") : null;
     }
 
     public void setMockPackageManager(PackageManager mock) {
@@ -86,19 +109,15 @@
     }
 
     public void addMockSystemService(String name, Object service) {
-        mMockSystemServices = lazyInit(mMockSystemServices);
+        if (mMockSystemServices == null) mMockSystemServices = new ArrayMap<>();
         mMockSystemServices.put(name, service);
     }
 
     public void addMockService(ComponentName component, IBinder service) {
-        mMockServices = lazyInit(mMockServices);
+        if (mMockServices == null) mMockServices = new ArrayMap<>();
         mMockServices.put(component, service);
     }
 
-    private <T, V> ArrayMap<T, V> lazyInit(ArrayMap<T, V> services) {
-        return services != null ? services : new ArrayMap<T, V>();
-    }
-
     @Override
     public Object getSystemService(String name) {
         if (mMockSystemServices != null && mMockSystemServices.containsKey(name)) {
@@ -110,13 +129,13 @@
         return super.getSystemService(name);
     }
 
-    public FakeSettingsProvider getSettingsProvider() {
+    public TestableSettings getSettingsProvider() {
         return mSettingsProvider;
     }
 
     @Override
-    public FakeContentResolver getContentResolver() {
-        return mFakeContentResolver;
+    public TestableContentResolver getContentResolver() {
+        return mTestableContentResolver;
     }
 
     @Override
@@ -177,7 +196,7 @@
 
     private boolean checkMocks(ComponentName component, ServiceConnection conn) {
         if (mMockServices != null && component != null && mMockServices.containsKey(component)) {
-            mActiveServices = lazyInit(mActiveServices);
+            if (mActiveServices == null) mActiveServices = new ArrayMap<>();
             mActiveServices.put(conn, component);
             conn.onServiceConnected(component, mMockServices.get(component));
             return true;
@@ -212,13 +231,18 @@
         super.unregisterComponentCallbacks(callback);
     }
 
-    @SuppressWarnings("unchecked")
-    public <T> T getComponent(Class<T> interfaceType) {
-        return (T) (mComponents != null ? mComponents.get(interfaceType) : null);
-    }
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return new TestWatcher() {
+            @Override
+            protected void succeeded(Description description) {
+                mSettingsProvider.clearOverrides();
+            }
 
-    public <T, C extends T> void putComponent(Class<T> interfaceType, C component) {
-        mComponents = lazyInit(mComponents);
-        mComponents.put(interfaceType, component);
+            @Override
+            protected void failed(Throwable e, Description description) {
+                mSettingsProvider.clearOverrides();
+            }
+        }.apply(base, description);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableImageView.java b/tests/testables/src/android/testing/TestableImageView.java
similarity index 92%
rename from packages/SystemUI/tests/src/com/android/systemui/utils/TestableImageView.java
rename to tests/testables/src/android/testing/TestableImageView.java
index b131460..901e25b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableImageView.java
+++ b/tests/testables/src/android/testing/TestableImageView.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
  * except in compliance with the License. You may obtain a copy of the License at
@@ -12,7 +12,7 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.utils;
+package android.testing;
 
 import android.annotation.DrawableRes;
 import android.annotation.Nullable;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooper.java
rename to tests/testables/src/android/testing/TestableLooper.java
index 8902e0c..8a33cf9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -12,7 +12,7 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.utils;
+package android.testing;
 
 import android.os.Handler;
 import android.os.Looper;
diff --git a/tests/testables/src/android/testing/TestableSettings.java b/tests/testables/src/android/testing/TestableSettings.java
new file mode 100644
index 0000000..d19f1ef
--- /dev/null
+++ b/tests/testables/src/android/testing/TestableSettings.java
@@ -0,0 +1,318 @@
+/*
+ * 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.testing;
+
+import android.content.ContentProvider;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.support.annotation.VisibleForTesting;
+import android.test.mock.MockContentProvider;
+import android.testing.TestableSettings.SettingOverrider.Builder;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Allows calls to android.provider.Settings to be tested easier.  A SettingOverride
+ * can be acquired and a set of specific settings can be set to a value (and not changed
+ * in the system when set), so that they can be tested without breaking the test device.
+ * <p>
+ * To use, in the before method acquire the override add all settings that will affect if
+ * your test passes or not.
+ *
+ * <pre class="prettyprint">
+ * {@literal
+ * mSettingOverride = mTestableContext.getSettingsProvider().acquireOverridesBuilder()
+ * .addSetting("secure", Secure.USER_SETUP_COMPLETE, "0")
+ * .build();
+ * }
+ * </pre>
+ *
+ * Then in the after free up the settings.
+ *
+ * <pre class="prettyprint">
+ * {@literal
+ * mSettingOverride.release();
+ * }
+ * </pre>
+ */
+public class TestableSettings {
+
+    private static final String TAG = "TestableSettings";
+    private static final boolean DEBUG = false;
+
+    // Number of times to try to acquire a setting if in use.
+    private static final int MAX_TRIES = 10;
+    // Time to wait for each setting.  WAIT_TIMEOUT * MAX_TRIES will be the maximum wait time
+    // for a setting.
+    private static final long WAIT_TIMEOUT = 1000;
+
+    private static TestableSettingsProvider sInstance;
+
+    private final TestableSettingsProvider mProvider;
+
+    private TestableSettings(TestableSettingsProvider provider) {
+        mProvider = provider;
+    }
+
+    public Builder acquireOverridesBuilder() {
+        return new Builder(this);
+    }
+
+    public void clearOverrides() {
+        List<SettingOverrider> overrides = mProvider.mOwners.remove(this);
+        if (overrides != null) {
+            overrides.forEach(override -> override.ensureReleased());
+        }
+    }
+
+    private void acquireSettings(SettingOverrider overridder, Set<String> keys)
+            throws AcquireTimeoutException {
+        mProvider.acquireSettings(overridder, keys, this);
+    }
+
+    ContentProvider getProvider() {
+        return mProvider;
+    }
+
+    @VisibleForTesting
+    Object getLock() {
+        return mProvider.mOverrideMap;
+    }
+
+    public static class SettingOverrider {
+        private final Set<String> mValidKeys;
+        private final Map<String, String> mValueMap = new ArrayMap<>();
+        private final TestableSettings mSettings;
+        private boolean mReleased;
+        public Throwable mObtain;
+
+        private SettingOverrider(Set<String> keys, TestableSettings provider) {
+            mValidKeys = new ArraySet<>(keys);
+            mSettings = provider;
+        }
+
+        private void ensureReleased() {
+            if (!mReleased) {
+                release();
+            }
+        }
+
+        public void release() {
+            mSettings.mProvider.releaseSettings(mValidKeys);
+            mReleased = true;
+        }
+
+        private void putDirect(String key, String value) {
+            mValueMap.put(key, value);
+        }
+
+        public void put(String table, String key, String value) {
+            if (!mValidKeys.contains(key(table, key))) {
+                throw new IllegalArgumentException("Key " + table + " " + key
+                        + " not acquired for this overrider");
+            }
+            mValueMap.put(key(table, key), value);
+        }
+
+        public void remove(String table, String key) {
+            if (!mValidKeys.contains(key(table, key))) {
+                throw new IllegalArgumentException("Key " + table + " " + key
+                        + " not acquired for this overrider");
+            }
+            mValueMap.remove(key(table, key));
+        }
+
+        public String get(String table, String key) {
+            if (!mValidKeys.contains(key(table, key))) {
+                throw new IllegalArgumentException("Key " + table + " " + key
+                        + " not acquired for this overrider");
+            }
+            Log.d(TAG, "Get " + table + " " + key + " " + mValueMap.get(key(table, key)));
+            return mValueMap.get(key(table, key));
+        }
+
+        public static class Builder {
+            private final TestableSettings mProvider;
+            private Set<String> mKeys = new ArraySet<>();
+            private Map<String, String> mValues = new ArrayMap<>();
+
+            private Builder(TestableSettings provider) {
+                mProvider = provider;
+            }
+
+            public Builder addSetting(String table, String key) {
+                mKeys.add(key(table, key));
+                return this;
+            }
+
+            public Builder addSetting(String table, String key, String value) {
+                addSetting(table, key);
+                mValues.put(key(table, key), value);
+                return this;
+            }
+
+            public SettingOverrider build() throws AcquireTimeoutException {
+                SettingOverrider overrider = new SettingOverrider(mKeys, mProvider);
+                mProvider.acquireSettings(overrider, mKeys);
+                mValues.forEach((key, value) -> overrider.putDirect(key, value));
+                return overrider;
+            }
+        }
+    }
+
+    private static class TestableSettingsProvider extends MockContentProvider {
+
+        private final Map<String, SettingOverrider> mOverrideMap = new ArrayMap<>();
+        private final Map<Object, List<SettingOverrider>> mOwners = new ArrayMap<>();
+
+        private final ContentProviderClient mSettings;
+        private final ContentResolver mResolver;
+
+        public TestableSettingsProvider(ContentProviderClient settings, ContentResolver resolver) {
+            mSettings = settings;
+            mResolver = resolver;
+        }
+
+        private void releaseSettings(Set<String> keys) {
+            synchronized (mOverrideMap) {
+                for (String key : keys) {
+                    if (DEBUG) Log.d(TAG, "Releasing " + key);
+                    mOverrideMap.remove(key);
+                }
+                if (DEBUG) Log.d(TAG, "Notifying");
+                mOverrideMap.notify();
+            }
+        }
+
+        private boolean checkKeysLocked(Set<String> keys, boolean shouldThrow)
+                throws AcquireTimeoutException {
+            for (String key : keys) {
+                if (mOverrideMap.containsKey(key)) {
+                    if (shouldThrow) {
+                        if (DEBUG) Log.e(TAG, "Lock obtained at",
+                                mOverrideMap.get(key).mObtain);
+                        throw new AcquireTimeoutException("Could not acquire " + key,
+                                mOverrideMap.get(key).mObtain);
+                    }
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private void acquireSettings(SettingOverrider overridder, Set<String> keys,
+                Object owner) throws AcquireTimeoutException {
+            synchronized (mOwners) {
+                List<SettingOverrider> list = mOwners.get(owner);
+                if (list == null) {
+                    list = new ArrayList<>();
+                    mOwners.put(owner, list);
+                }
+                list.add(overridder);
+            }
+            synchronized (mOverrideMap) {
+                for (int i = 0; i < MAX_TRIES; i++) {
+                    if (checkKeysLocked(keys, false)) break;
+                    try {
+                        if (DEBUG) Log.d(TAG, "Waiting for contention to finish");
+                        mOverrideMap.wait(WAIT_TIMEOUT);
+                    } catch (InterruptedException e) {
+                    }
+                }
+                overridder.mObtain = new Throwable();
+                checkKeysLocked(keys, true);
+                for (String key : keys) {
+                    if (DEBUG) Log.d(TAG, "Acquiring " + key);
+                    mOverrideMap.put(key, overridder);
+                }
+            }
+        }
+
+        public Bundle call(String method, String arg, Bundle extras) {
+            // Methods are "GET_system", "GET_global", "PUT_secure", etc.
+            final String[] commands = method.split("_", 2);
+            final String op = commands[0];
+            final String table = commands[1];
+
+            synchronized (mOverrideMap) {
+                SettingOverrider overrider = mOverrideMap.get(key(table, arg));
+                if (overrider == null) {
+                    // Fall through to real settings.
+                    try {
+                        if (DEBUG) Log.d(TAG, "Falling through to real settings " + method);
+                        // TODO: Add our own version of caching to handle this.
+                        Bundle call = mSettings.call(method, arg, extras);
+                        call.remove(Settings.CALL_METHOD_TRACK_GENERATION_KEY);
+                        return call;
+                    } catch (RemoteException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+                String value;
+                Bundle out = new Bundle();
+                switch (op) {
+                    case "GET":
+                        value = overrider.get(table, arg);
+                        if (value != null) {
+                            out.putString(Settings.NameValueTable.VALUE, value);
+                        }
+                        break;
+                    case "PUT":
+                        value = extras.getString(Settings.NameValueTable.VALUE, null);
+                        if (value != null) {
+                            overrider.put(table, arg, value);
+                        } else {
+                            overrider.remove(table, arg);
+                        }
+                        break;
+                    default:
+                        throw new UnsupportedOperationException("Unknown command " + method);
+                }
+                return out;
+            }
+        }
+    }
+
+    public static class AcquireTimeoutException extends Exception {
+        public AcquireTimeoutException(String str, Throwable cause) {
+            super(str, cause);
+        }
+    }
+
+    private static String key(String table, String key) {
+        return table + "_" + key;
+    }
+
+    /**
+     * Since the settings provider is cached inside android.provider.Settings, this must
+     * be gotten statically to ensure there is only one instance referenced.
+     */
+    public static TestableSettings getFakeSettingsProvider(ContentProviderClient settings,
+            ContentResolver resolver) {
+        if (sInstance == null) {
+            sInstance = new TestableSettingsProvider(settings, resolver);
+        }
+        return new TestableSettings(sInstance);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/UiThreadTest.java b/tests/testables/src/android/testing/UiThreadTest.java
similarity index 96%
rename from packages/SystemUI/tests/src/com/android/systemui/UiThreadTest.java
rename to tests/testables/src/android/testing/UiThreadTest.java
index 58369b1..e40e1d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/UiThreadTest.java
+++ b/tests/testables/src/android/testing/UiThreadTest.java
@@ -12,7 +12,7 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui;
+package android.testing;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/ViewUtils.java b/tests/testables/src/android/testing/ViewUtils.java
similarity index 90%
rename from packages/SystemUI/tests/src/com/android/systemui/utils/ViewUtils.java
rename to tests/testables/src/android/testing/ViewUtils.java
index 678b9f4..5a651aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/ViewUtils.java
+++ b/tests/testables/src/android/testing/ViewUtils.java
@@ -12,19 +12,14 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.utils;
+package android.testing;
 
 import android.content.pm.ApplicationInfo;
 import android.graphics.PixelFormat;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Log;
+import android.support.test.InstrumentationRegistry;
 import android.view.View;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
-import android.support.test.InstrumentationRegistry;
-
-import com.android.systemui.SysuiTestCase;
 
 public class ViewUtils {
 
diff --git a/tests/testables/tests/Android.mk b/tests/testables/tests/Android.mk
new file mode 100644
index 0000000..752d536
--- /dev/null
+++ b/tests/testables/tests/Android.mk
@@ -0,0 +1,39 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_USE_AAPT2 := true
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_PACKAGE_NAME := TestablesTest
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    $(call all-Iaidl-files-under, src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    mockito-updated-target-minus-junit4 \
+    legacy-android-test \
+	testables
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
diff --git a/tests/testables/tests/AndroidManifest.xml b/tests/testables/tests/AndroidManifest.xml
new file mode 100644
index 0000000..f6006b0
--- /dev/null
+++ b/tests/testables/tests/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.testables">
+
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.testables"
+        android:label="Tests for Testables">
+    </instrumentation>
+</manifest>
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooperTest.java b/tests/testables/tests/src/android/testing/TestableLooperTest.java
similarity index 93%
rename from packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooperTest.java
rename to tests/testables/tests/src/android/testing/TestableLooperTest.java
index 2416e1d..18e5fff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/TestableLooperTest.java
+++ b/tests/testables/tests/src/android/testing/TestableLooperTest.java
@@ -12,7 +12,7 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.utils;
+package android.testing;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
@@ -27,20 +27,17 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-
-import com.android.systemui.SysUIRunner;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.utils.TestableLooper.MessageHandler;
-import com.android.systemui.utils.TestableLooper.RunWithLooper;
+import android.testing.TestableLooper.MessageHandler;
+import android.testing.TestableLooper.RunWithLooper;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@RunWith(SysUIRunner.class)
+@RunWith(AndroidTestingRunner.class)
 @RunWithLooper
-public class TestableLooperTest extends SysuiTestCase {
+public class TestableLooperTest {
 
     private TestableLooper mTestableLooper;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProviderTest.java b/tests/testables/tests/src/android/testing/TestableSettingsTest.java
similarity index 88%
rename from packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProviderTest.java
rename to tests/testables/tests/src/android/testing/TestableSettingsTest.java
index 63bb5e7..1b01542 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/FakeSettingsProviderTest.java
+++ b/tests/testables/tests/src/android/testing/TestableSettingsTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
  * except in compliance with the License. You may obtain a copy of the License at
@@ -12,7 +12,7 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.utils;
+package android.testing;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -25,29 +25,32 @@
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
+import android.testing.TestableSettings.AcquireTimeoutException;
+import android.testing.TestableSettings.SettingOverrider;
 import android.util.Log;
 
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.utils.FakeSettingsProvider.AcquireTimeoutException;
-import com.android.systemui.utils.FakeSettingsProvider.SettingOverrider;
-
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
-public class FakeSettingsProviderTest extends SysuiTestCase {
+public class TestableSettingsTest {
 
     public static final String NONEXISTENT_SETTING = "nonexistent_setting";
-    private static final String TAG = "FakeSettingsProviderTest";
+    private static final String TAG = "TestableSettingsTest";
     private SettingOverrider mOverrider;
     private ContentResolver mContentResolver;
+    @Rule
+    public final TestableContext mContext =
+            new TestableContext(InstrumentationRegistry.getContext());
 
     @Before
     public void setup() throws AcquireTimeoutException {
-        mOverrider = mContext.getSettingsProvider().acquireOverridesBuilder(this)
+        mOverrider = mContext.getSettingsProvider().acquireOverridesBuilder()
                 .addSetting("secure", NONEXISTENT_SETTING)
                 .addSetting("global", NONEXISTENT_SETTING, "initial value")
                 .addSetting("global", Global.DEVICE_PROVISIONED)
@@ -55,13 +58,6 @@
         mContentResolver = mContext.getContentResolver();
     }
 
-    @After
-    public void teardown() {
-        if (mOverrider != null) {
-            mOverrider.release();
-        }
-    }
-
     @Test
     public void testInitialValueSecure() {
         String value = Secure.getString(mContentResolver, NONEXISTENT_SETTING);
@@ -109,8 +105,9 @@
 
     @Test
     public void testAutoRelease() throws Exception {
-        super.cleanup();
-        mContext.getSettingsProvider().acquireOverridesBuilder(this)
+        mOverrider.release();
+        mOverrider = null;
+        mContext.getSettingsProvider().acquireOverridesBuilder()
                 .addSetting("global", Global.DEVICE_PROVISIONED)
                 .build();
     }
@@ -122,7 +119,7 @@
         String secure = "secure";
         String key = "something shared";
         String[] result = new String[1];
-        overriders[0] = mContext.getSettingsProvider().acquireOverridesBuilder(this)
+        overriders[0] = mContext.getSettingsProvider().acquireOverridesBuilder()
                 .addSetting(secure, key, "Some craziness")
                 .build();
         synchronized (lock) {
@@ -137,7 +134,7 @@
                             lock.notify();
                         }
                         overriders[1] = mContext.getSettingsProvider()
-                                .acquireOverridesBuilder(FakeSettingsProviderTest.this)
+                                .acquireOverridesBuilder()
                                 .addSetting(secure, key, "default value")
                                 .build();
                         // Ensure that the default is the one we set, and not left over from
diff --git a/tools/aapt2/integration-tests/AppOne/Android.mk b/tools/aapt2/integration-tests/AppOne/Android.mk
index a6f32d4..38bd5b5 100644
--- a/tools/aapt2/integration-tests/AppOne/Android.mk
+++ b/tools/aapt2/integration-tests/AppOne/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_PACKAGE_NAME := AaptTestAppOne
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets $(LOCAL_PATH)/assets2
 LOCAL_STATIC_ANDROID_LIBRARIES := \
     AaptTestStaticLibOne \
     AaptTestStaticLibTwo
diff --git a/tools/aapt2/integration-tests/AppOne/assets/subdir/subsubdir/test.txt b/tools/aapt2/integration-tests/AppOne/assets/subdir/subsubdir/test.txt
new file mode 100644
index 0000000..1251949
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/assets/subdir/subsubdir/test.txt
@@ -0,0 +1 @@
+subdir/subsubdir/test.txt comes from assets
diff --git a/tools/aapt2/integration-tests/AppOne/assets/test.txt b/tools/aapt2/integration-tests/AppOne/assets/test.txt
new file mode 100644
index 0000000..88266de
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/assets/test.txt
@@ -0,0 +1 @@
+test.txt came from assets
diff --git a/tools/aapt2/integration-tests/AppOne/assets2/new.txt b/tools/aapt2/integration-tests/AppOne/assets2/new.txt
new file mode 100644
index 0000000..f4963a9
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/assets2/new.txt
@@ -0,0 +1 @@
+new.txt came from assets2
diff --git a/tools/aapt2/integration-tests/AppOne/assets2/test.txt b/tools/aapt2/integration-tests/AppOne/assets2/test.txt
new file mode 100644
index 0000000..5d8b36c
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/assets2/test.txt
@@ -0,0 +1 @@
+test.txt came from assets2
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index c8f0217..1042111 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -77,6 +77,7 @@
   std::string manifest_path;
   std::vector<std::string> include_paths;
   std::vector<std::string> overlay_files;
+  std::vector<std::string> assets_dirs;
   bool output_to_directory = false;
   bool auto_add_overlay = false;
 
@@ -1412,6 +1413,46 @@
     return doc;
   }
 
+  bool CopyAssetsDirsToApk(IArchiveWriter* writer) {
+    std::map<std::string, std::unique_ptr<io::RegularFile>> merged_assets;
+    for (const std::string& assets_dir : options_.assets_dirs) {
+      Maybe<std::vector<std::string>> files =
+          file::FindFiles(assets_dir, context_->GetDiagnostics(), nullptr);
+      if (!files) {
+        return false;
+      }
+
+      for (const std::string& file : files.value()) {
+        std::string full_key = "assets/" + file;
+        std::string full_path = assets_dir;
+        file::AppendPath(&full_path, file);
+
+        auto iter = merged_assets.find(full_key);
+        if (iter == merged_assets.end()) {
+          merged_assets.emplace(std::move(full_key),
+                                util::make_unique<io::RegularFile>(Source(std::move(full_path))));
+        } else if (context_->IsVerbose()) {
+          context_->GetDiagnostics()->Warn(DiagMessage(iter->second->GetSource())
+                                           << "asset file overrides '" << full_path << "'");
+        }
+      }
+    }
+
+    for (auto& entry : merged_assets) {
+      uint32_t compression_flags = ArchiveEntry::kCompress;
+      std::string extension = file::GetExtension(entry.first).to_string();
+      if (options_.extensions_to_not_compress.count(extension) > 0) {
+        compression_flags = 0u;
+      }
+
+      if (!CopyFileToArchive(entry.second.get(), entry.first, compression_flags, writer,
+                             context_)) {
+        return false;
+      }
+    }
+    return true;
+  }
+
   /**
    * Writes the AndroidManifest, ResourceTable, and all XML files referenced by
    * the ResourceTable to the IArchiveWriter.
@@ -1724,11 +1765,9 @@
     }
 
     // Start writing the base APK.
-    std::unique_ptr<IArchiveWriter> archive_writer =
-        MakeArchiveWriter(options_.output_path);
+    std::unique_ptr<IArchiveWriter> archive_writer = MakeArchiveWriter(options_.output_path);
     if (!archive_writer) {
-      context_->GetDiagnostics()->Error(DiagMessage()
-                                        << "failed to create archive");
+      context_->GetDiagnostics()->Error(DiagMessage() << "failed to create archive");
       return 1;
     }
 
@@ -1743,16 +1782,15 @@
       XmlReferenceLinker manifest_linker;
       if (manifest_linker.Consume(context_, manifest_xml.get())) {
         if (options_.generate_proguard_rules_path &&
-            !proguard::CollectProguardRulesForManifest(
-                Source(options_.manifest_path), manifest_xml.get(),
-                &proguard_keep_set)) {
+            !proguard::CollectProguardRulesForManifest(Source(options_.manifest_path),
+                                                       manifest_xml.get(), &proguard_keep_set)) {
           error = true;
         }
 
         if (options_.generate_main_dex_proguard_rules_path &&
-            !proguard::CollectProguardRulesForManifest(
-                Source(options_.manifest_path), manifest_xml.get(),
-                &proguard_main_dex_keep_set, true)) {
+            !proguard::CollectProguardRulesForManifest(Source(options_.manifest_path),
+                                                       manifest_xml.get(),
+                                                       &proguard_main_dex_keep_set, true)) {
           error = true;
         }
 
@@ -1776,13 +1814,15 @@
     }
 
     if (error) {
-      context_->GetDiagnostics()->Error(DiagMessage()
-                                        << "failed processing manifest");
+      context_->GetDiagnostics()->Error(DiagMessage() << "failed processing manifest");
       return 1;
     }
 
-    if (!WriteApk(archive_writer.get(), &proguard_keep_set, manifest_xml.get(),
-                  &final_table_)) {
+    if (!WriteApk(archive_writer.get(), &proguard_keep_set, manifest_xml.get(), &final_table_)) {
+      return 1;
+    }
+
+    if (!CopyAssetsDirsToApk(archive_writer.get())) {
       return 1;
     }
 
@@ -1863,12 +1903,6 @@
                            proguard_main_dex_keep_set)) {
       return 1;
     }
-
-    if (context_->IsVerbose()) {
-      DebugPrintTableOptions debug_print_table_options;
-      debug_print_table_options.show_sources = true;
-      Debug::PrintTable(&final_table_, debug_print_table_options);
-    }
     return 0;
   }
 
@@ -1916,6 +1950,9 @@
           .RequiredFlag("--manifest", "Path to the Android manifest to build",
                         &options.manifest_path)
           .OptionalFlagList("-I", "Adds an Android APK to link against", &options.include_paths)
+          .OptionalFlagList("-A",
+                            "An assets directory to include in the APK. These are unprocessed.",
+                            &options.assets_dirs)
           .OptionalFlagList("-R",
                             "Compilation unit to link, using `overlay` semantics.\n"
                             "The last conflicting resource given takes precedence.",
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index aa840e2..d10351b 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -264,5 +264,57 @@
   return true;
 }
 
+Maybe<std::vector<std::string>> FindFiles(const android::StringPiece& path, IDiagnostics* diag,
+                                          const FileFilter* filter) {
+  const std::string root_dir = path.to_string();
+  std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()), closedir);
+  if (!d) {
+    diag->Error(DiagMessage() << android::base::SystemErrorCodeToString(errno));
+    return {};
+  }
+
+  std::vector<std::string> files;
+  std::vector<std::string> subdirs;
+  while (struct dirent* entry = readdir(d.get())) {
+    if (util::StartsWith(entry->d_name, ".")) {
+      continue;
+    }
+
+    std::string file_name = entry->d_name;
+    std::string full_path = root_dir;
+    AppendPath(&full_path, file_name);
+    const FileType file_type = GetFileType(full_path);
+
+    if (filter != nullptr) {
+      if (!(*filter)(file_name, file_type)) {
+        continue;
+      }
+    }
+
+    if (file_type == file::FileType::kDirectory) {
+      subdirs.push_back(std::move(file_name));
+    } else {
+      files.push_back(std::move(file_name));
+    }
+  }
+
+  // Now process subdirs.
+  for (const std::string& subdir : subdirs) {
+    std::string full_subdir = root_dir;
+    AppendPath(&full_subdir, subdir);
+    Maybe<std::vector<std::string>> subfiles = FindFiles(full_subdir, diag, filter);
+    if (!subfiles) {
+      return {};
+    }
+
+    for (const std::string& subfile : subfiles.value()) {
+      std::string new_file = subdir;
+      AppendPath(&new_file, subfile);
+      files.push_back(new_file);
+    }
+  }
+  return files;
+}
+
 }  // namespace file
 }  // namespace aapt
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index 95c492f..b3b1e48 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -132,6 +132,11 @@
   std::vector<std::string> pattern_tokens_;
 };
 
+// Returns a list of files relative to the directory identified by `path`.
+// An optional FileFilter filters out any files that don't pass.
+Maybe<std::vector<std::string>> FindFiles(const android::StringPiece& path, IDiagnostics* diag,
+                                          const FileFilter* filter = nullptr);
+
 }  // namespace file
 }  // namespace aapt
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
index ff5a5e9..aaff5d5 100644
--- a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -16,7 +16,6 @@
 
 package android.graphics;
 
-import android.text.FontConfig;
 import com.android.ide.common.rendering.api.AssetRepository;
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
@@ -293,12 +292,16 @@
 
     @LayoutlibDelegate
     /*package*/ static boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font,
-            int ttcIndex, List<FontConfig.Axis> listOfAxis,
-            int weight, boolean isItalic) {
+            int ttcIndex, int weight, boolean isItalic) {
         assert false : "The only client of this method has been overriden.";
         return false;
     }
 
+    @LayoutlibDelegate
+    /*package*/ static void nAddAxisValue(long builderPtr, int tag, float value) {
+        assert false : "The only client of this method has been overriden.";
+    }
+
     static boolean addFont(long builderPtr, final String path, final int weight,
             final boolean isItalic) {
         final FontFamily_Delegate delegate = getDelegate(builderPtr);
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index aa1f00d..1bb56e3 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -964,8 +964,9 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, char[] text,
-            int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
+    /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, long typefacePtr,
+            char[] text, int contextStart, int contextLength, int flags, int offset,
+            int cursorOpt) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                 "Paint.getTextRunCursor is not supported.", null, null /*data*/);
@@ -973,8 +974,8 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, String text,
-            int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
+    /*package*/ static int nGetTextRunCursor(Paint paint, long native_object, long typefacePtr,
+            String text, int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
         // FIXME
         Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
                 "Paint.getTextRunCursor is not supported.", null, null /*data*/);
diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/AdaptiveIconDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/AdaptiveIconDrawable_Delegate.java
new file mode 100644
index 0000000..7e9432d
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/drawable/AdaptiveIconDrawable_Delegate.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable.LayerState;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link
+ * AdaptiveIconDrawable}
+ * <p>
+ * Through the layoutlib_create tool, the original  methods of AdaptiveIconDrawable have been
+ * replaced by calls to methods of the same name in this delegate class.
+ */
+@SuppressWarnings("unused")
+public class AdaptiveIconDrawable_Delegate {
+    @LayoutlibDelegate
+    /*package*/ static void draw(AdaptiveIconDrawable thisDrawable, Canvas canvas) {
+        // This is a workaround for the broken BitmapShader in layoutlib. This new draw methods
+        // avoids the use of the shader.
+
+        for (int i = 0; i < LayerState.N_CHILDREN; i++) {
+            if (thisDrawable.mLayerState.mChildren[i] == null) {
+                continue;
+            }
+            final Drawable dr = thisDrawable.mLayerState.mChildren[i].mDrawable;
+            if (dr != null) {
+                dr.draw(canvas);
+            }
+        }
+
+        if (thisDrawable.mMaskBitmap != null) {
+            Rect bounds = thisDrawable.getBounds();
+            Paint paint = new Paint();
+            paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
+            canvas.drawBitmap(thisDrawable.mMaskBitmap, bounds.left, bounds.top, paint);
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index fb90ce0..1282349 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -399,7 +399,7 @@
     }
 
     @Override
-    public int watchRotation(IRotationWatcher arg0) throws RemoteException {
+    public int watchRotation(IRotationWatcher arg0, int arg1) throws RemoteException {
         // TODO Auto-generated method stub
         return 0;
     }
diff --git a/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java b/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java
index 3ce7cab..672ff6d 100644
--- a/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java
+++ b/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java
@@ -95,6 +95,9 @@
 
                 public void notifyServicesStateChanged() {
                 }
+
+                public void setRelevantEventTypes(int eventTypes) {
+                }
             };
 
     /**
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index 2274b90..906ebb1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -657,6 +657,10 @@
     }
 
     @Override
+    public void setUpdateAvailable(String packageName, boolean updateAvailable) {
+    }
+
+    @Override
     public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {
     }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index 7582fda..4dfe47b 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -96,18 +96,6 @@
     }
 
     @Override
-    public void repositionChild(IWindow window, int left, int top, int right, int bottom,
-            long deferTransactionUntilFrame, Rect outFrame) {
-        // pass for now.
-        return;
-    }
-
-    @Override
-    public void performDeferredDestroy(IWindow window) {
-        // pass for now.
-    }
-
-    @Override
     public boolean outOfMemory(IWindow window) throws RemoteException {
         return false;
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index f1e7b51..4533774 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -415,9 +415,9 @@
                 BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
                         parser, context, isFramework);
                 try {
-                    FontConfig config = FontResourcesParser.parse(blockParser, context
-                            .getResources());
-                    typeface = Typeface.createFromResources(config, context.getAssets(),
+                    FontResourcesParser.FamilyResourceEntry entry =
+                            FontResourcesParser.parse(blockParser, context.getResources());
+                    typeface = Typeface.createFromResources(entry, context.getAssets(),
                             fontName);
                 } catch (XmlPullParserException | IOException e) {
                     Bridge.getLog().error(null, "Failed to parse file " + fontName,
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon.png
new file mode 100644
index 0000000..7014ddb
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/adaptive.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/adaptive.xml
new file mode 100644
index 0000000..8f862c8
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/adaptive.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@android:color/red" />
+    <foreground android:drawable="@drawable/headset" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/adaptive_icon.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/adaptive_icon.xml
new file mode 100644
index 0000000..ca9fa55
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/adaptive_icon.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:padding="16dp"
+              android:orientation="horizontal"
+              android:layout_width="fill_parent"
+              android:layout_height="fill_parent">
+    <ImageView
+             android:layout_height="wrap_content"
+             android:layout_width="wrap_content"
+             android:src="@drawable/adaptive" />
+
+</LinearLayout>
+
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
index 7199781..2b5e0f9 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
@@ -396,6 +396,22 @@
     }
 
     @Test
+    public void testAdaptiveIcon() throws ClassNotFoundException {
+        // Create the layout pull parser.
+        LayoutPullParser parser = createLayoutPullParser("adaptive_icon.xml");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+
+        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5,
+                layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false,
+                RenderingMode.V_SCROLL, 22);
+
+        renderAndVerify(params, "adaptive_icon.png");
+    }
+
+    @Test
     public void testColorTypedValue() throws Exception {
         // Setup
         // Create the layout pull parser for our resources (empty.xml can not be part of the test
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index b0aa3c2..4f226cb 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -163,6 +163,7 @@
         "android.content.res.TypedArray#obtain",
         "android.graphics.BitmapFactory#finishDecode",
         "android.graphics.BitmapFactory#setDensityFromOptions",
+        "android.graphics.drawable.AdaptiveIconDrawable#draw",
         "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#useLastSeenTarget",
         "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#onDraw",
         "android.graphics.drawable.GradientDrawable#buildRing",
@@ -332,6 +333,8 @@
      * needed when access from the delegate classes is needed.
      */
     private final static String[] PROMOTED_FIELDS = new String[] {
+        "android.graphics.drawable.AdaptiveIconDrawable#mMaskBitmap",
+        "android.graphics.drawable.AdaptiveIconDrawable#mPaint",
         "android.graphics.drawable.VectorDrawable#mVectorState",
         "android.view.Choreographer#mLastFrameTimeNanos",
         "android.graphics.FontFamily#mBuilderPtr"
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 18c1245..af48d0a 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -16,7 +16,11 @@
 
 package android.net.wifi;
 
+
+import android.content.pm.ParceledListSlice;
+
 import android.net.wifi.hotspot2.PasspointConfiguration;
+
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.ScanSettings;
@@ -51,9 +55,9 @@
      */
     oneway void requestActivityInfo(in ResultReceiver result);
 
-    List<WifiConfiguration> getConfiguredNetworks();
+    ParceledListSlice getConfiguredNetworks();
 
-    List<WifiConfiguration> getPrivilegedConfiguredNetworks();
+    ParceledListSlice getPrivilegedConfiguredNetworks();
 
     WifiConfiguration getMatchingWifiConfig(in ScanResult scanResult);
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index bbe96a7..4f2881b 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -21,6 +21,7 @@
 import android.annotation.SystemApi;
 import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
+import android.content.pm.ParceledListSlice;
 import android.net.ConnectivityManager;
 import android.net.DhcpInfo;
 import android.net.Network;
@@ -46,6 +47,7 @@
 import java.net.InetAddress;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
+import java.util.Collections;
 
 /**
  * This class provides the primary API for managing all aspects of Wi-Fi
@@ -811,7 +813,12 @@
      */
     public List<WifiConfiguration> getConfiguredNetworks() {
         try {
-            return mService.getConfiguredNetworks();
+            ParceledListSlice<WifiConfiguration> parceledList =
+                mService.getConfiguredNetworks();
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -821,7 +828,12 @@
     @SystemApi
     public List<WifiConfiguration> getPrivilegedConfiguredNetworks() {
         try {
-            return mService.getPrivilegedConfiguredNetworks();
+            ParceledListSlice<WifiConfiguration> parceledList =
+                mService.getPrivilegedConfiguredNetworks();
+            if (parceledList == null) {
+                return Collections.emptyList();
+            }
+            return parceledList.getList();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -971,10 +983,13 @@
     }
 
     /**
-     * Query for a Hotspot 2.0 release 2 OSU icon file.
+     * Query for a Hotspot 2.0 release 2 OSU icon file. An {@link #ACTION_PASSPOINT_ICON} intent
+     * will be broadcasted once the request is completed.  The return value of
+     * {@link IconInfo#getData} from the intent extra will indicate the result of the request.
+     * A value of {@code null} will indicate a failure.
      *
      * @param bssid The BSSID of the AP
-     * @param fileName File name of the icon to query
+     * @param fileName Name of the icon file (remote file) to query from the AP
      */
     public void queryPasspointIcon(long bssid, String fileName) {
         try {
diff --git a/wifi/tests/Android.mk b/wifi/tests/Android.mk
index eac49d2..8dc244f 100644
--- a/wifi/tests/Android.mk
+++ b/wifi/tests/Android.mk
@@ -58,5 +58,6 @@
 	android.test.runner \
 
 LOCAL_PACKAGE_NAME := FrameworksWifiApiTests
+LOCAL_COMPATIBILITY_SUITE := device-tests
 
 include $(BUILD_PACKAGE)