Merge changes Ic359aaee,Id7aba066

* changes:
  AOD: Use color filter to invert icons for AOD
  NotificationWrappers: Factor out doze treatment
diff --git a/Android.mk b/Android.mk
index 0e5dfed..03b2533 100644
--- a/Android.mk
+++ b/Android.mk
@@ -944,27 +944,8 @@
 		-werror -hide 111 -hide 113 \
 		-overview $(LOCAL_PATH)/core/java/overview.html
 
-SUPPORT_API_DIR := ./frameworks/support/api
-
-# More API Level information for the Support Library, which is currently
-# included as part of the core framework docs build.
-framework_docs_LOCAL_DROIDDOC_OPTIONS += \
-    -since $(SUPPORT_API_DIR)/22.0.0.txt 22.0.0 \
-    -since $(SUPPORT_API_DIR)/22.0.0.txt 22.0.0 \
-    -since $(SUPPORT_API_DIR)/22.1.0.txt 22.1.0 \
-    -since $(SUPPORT_API_DIR)/22.2.0.txt 22.2.0 \
-    -since $(SUPPORT_API_DIR)/22.2.1.txt 22.2.1 \
-    -since $(SUPPORT_API_DIR)/23.0.0.txt 23.0.0 \
-    -since $(SUPPORT_API_DIR)/23.1.0.txt 23.1.0 \
-    -since $(SUPPORT_API_DIR)/23.1.1.txt 23.1.1 \
-    -since $(SUPPORT_API_DIR)/23.2.0.txt 23.2.0 \
-    -since $(SUPPORT_API_DIR)/23.2.1.txt 23.2.1 \
-    -since $(SUPPORT_API_DIR)/23.4.0.txt 23.4.0 \
-    -since $(SUPPORT_API_DIR)/24.0.0.txt 24.0.0 \
-    -since $(SUPPORT_API_DIR)/24.1.0.txt 24.1.0 \
-    -since $(SUPPORT_API_DIR)/24.2.0.txt 24.2.0 \
-    -since $(SUPPORT_API_DIR)/25.0.0.txt 25.0.0 \
-    -since $(SUPPORT_API_DIR)/25.1.0.txt 25.1.0
+# Allow the support library to add its own droiddoc options.
+include $(LOCAL_PATH)/../support/droiddoc.mk
 
 framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \
 	$(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)
@@ -1472,10 +1453,6 @@
 LOCAL_SRC_FILES := \
     $(call all-proto-files-under, core/proto) \
     $(call all-proto-files-under, libs/incident/proto)
-LOCAL_C_INCLUDES := \
-    $(call generated-sources-dir-for,STATIC_LIBRARIES,libplatformprotos,)/proto
-LOCAL_EXPORT_C_INCLUDES := \
-    $(call generated-sources-dir-for,STATIC_LIBRARIES,libplatformprotos,)/proto
 include $(BUILD_STATIC_LIBRARY)
 
 # ====  c++ proto host library  ==============================
@@ -1490,10 +1467,6 @@
 LOCAL_SRC_FILES := \
     $(call all-proto-files-under, core/proto) \
     $(call all-proto-files-under, libs/incident/proto)
-LOCAL_C_INCLUDES := \
-    $(call generated-sources-dir-for,STATIC_LIBRARIES,libplatformprotos,)/proto
-LOCAL_EXPORT_C_INCLUDES := \
-    $(call generated-sources-dir-for,STATIC_LIBRARIES,libplatformprotos,)/proto
 include $(BUILD_HOST_SHARED_LIBRARY)
 
 
diff --git a/api/current.txt b/api/current.txt
index 4981c43..aab29be 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -746,11 +746,13 @@
     field public static final int isAsciiCapable = 16843753; // 0x10103e9
     field public static final int isAuxiliary = 16843647; // 0x101037f
     field public static final int isDefault = 16843297; // 0x1010221
+    field public static final int isFeatureSplit = 16844126; // 0x101055e
     field public static final int isGame = 16843764; // 0x10103f4
     field public static final int isIndicator = 16843079; // 0x1010147
     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
@@ -1789,6 +1791,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
@@ -5413,6 +5416,7 @@
     ctor public Notification.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
     method public java.lang.String getDataMimeType();
     method public android.net.Uri getDataUri();
+    method public android.os.Bundle getExtras();
     method public java.lang.CharSequence getSender();
     method public java.lang.CharSequence getText();
     method public long getTimestamp();
@@ -5492,7 +5496,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();
@@ -5506,7 +5509,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);
@@ -5514,6 +5516,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[]);
@@ -5526,14 +5529,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;
   }
@@ -5549,12 +5550,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();
@@ -5716,6 +5719,8 @@
     method public java.lang.CharSequence getContentDescription();
     method public android.graphics.drawable.Icon getIcon();
     method public java.lang.CharSequence getTitle();
+    method public boolean isEnabled();
+    method public void setEnabled(boolean);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.RemoteAction> CREATOR;
   }
@@ -6566,7 +6571,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();
@@ -7121,6 +7126,7 @@
     method public android.bluetooth.le.BluetoothLeScanner getBluetoothLeScanner();
     method public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices();
     method public static synchronized android.bluetooth.BluetoothAdapter getDefaultAdapter();
+    method public int getLeMaximumAdvertisingDataLength();
     method public java.lang.String getName();
     method public android.bluetooth.le.PeriodicAdvertisingManager getPeriodicAdvertisingManager();
     method public int getProfileConnectionState(int);
@@ -7932,7 +7938,7 @@
   }
 
   public final class AdvertisingSet {
-    method public void enableAdvertising(boolean);
+    method public void enableAdvertising(boolean, int);
     method public void periodicAdvertisingEnable(boolean);
     method public void setAdvertisingData(android.bluetooth.le.AdvertiseData);
     method public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters);
@@ -7965,7 +7971,6 @@
     method public int getInterval();
     method public int getPrimaryPhy();
     method public int getSecondaryPhy();
-    method public int getTimeout();
     method public int getTxPowerLevel();
     method public boolean includeTxPower();
     method public boolean isAnonymous();
@@ -7999,7 +8004,6 @@
     method public android.bluetooth.le.AdvertisingSetParameters.Builder setLegacyMode(boolean);
     method public android.bluetooth.le.AdvertisingSetParameters.Builder setPrimaryPhy(int);
     method public android.bluetooth.le.AdvertisingSetParameters.Builder setSecondaryPhy(int);
-    method public android.bluetooth.le.AdvertisingSetParameters.Builder setTimeout(int);
     method public android.bluetooth.le.AdvertisingSetParameters.Builder setTxPowerLevel(int);
   }
 
@@ -8008,6 +8012,8 @@
     method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
     method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback);
     method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
+    method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, int, android.bluetooth.le.AdvertisingSetCallback);
+    method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, int, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
     method public void stopAdvertising(android.bluetooth.le.AdvertiseCallback);
     method public void stopAdvertisingSet(android.bluetooth.le.AdvertisingSetCallback);
   }
@@ -10671,6 +10677,7 @@
     field public static final java.lang.String FEATURE_USB_ACCESSORY = "android.hardware.usb.accessory";
     field public static final java.lang.String FEATURE_USB_HOST = "android.hardware.usb.host";
     field public static final java.lang.String FEATURE_VERIFIED_BOOT = "android.software.verified_boot";
+    field public static final java.lang.String FEATURE_VR_HEADTRACKING = "android.hardware.vr.headtracking";
     field public static final java.lang.String FEATURE_VR_MODE = "android.software.vr.mode";
     field public static final java.lang.String FEATURE_VR_MODE_HIGH_PERFORMANCE = "android.hardware.vr.high_performance";
     field public static final java.lang.String FEATURE_VULKAN_HARDWARE_COMPUTE = "android.hardware.vulkan.compute";
@@ -12756,7 +12763,7 @@
   }
 
   public class ColorFilter {
-    ctor public ColorFilter();
+    ctor public deprecated ColorFilter();
   }
 
   public class ColorMatrix {
@@ -12780,6 +12787,9 @@
   public class ColorMatrixColorFilter extends android.graphics.ColorFilter {
     ctor public ColorMatrixColorFilter(android.graphics.ColorMatrix);
     ctor public ColorMatrixColorFilter(float[]);
+    method public void getColorMatrix(android.graphics.ColorMatrix);
+    method public void setColorMatrix(android.graphics.ColorMatrix);
+    method public void setColorMatrixArray(float[]);
   }
 
   public abstract class ColorSpace {
@@ -13003,6 +13013,10 @@
 
   public class LightingColorFilter extends android.graphics.ColorFilter {
     ctor public LightingColorFilter(int, int);
+    method public int getColorAdd();
+    method public int getColorMultiply();
+    method public void setColorAdd(int);
+    method public void setColorMultiply(int);
   }
 
   public class LinearGradient extends android.graphics.Shader {
@@ -13519,6 +13533,10 @@
 
   public class PorterDuffColorFilter extends android.graphics.ColorFilter {
     ctor public PorterDuffColorFilter(int, android.graphics.PorterDuff.Mode);
+    method public int getColor();
+    method public android.graphics.PorterDuff.Mode getMode();
+    method public void setColor(int);
+    method public void setMode(android.graphics.PorterDuff.Mode);
   }
 
   public class PorterDuffXfermode extends android.graphics.Xfermode {
@@ -21826,7 +21844,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 +22396,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 +22651,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 +22754,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 +22922,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 +24733,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 +25274,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);
@@ -33877,6 +33958,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";
@@ -36741,7 +36823,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";
@@ -36796,11 +36878,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[]);
   }
 
 }
@@ -37092,11 +37174,11 @@
     field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0
     field public static final int REASON_APP_CANCEL = 8; // 0x8
     field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9
+    field public static final int REASON_CANCEL = 2; // 0x2
+    field public static final int REASON_CANCEL_ALL = 3; // 0x3
     field public static final int REASON_CHANNEL_BANNED = 17; // 0x11
-    field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2
-    field public static final int REASON_DELEGATE_CANCEL_ALL = 3; // 0x3
-    field public static final int REASON_DELEGATE_CLICK = 1; // 0x1
-    field public static final int REASON_DELEGATE_ERROR = 4; // 0x4
+    field public static final int REASON_CLICK = 1; // 0x1
+    field public static final int REASON_ERROR = 4; // 0x4
     field public static final int REASON_GROUP_OPTIMIZATION = 13; // 0xd
     field public static final int REASON_GROUP_SUMMARY_CANCELED = 12; // 0xc
     field public static final int REASON_LISTENER_CANCEL = 10; // 0xa
@@ -39178,6 +39260,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";
@@ -39220,6 +39303,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";
@@ -39275,6 +39360,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";
@@ -39807,7 +39893,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();
@@ -39848,6 +39933,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();
@@ -40885,11 +40971,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;
   }
@@ -40914,22 +40999,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();
@@ -45007,8 +45092,8 @@
     method public void addTouchables(java.util.ArrayList<android.view.View>);
     method public android.view.ViewPropertyAnimator animate();
     method public void announceForAccessibility(java.lang.CharSequence);
-    method public void autofill(android.view.autofill.AutofillValue);
-    method public void autofillVirtual(int, android.view.autofill.AutofillValue);
+    method public boolean autofill(android.view.autofill.AutofillValue);
+    method public boolean autofill(int, android.view.autofill.AutofillValue);
     method protected boolean awakenScrollBars();
     method protected boolean awakenScrollBars(int);
     method protected boolean awakenScrollBars(int, boolean);
@@ -45098,7 +45183,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();
@@ -45419,7 +45504,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);
@@ -45562,20 +45647,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
@@ -46224,7 +46308,7 @@
     method public abstract int addChildCount(int);
     method public abstract void asyncCommit();
     method public abstract android.view.ViewStructure asyncNewChild(int);
-    method public abstract android.view.ViewStructure asyncNewChildForAutofill(int, int, int);
+    method public abstract android.view.ViewStructure asyncNewChild(int, int, int);
     method public abstract int getChildCount();
     method public abstract android.os.Bundle getExtras();
     method public abstract java.lang.CharSequence getHint();
@@ -46233,11 +46317,11 @@
     method public abstract int getTextSelectionStart();
     method public abstract boolean hasExtras();
     method public abstract android.view.ViewStructure newChild(int);
-    method public abstract android.view.ViewStructure newChildForAutofill(int, int, int);
+    method public abstract android.view.ViewStructure newChild(int, int, int);
     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);
@@ -46248,6 +46332,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);
@@ -46258,7 +46343,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);
@@ -47503,24 +47587,29 @@
   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 notifyValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
     method public void notifyViewEntered(android.view.View);
+    method public void notifyViewEntered(android.view.View, int, android.graphics.Rect);
     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 notifyViewExited(android.view.View, int);
     method public void registerCallback(android.view.autofill.AutofillManager.AutofillCallback);
+    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);
     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 {
     ctor public AutofillManager.AutofillCallback();
     method public void onAutofillEvent(android.view.View, int);
-    method public void onAutofillEventVirtual(android.view.View, int, int);
+    method public void onAutofillEvent(android.view.View, int, int);
     field public static final int EVENT_INPUT_HIDDEN = 2; // 0x2
     field public static final int EVENT_INPUT_SHOWN = 1; // 0x1
+    field public static final int EVENT_INPUT_UNAVAILABLE = 3; // 0x3
   }
 
   public final class AutofillValue implements android.os.Parcelable {
@@ -47533,6 +47622,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 fbfee83..0f42a54 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -859,11 +859,13 @@
     field public static final int isAsciiCapable = 16843753; // 0x10103e9
     field public static final int isAuxiliary = 16843647; // 0x101037f
     field public static final int isDefault = 16843297; // 0x1010221
+    field public static final int isFeatureSplit = 16844126; // 0x101055e
     field public static final int isGame = 16843764; // 0x10103f4
     field public static final int isIndicator = 16843079; // 0x1010147
     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
@@ -1906,6 +1908,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
@@ -5587,6 +5590,7 @@
     ctor public Notification.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
     method public java.lang.String getDataMimeType();
     method public android.net.Uri getDataUri();
+    method public android.os.Bundle getExtras();
     method public java.lang.CharSequence getSender();
     method public java.lang.CharSequence getText();
     method public long getTimestamp();
@@ -5679,7 +5683,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();
@@ -5693,19 +5696,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[]);
@@ -5730,18 +5731,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;
   }
 
@@ -5756,12 +5753,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();
@@ -5923,6 +5922,8 @@
     method public java.lang.CharSequence getContentDescription();
     method public android.graphics.drawable.Icon getIcon();
     method public java.lang.CharSequence getTitle();
+    method public boolean isEnabled();
+    method public void setEnabled(boolean);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.RemoteAction> CREATOR;
   }
@@ -6816,7 +6817,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();
@@ -7594,6 +7595,7 @@
     method public android.bluetooth.le.BluetoothLeScanner getBluetoothLeScanner();
     method public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices();
     method public static synchronized android.bluetooth.BluetoothAdapter getDefaultAdapter();
+    method public int getLeMaximumAdvertisingDataLength();
     method public java.lang.String getName();
     method public android.bluetooth.le.PeriodicAdvertisingManager getPeriodicAdvertisingManager();
     method public int getProfileConnectionState(int);
@@ -8411,7 +8413,7 @@
   }
 
   public final class AdvertisingSet {
-    method public void enableAdvertising(boolean);
+    method public void enableAdvertising(boolean, int);
     method public void periodicAdvertisingEnable(boolean);
     method public void setAdvertisingData(android.bluetooth.le.AdvertiseData);
     method public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters);
@@ -8444,7 +8446,6 @@
     method public int getInterval();
     method public int getPrimaryPhy();
     method public int getSecondaryPhy();
-    method public int getTimeout();
     method public int getTxPowerLevel();
     method public boolean includeTxPower();
     method public boolean isAnonymous();
@@ -8478,7 +8479,6 @@
     method public android.bluetooth.le.AdvertisingSetParameters.Builder setLegacyMode(boolean);
     method public android.bluetooth.le.AdvertisingSetParameters.Builder setPrimaryPhy(int);
     method public android.bluetooth.le.AdvertisingSetParameters.Builder setSecondaryPhy(int);
-    method public android.bluetooth.le.AdvertisingSetParameters.Builder setTimeout(int);
     method public android.bluetooth.le.AdvertisingSetParameters.Builder setTxPowerLevel(int);
   }
 
@@ -8487,6 +8487,8 @@
     method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
     method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback);
     method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
+    method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, int, android.bluetooth.le.AdvertisingSetCallback);
+    method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, int, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
     method public void stopAdvertising(android.bluetooth.le.AdvertiseCallback);
     method public void stopAdvertisingSet(android.bluetooth.le.AdvertisingSetCallback);
   }
@@ -11241,6 +11243,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>);
@@ -11332,6 +11335,7 @@
     field public static final java.lang.String FEATURE_USB_ACCESSORY = "android.hardware.usb.accessory";
     field public static final java.lang.String FEATURE_USB_HOST = "android.hardware.usb.host";
     field public static final java.lang.String FEATURE_VERIFIED_BOOT = "android.software.verified_boot";
+    field public static final java.lang.String FEATURE_VR_HEADTRACKING = "android.hardware.vr.headtracking";
     field public static final java.lang.String FEATURE_VR_MODE = "android.software.vr.mode";
     field public static final java.lang.String FEATURE_VR_MODE_HIGH_PERFORMANCE = "android.hardware.vr.high_performance";
     field public static final java.lang.String FEATURE_VULKAN_HARDWARE_COMPUTE = "android.hardware.vulkan.compute";
@@ -13493,7 +13497,7 @@
   }
 
   public class ColorFilter {
-    ctor public ColorFilter();
+    ctor public deprecated ColorFilter();
   }
 
   public class ColorMatrix {
@@ -13517,6 +13521,9 @@
   public class ColorMatrixColorFilter extends android.graphics.ColorFilter {
     ctor public ColorMatrixColorFilter(android.graphics.ColorMatrix);
     ctor public ColorMatrixColorFilter(float[]);
+    method public void getColorMatrix(android.graphics.ColorMatrix);
+    method public void setColorMatrix(android.graphics.ColorMatrix);
+    method public void setColorMatrixArray(float[]);
   }
 
   public abstract class ColorSpace {
@@ -13740,6 +13747,10 @@
 
   public class LightingColorFilter extends android.graphics.ColorFilter {
     ctor public LightingColorFilter(int, int);
+    method public int getColorAdd();
+    method public int getColorMultiply();
+    method public void setColorAdd(int);
+    method public void setColorMultiply(int);
   }
 
   public class LinearGradient extends android.graphics.Shader {
@@ -14256,6 +14267,10 @@
 
   public class PorterDuffColorFilter extends android.graphics.ColorFilter {
     ctor public PorterDuffColorFilter(int, android.graphics.PorterDuff.Mode);
+    method public int getColor();
+    method public android.graphics.PorterDuff.Mode getMode();
+    method public void setColor(int);
+    method public void setMode(android.graphics.PorterDuff.Mode);
   }
 
   public class PorterDuffXfermode extends android.graphics.Xfermode {
@@ -23621,7 +23636,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 +24188,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 +24443,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 +24546,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 +24714,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 +26694,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 +26721,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 +26729,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 +27395,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);
@@ -34351,6 +34434,7 @@
     method public deprecated void setUserRestrictions(android.os.Bundle);
     method public deprecated void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
     method public static boolean supportsMultipleUsers();
+    field public static final java.lang.String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
     field public static final java.lang.String ALLOW_PARENT_PROFILE_APP_LINKING = "allow_parent_profile_app_linking";
     field public static final java.lang.String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
     field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
@@ -36751,6 +36835,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";
@@ -39773,7 +39858,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";
@@ -39828,11 +39913,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[]);
   }
 
 }
@@ -40161,11 +40246,11 @@
     field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0
     field public static final int REASON_APP_CANCEL = 8; // 0x8
     field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9
+    field public static final int REASON_CANCEL = 2; // 0x2
+    field public static final int REASON_CANCEL_ALL = 3; // 0x3
     field public static final int REASON_CHANNEL_BANNED = 17; // 0x11
-    field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2
-    field public static final int REASON_DELEGATE_CANCEL_ALL = 3; // 0x3
-    field public static final int REASON_DELEGATE_CLICK = 1; // 0x1
-    field public static final int REASON_DELEGATE_ERROR = 4; // 0x4
+    field public static final int REASON_CLICK = 1; // 0x1
+    field public static final int REASON_ERROR = 4; // 0x4
     field public static final int REASON_GROUP_OPTIMIZATION = 13; // 0xd
     field public static final int REASON_GROUP_SUMMARY_CANCELED = 12; // 0xc
     field public static final int REASON_LISTENER_CANCEL = 10; // 0xa
@@ -42536,6 +42621,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";
@@ -42578,6 +42664,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";
@@ -42633,6 +42721,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";
@@ -43205,8 +43294,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();
@@ -43253,6 +43342,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();
@@ -44138,6 +44228,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>);
@@ -44343,11 +44434,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;
   }
@@ -44372,22 +44462,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();
@@ -48466,8 +48556,8 @@
     method public void addTouchables(java.util.ArrayList<android.view.View>);
     method public android.view.ViewPropertyAnimator animate();
     method public void announceForAccessibility(java.lang.CharSequence);
-    method public void autofill(android.view.autofill.AutofillValue);
-    method public void autofillVirtual(int, android.view.autofill.AutofillValue);
+    method public boolean autofill(android.view.autofill.AutofillValue);
+    method public boolean autofill(int, android.view.autofill.AutofillValue);
     method protected boolean awakenScrollBars();
     method protected boolean awakenScrollBars(int);
     method protected boolean awakenScrollBars(int, boolean);
@@ -48557,7 +48647,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();
@@ -48878,7 +48968,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);
@@ -49021,20 +49111,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
@@ -49683,7 +49772,7 @@
     method public abstract int addChildCount(int);
     method public abstract void asyncCommit();
     method public abstract android.view.ViewStructure asyncNewChild(int);
-    method public abstract android.view.ViewStructure asyncNewChildForAutofill(int, int, int);
+    method public abstract android.view.ViewStructure asyncNewChild(int, int, int);
     method public abstract int getChildCount();
     method public abstract android.os.Bundle getExtras();
     method public abstract java.lang.CharSequence getHint();
@@ -49692,11 +49781,11 @@
     method public abstract int getTextSelectionStart();
     method public abstract boolean hasExtras();
     method public abstract android.view.ViewStructure newChild(int);
-    method public abstract android.view.ViewStructure newChildForAutofill(int, int, int);
+    method public abstract android.view.ViewStructure newChild(int, int, int);
     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);
@@ -49707,6 +49796,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);
@@ -49717,7 +49807,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);
@@ -50965,24 +51054,29 @@
   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 notifyValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
     method public void notifyViewEntered(android.view.View);
+    method public void notifyViewEntered(android.view.View, int, android.graphics.Rect);
     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 notifyViewExited(android.view.View, int);
     method public void registerCallback(android.view.autofill.AutofillManager.AutofillCallback);
+    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);
     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 {
     ctor public AutofillManager.AutofillCallback();
     method public void onAutofillEvent(android.view.View, int);
-    method public void onAutofillEventVirtual(android.view.View, int, int);
+    method public void onAutofillEvent(android.view.View, int, int);
     field public static final int EVENT_INPUT_HIDDEN = 2; // 0x2
     field public static final int EVENT_INPUT_SHOWN = 1; // 0x1
+    field public static final int EVENT_INPUT_UNAVAILABLE = 3; // 0x3
   }
 
   public final class AutofillValue implements android.os.Parcelable {
@@ -50995,6 +51089,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 3be7f67..f91bbb9 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -746,11 +746,13 @@
     field public static final int isAsciiCapable = 16843753; // 0x10103e9
     field public static final int isAuxiliary = 16843647; // 0x101037f
     field public static final int isDefault = 16843297; // 0x1010221
+    field public static final int isFeatureSplit = 16844126; // 0x101055e
     field public static final int isGame = 16843764; // 0x10103f4
     field public static final int isIndicator = 16843079; // 0x1010147
     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
@@ -1789,6 +1791,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
@@ -5423,6 +5426,7 @@
     ctor public Notification.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
     method public java.lang.String getDataMimeType();
     method public android.net.Uri getDataUri();
+    method public android.os.Bundle getExtras();
     method public java.lang.CharSequence getSender();
     method public java.lang.CharSequence getText();
     method public long getTimestamp();
@@ -5502,7 +5506,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();
@@ -5516,7 +5519,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);
@@ -5524,6 +5526,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[]);
@@ -5536,14 +5539,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;
   }
@@ -5559,6 +5560,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();
@@ -5566,6 +5568,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();
@@ -5727,6 +5730,8 @@
     method public java.lang.CharSequence getContentDescription();
     method public android.graphics.drawable.Icon getIcon();
     method public java.lang.CharSequence getTitle();
+    method public boolean isEnabled();
+    method public void setEnabled(boolean);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.RemoteAction> CREATOR;
   }
@@ -6593,7 +6598,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();
@@ -7148,6 +7153,7 @@
     method public android.bluetooth.le.BluetoothLeScanner getBluetoothLeScanner();
     method public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices();
     method public static synchronized android.bluetooth.BluetoothAdapter getDefaultAdapter();
+    method public int getLeMaximumAdvertisingDataLength();
     method public java.lang.String getName();
     method public android.bluetooth.le.PeriodicAdvertisingManager getPeriodicAdvertisingManager();
     method public int getProfileConnectionState(int);
@@ -7959,7 +7965,7 @@
   }
 
   public final class AdvertisingSet {
-    method public void enableAdvertising(boolean);
+    method public void enableAdvertising(boolean, int);
     method public void periodicAdvertisingEnable(boolean);
     method public void setAdvertisingData(android.bluetooth.le.AdvertiseData);
     method public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters);
@@ -7992,7 +7998,6 @@
     method public int getInterval();
     method public int getPrimaryPhy();
     method public int getSecondaryPhy();
-    method public int getTimeout();
     method public int getTxPowerLevel();
     method public boolean includeTxPower();
     method public boolean isAnonymous();
@@ -8026,7 +8031,6 @@
     method public android.bluetooth.le.AdvertisingSetParameters.Builder setLegacyMode(boolean);
     method public android.bluetooth.le.AdvertisingSetParameters.Builder setPrimaryPhy(int);
     method public android.bluetooth.le.AdvertisingSetParameters.Builder setSecondaryPhy(int);
-    method public android.bluetooth.le.AdvertisingSetParameters.Builder setTimeout(int);
     method public android.bluetooth.le.AdvertisingSetParameters.Builder setTxPowerLevel(int);
   }
 
@@ -8035,6 +8039,8 @@
     method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
     method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback);
     method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
+    method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, int, android.bluetooth.le.AdvertisingSetCallback);
+    method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, int, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
     method public void stopAdvertising(android.bluetooth.le.AdvertiseCallback);
     method public void stopAdvertisingSet(android.bluetooth.le.AdvertisingSetCallback);
   }
@@ -10707,6 +10713,7 @@
     field public static final java.lang.String FEATURE_USB_ACCESSORY = "android.hardware.usb.accessory";
     field public static final java.lang.String FEATURE_USB_HOST = "android.hardware.usb.host";
     field public static final java.lang.String FEATURE_VERIFIED_BOOT = "android.software.verified_boot";
+    field public static final java.lang.String FEATURE_VR_HEADTRACKING = "android.hardware.vr.headtracking";
     field public static final java.lang.String FEATURE_VR_MODE = "android.software.vr.mode";
     field public static final java.lang.String FEATURE_VR_MODE_HIGH_PERFORMANCE = "android.hardware.vr.high_performance";
     field public static final java.lang.String FEATURE_VULKAN_HARDWARE_COMPUTE = "android.hardware.vulkan.compute";
@@ -12794,7 +12801,7 @@
   }
 
   public class ColorFilter {
-    ctor public ColorFilter();
+    ctor public deprecated ColorFilter();
   }
 
   public class ColorMatrix {
@@ -12818,6 +12825,9 @@
   public class ColorMatrixColorFilter extends android.graphics.ColorFilter {
     ctor public ColorMatrixColorFilter(android.graphics.ColorMatrix);
     ctor public ColorMatrixColorFilter(float[]);
+    method public void getColorMatrix(android.graphics.ColorMatrix);
+    method public void setColorMatrix(android.graphics.ColorMatrix);
+    method public void setColorMatrixArray(float[]);
   }
 
   public abstract class ColorSpace {
@@ -13041,6 +13051,10 @@
 
   public class LightingColorFilter extends android.graphics.ColorFilter {
     ctor public LightingColorFilter(int, int);
+    method public int getColorAdd();
+    method public int getColorMultiply();
+    method public void setColorAdd(int);
+    method public void setColorMultiply(int);
   }
 
   public class LinearGradient extends android.graphics.Shader {
@@ -13557,6 +13571,10 @@
 
   public class PorterDuffColorFilter extends android.graphics.ColorFilter {
     ctor public PorterDuffColorFilter(int, android.graphics.PorterDuff.Mode);
+    method public int getColor();
+    method public android.graphics.PorterDuff.Mode getMode();
+    method public void setColor(int);
+    method public void setMode(android.graphics.PorterDuff.Mode);
   }
 
   public class PorterDuffXfermode extends android.graphics.Xfermode {
@@ -21927,7 +21945,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 +22497,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 +22752,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 +22855,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 +23023,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 +24834,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 +25375,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);
@@ -31655,6 +31736,7 @@
     method public deprecated void setUserRestrictions(android.os.Bundle);
     method public deprecated void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
     method public static boolean supportsMultipleUsers();
+    field public static final java.lang.String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
     field public static final java.lang.String ALLOW_PARENT_PROFILE_APP_LINKING = "allow_parent_profile_app_linking";
     field public static final java.lang.String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
     field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
@@ -34004,6 +34086,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";
@@ -34938,6 +35021,7 @@
     field public static final java.lang.String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype";
     field public static final java.lang.String SETTINGS_CLASSNAME = "settings_classname";
     field public static final java.lang.String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
+    field public static final java.lang.String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
     field public static final java.lang.String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
     field public static final deprecated java.lang.String TTS_DEFAULT_COUNTRY = "tts_default_country";
     field public static final deprecated java.lang.String TTS_DEFAULT_LANG = "tts_default_lang";
@@ -36885,7 +36969,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";
@@ -36940,11 +37024,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[]);
   }
 
 }
@@ -37268,11 +37352,11 @@
     field public static final int INTERRUPTION_FILTER_UNKNOWN = 0; // 0x0
     field public static final int REASON_APP_CANCEL = 8; // 0x8
     field public static final int REASON_APP_CANCEL_ALL = 9; // 0x9
+    field public static final int REASON_CANCEL = 2; // 0x2
+    field public static final int REASON_CANCEL_ALL = 3; // 0x3
     field public static final int REASON_CHANNEL_BANNED = 17; // 0x11
-    field public static final int REASON_DELEGATE_CANCEL = 2; // 0x2
-    field public static final int REASON_DELEGATE_CANCEL_ALL = 3; // 0x3
-    field public static final int REASON_DELEGATE_CLICK = 1; // 0x1
-    field public static final int REASON_DELEGATE_ERROR = 4; // 0x4
+    field public static final int REASON_CLICK = 1; // 0x1
+    field public static final int REASON_ERROR = 4; // 0x4
     field public static final int REASON_GROUP_OPTIMIZATION = 13; // 0xd
     field public static final int REASON_GROUP_SUMMARY_CANCELED = 12; // 0xc
     field public static final int REASON_LISTENER_CANCEL = 10; // 0xa
@@ -39367,6 +39451,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";
@@ -39409,6 +39494,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";
@@ -39464,6 +39551,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";
@@ -39996,7 +40084,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();
@@ -40037,6 +40124,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();
@@ -41078,11 +41166,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;
   }
@@ -41107,22 +41194,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();
@@ -45367,8 +45454,8 @@
     method public void addTouchables(java.util.ArrayList<android.view.View>);
     method public android.view.ViewPropertyAnimator animate();
     method public void announceForAccessibility(java.lang.CharSequence);
-    method public void autofill(android.view.autofill.AutofillValue);
-    method public void autofillVirtual(int, android.view.autofill.AutofillValue);
+    method public boolean autofill(android.view.autofill.AutofillValue);
+    method public boolean autofill(int, android.view.autofill.AutofillValue);
     method protected boolean awakenScrollBars();
     method protected boolean awakenScrollBars(int);
     method protected boolean awakenScrollBars(int, boolean);
@@ -45458,7 +45545,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();
@@ -45782,7 +45869,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);
@@ -45925,20 +46012,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
@@ -46591,7 +46677,7 @@
     method public abstract int addChildCount(int);
     method public abstract void asyncCommit();
     method public abstract android.view.ViewStructure asyncNewChild(int);
-    method public abstract android.view.ViewStructure asyncNewChildForAutofill(int, int, int);
+    method public abstract android.view.ViewStructure asyncNewChild(int, int, int);
     method public abstract int getChildCount();
     method public abstract android.os.Bundle getExtras();
     method public abstract java.lang.CharSequence getHint();
@@ -46600,11 +46686,11 @@
     method public abstract int getTextSelectionStart();
     method public abstract boolean hasExtras();
     method public abstract android.view.ViewStructure newChild(int);
-    method public abstract android.view.ViewStructure newChildForAutofill(int, int, int);
+    method public abstract android.view.ViewStructure newChild(int, int, int);
     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);
@@ -46615,6 +46701,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);
@@ -46625,7 +46712,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);
@@ -47872,24 +47958,29 @@
   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 notifyValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
     method public void notifyViewEntered(android.view.View);
+    method public void notifyViewEntered(android.view.View, int, android.graphics.Rect);
     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 notifyViewExited(android.view.View, int);
     method public void registerCallback(android.view.autofill.AutofillManager.AutofillCallback);
+    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);
     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 {
     ctor public AutofillManager.AutofillCallback();
     method public void onAutofillEvent(android.view.View, int);
-    method public void onAutofillEventVirtual(android.view.View, int, int);
+    method public void onAutofillEvent(android.view.View, int, int);
     field public static final int EVENT_INPUT_HIDDEN = 2; // 0x2
     field public static final int EVENT_INPUT_SHOWN = 1; // 0x1
+    field public static final int EVENT_INPUT_UNAVAILABLE = 3; // 0x3
   }
 
   public final class AutofillValue implements android.os.Parcelable {
@@ -47902,6 +47993,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/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 91520f1..827a77f 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -411,7 +411,8 @@
             if (file.isFile()) {
                 try {
                     ApkLite baseApk = PackageParser.parseApkLite(file, 0);
-                    PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null);
+                    PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
+                            null, null);
                     params.sessionParams.setSize(
                             PackageHelper.calculateInstalledSize(pkgLite, false,
                             params.sessionParams.abiOverride));
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 78c29e8..37a11ec 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.metrics.LogMaker;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
@@ -23,6 +24,8 @@
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.app.ToolbarActionBar;
 import com.android.internal.app.WindowDecorActionBar;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.policy.PhoneWindow;
 
 import android.annotation.CallSuper;
@@ -765,6 +768,7 @@
     /*package*/ Configuration mCurrentConfig;
     private SearchManager mSearchManager;
     private MenuInflater mMenuInflater;
+    private final MetricsLogger mMetricsLogger = new MetricsLogger();
 
     static final class NonConfigurationInstances {
         Object activity;
@@ -7188,6 +7192,8 @@
     public void autofill(List<AutofillId> ids, List<AutofillValue> values) {
         final View root = getWindow().getDecorView();
         final int itemCount = ids.size();
+        int numApplied = 0;
+
         for (int i = 0; i < itemCount; i++) {
             final AutofillId id = ids.get(i);
             final AutofillValue value = values.get(i);
@@ -7197,12 +7203,22 @@
                 Log.w(TAG, "autofill(): no View with id " + viewId);
                 continue;
             }
+            final boolean wasApplied;
             if (id.isVirtual()) {
-                view.autofillVirtual(id.getVirtualChildId(), value);
+                wasApplied = view.autofill(id.getVirtualChildId(), value);
             } else {
-                view.autofill(value);
+                wasApplied = view.autofill(value);
+            }
+
+            if (wasApplied) {
+                numApplied++;
             }
         }
+
+        LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED);
+        log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount);
+        log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied);
+        mMetricsLogger.write(log);
     }
 
     /** @hide */
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 44cc5b4..6b53cd8 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;
@@ -148,6 +149,7 @@
 import libcore.io.EventLogger;
 import libcore.io.IoUtils;
 import libcore.net.event.NetworkEventDispatcher;
+import dalvik.system.BaseDexClassLoader;
 import dalvik.system.CloseGuard;
 import dalvik.system.VMDebug;
 import dalvik.system.VMRuntime;
@@ -335,6 +337,8 @@
         Configuration overrideConfig;
         // Used for consolidating configs before sending on to Activity.
         private Configuration tmpConfig = new Configuration();
+        // Callback used for updating activity override config.
+        ViewRootImpl.ActivityConfigCallback configCallback;
         ActivityClientRecord nextIdle;
 
         ProfilerInfo profilerInfo;
@@ -370,6 +374,14 @@
             stopped = false;
             hideForNow = false;
             nextIdle = null;
+            configCallback = (Configuration overrideConfig, int newDisplayId) -> {
+                if (activity == null) {
+                    throw new IllegalStateException(
+                            "Received config update for non-existing activity");
+                }
+                activity.mMainThread.handleActivityConfigurationChanged(
+                        new ActivityConfigChangeData(token, overrideConfig), newDisplayId);
+            };
         }
 
         public boolean isPreHoneycomb() {
@@ -3679,6 +3691,12 @@
                 if (r.activity.mVisibleFromClient) {
                     r.activity.makeVisible();
                 }
+                final ViewRootImpl viewRoot = r.activity.mDecor.getViewRootImpl();
+                if (viewRoot != null) {
+                    // TODO: Figure out the best place to set the callback.
+                    // This looks like a place where decor view is already initialized.
+                    viewRoot.setActivityConfigCallback(r.configCallback);
+                }
             }
 
             if (!r.onlyLocalRequest) {
@@ -5027,7 +5045,7 @@
      * @param displayId Id of the display where activity was moved to, -1 if there was no move and
      *                  value didn't change.
      */
-    private void handleActivityConfigurationChanged(ActivityConfigChangeData data, int displayId) {
+    void handleActivityConfigurationChanged(ActivityConfigChangeData data, int displayId) {
         ActivityClientRecord r = mActivities.get(data.activityToken);
         // Check input params.
         if (r == null || r.activity == null) {
@@ -5044,6 +5062,7 @@
 
         // Perform updates.
         r.overrideConfig = data.overrideConfig;
+        final ViewRootImpl viewRoot = r.activity.mDecor.getViewRootImpl();
         if (movedToDifferentDisplay) {
             if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity moved to display, activity:"
                     + r.activityInfo.name + ", displayId=" + displayId
@@ -5051,13 +5070,15 @@
 
             performConfigurationChangedForActivity(r, mCompatConfiguration, displayId,
                     true /* movedToDifferentDisplay */);
-            final ViewRootImpl viewRoot = r.activity.mDecor.getViewRootImpl();
             viewRoot.onMovedToDisplay(displayId);
         } else {
             if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity config changed: "
                     + r.activityInfo.name + ", config=" + data.overrideConfig);
             performConfigurationChangedForActivity(r, mCompatConfiguration);
         }
+        // Notify the ViewRootImpl instance about configuration changes. It may have initiated this
+        // update to make sure that resources are updated before updating itself.
+        viewRoot.updateConfiguration();
         mSomeActivitiesChanged = true;
     }
 
@@ -5570,6 +5591,16 @@
             }
         }
 
+        // If we use profiles, setup the dex reporter to notify package manager
+        // of any relevant dex loads. The idle maintenance job will use the information
+        // reported to optimize the loaded dex files.
+        // Note that we only need one global reporter per app.
+        // Make sure we do this before calling onCreate so that we can capture the
+        // complete application startup.
+        if (SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false)) {
+            BaseDexClassLoader.setReporter(DexLoadReporter.getInstance());
+        }
+
         // Install the Network Security Config Provider. This must happen before the application
         // code is loaded to prevent issues with instances of TLS objects being created before
         // the provider is installed.
@@ -5665,6 +5696,7 @@
         }
 
         // Preload fonts resources
+        Typeface.setApplicationContext(appContext);
         try {
             final ApplicationInfo info =
                     getPackageManager().getApplicationInfo(
@@ -6282,35 +6314,26 @@
         // add dropbox logging to libcore
         DropBox.setReporter(new DropBoxReporter());
 
-        ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {
-            @Override
-            public void onConfigurationChanged(Configuration newConfig) {
-                synchronized (mResourcesManager) {
-                    // We need to apply this change to the resources
-                    // immediately, because upon returning the view
-                    // hierarchy will be informed about it.
-                    if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {
-                        updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
-                                mResourcesManager.getConfiguration().getLocales());
+        ViewRootImpl.ConfigChangedCallback configChangedCallback
+                = (Configuration globalConfig) -> {
+            synchronized (mResourcesManager) {
+                // We need to apply this change to the resources immediately, because upon returning
+                // the view hierarchy will be informed about it.
+                if (mResourcesManager.applyConfigurationToResourcesLocked(globalConfig,
+                        null /* compat */)) {
+                    updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
+                            mResourcesManager.getConfiguration().getLocales());
 
-                        // This actually changed the resources!  Tell
-                        // everyone about it.
-                        if (mPendingConfiguration == null ||
-                                mPendingConfiguration.isOtherSeqNewer(newConfig)) {
-                            mPendingConfiguration = newConfig;
-
-                            sendMessage(H.CONFIGURATION_CHANGED, newConfig);
-                        }
+                    // This actually changed the resources! Tell everyone about it.
+                    if (mPendingConfiguration == null
+                            || mPendingConfiguration.isOtherSeqNewer(globalConfig)) {
+                        mPendingConfiguration = globalConfig;
+                        sendMessage(H.CONFIGURATION_CHANGED, globalConfig);
                     }
                 }
             }
-            @Override
-            public void onLowMemory() {
-            }
-            @Override
-            public void onTrimMemory(int level) {
-            }
-        });
+        };
+        ViewRootImpl.addConfigCallback(configChangedCallback);
     }
 
     public static ActivityThread systemMain() {
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/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index 9f1a539..e645261 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -22,6 +22,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.Binder;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemProperties;
@@ -430,7 +431,7 @@
             dest.writeInt(throwLineNumber);
             dest.writeString(stackTrace);
             int total = dest.dataPosition()-start;
-            if (total > 20*1024) {
+            if (Binder.CHECK_PARCEL_SIZE && total > 20*1024) {
                 Slog.d("Error", "ERR: exClass=" + exceptionClassName);
                 Slog.d("Error", "ERR: exMsg=" + exceptionMessage);
                 Slog.d("Error", "ERR: file=" + throwFileName);
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/ContextImpl.java b/core/java/android/app/ContextImpl.java
index ede9281..8a3d9b1 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -156,13 +156,13 @@
     @GuardedBy("ContextImpl.class")
     private ArrayMap<String, File> mSharedPrefsPaths;
 
-    final ActivityThread mMainThread;
-    final LoadedApk mPackageInfo;
-    private ClassLoader mClassLoader;
+    final @NonNull ActivityThread mMainThread;
+    final @NonNull LoadedApk mPackageInfo;
+    private @Nullable ClassLoader mClassLoader;
 
-    private final IBinder mActivityToken;
+    private final @Nullable IBinder mActivityToken;
 
-    private final UserHandle mUser;
+    private final @Nullable UserHandle mUser;
 
     private final ApplicationContentResolver mContentResolver;
 
@@ -181,6 +181,9 @@
     private PackageManager mPackageManager;
     private Context mReceiverRestrictedContext = null;
 
+    // The name of the split this Context is representing. May be null.
+    private @Nullable String mSplitName = null;
+
     private final Object mSync = new Object();
 
     @GuardedBy("mSync")
@@ -1914,17 +1917,25 @@
         }
     }
 
-    private static Resources createResources(IBinder activityToken, LoadedApk pi, int displayId,
-            Configuration overrideConfig, CompatibilityInfo compatInfo) {
+    private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName,
+            int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
+        final String[] splitResDirs;
+        final ClassLoader classLoader;
+        try {
+            splitResDirs = pi.getSplitPaths(splitName);
+            classLoader = pi.getSplitClassLoader(splitName);
+        } catch (NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
         return ResourcesManager.getInstance().getResources(activityToken,
                 pi.getResDir(),
-                pi.getSplitResDirs(),
+                splitResDirs,
                 pi.getOverlayDirs(),
                 pi.getApplicationInfo().sharedLibraryFiles,
                 displayId,
                 overrideConfig,
                 compatInfo,
-                pi.getClassLoader());
+                classLoader);
     }
 
     @Override
@@ -1933,14 +1944,13 @@
         LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
                 flags | CONTEXT_REGISTER_PACKAGE);
         if (pi != null) {
-            ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken,
-                    new UserHandle(UserHandle.getUserId(application.uid)), flags,
-                    null);
+            ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken,
+                    new UserHandle(UserHandle.getUserId(application.uid)), flags, null);
 
             final int displayId = mDisplay != null
                     ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
 
-            c.setResources(createResources(mActivityToken, pi, displayId, null,
+            c.setResources(createResources(mActivityToken, pi, null, displayId, null,
                     getDisplayAdjustments(displayId).getCompatibilityInfo()));
             if (c.mResources != null) {
                 return c;
@@ -1964,20 +1974,20 @@
         if (packageName.equals("system") || packageName.equals("android")) {
             // The system resources are loaded in every application, so we can safely copy
             // the context without reloading Resources.
-            return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, user, flags,
-                    null);
+            return new ContextImpl(this, mMainThread, mPackageInfo, null, mActivityToken, user,
+                    flags, null);
         }
 
         LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
                 flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
         if (pi != null) {
-            ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken, user, flags,
-                    null);
+            ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user,
+                    flags, null);
 
             final int displayId = mDisplay != null
                     ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
 
-            c.setResources(createResources(mActivityToken, pi, displayId, null,
+            c.setResources(createResources(mActivityToken, pi, null, displayId, null,
                     getDisplayAdjustments(displayId).getCompatibilityInfo()));
             if (c.mResources != null) {
                 return c;
@@ -1999,7 +2009,7 @@
         final ClassLoader classLoader = mPackageInfo.getSplitClassLoader(splitName);
         final String[] paths = mPackageInfo.getSplitPaths(splitName);
 
-        final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo,
+        final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, splitName,
                 mActivityToken, mUser, mFlags, classLoader);
 
         final int displayId = mDisplay != null
@@ -2024,11 +2034,11 @@
             throw new IllegalArgumentException("overrideConfiguration must not be null");
         }
 
-        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
-                mUser, mFlags, mClassLoader);
+        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
+                mActivityToken, mUser, mFlags, mClassLoader);
 
         final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
-        context.setResources(createResources(mActivityToken, mPackageInfo, displayId,
+        context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
                 overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo()));
         return context;
     }
@@ -2039,12 +2049,12 @@
             throw new IllegalArgumentException("display must not be null");
         }
 
-        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
-                mUser, mFlags, mClassLoader);
+        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
+                mActivityToken, mUser, mFlags, mClassLoader);
 
         final int displayId = display.getDisplayId();
-        context.setResources(createResources(mActivityToken, mPackageInfo, displayId, null,
-                getDisplayAdjustments(displayId).getCompatibilityInfo()));
+        context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
+                null, getDisplayAdjustments(displayId).getCompatibilityInfo()));
         context.mDisplay = display;
         return context;
     }
@@ -2053,16 +2063,16 @@
     public Context createDeviceProtectedStorageContext() {
         final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)
                 | Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
-        return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, mUser, flags,
-                mClassLoader);
+        return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
+                flags, mClassLoader);
     }
 
     @Override
     public Context createCredentialProtectedStorageContext() {
         final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)
                 | Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
-        return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, mUser, flags,
-                mClassLoader);
+        return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
+                flags, mClassLoader);
     }
 
     @Override
@@ -2149,7 +2159,7 @@
 
     static ContextImpl createSystemContext(ActivityThread mainThread) {
         LoadedApk packageInfo = new LoadedApk(mainThread);
-        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, 0,
+        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
                 null);
         context.setResources(packageInfo.getResources());
         context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
@@ -2159,7 +2169,7 @@
 
     static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
         if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
-        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, 0,
+        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
                 null);
         context.setResources(packageInfo.getResources());
         return context;
@@ -2186,8 +2196,8 @@
             }
         }
 
-        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityToken, null,
-                0, classLoader);
+        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
+                activityToken, null, 0, classLoader);
 
         // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
         displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
@@ -2214,9 +2224,10 @@
         return context;
     }
 
-    private ContextImpl(ContextImpl container, ActivityThread mainThread,
-            LoadedApk packageInfo, IBinder activityToken, UserHandle user, int flags,
-            ClassLoader classLoader) {
+    private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
+            @NonNull LoadedApk packageInfo, @Nullable String splitName,
+            @Nullable IBinder activityToken, @Nullable UserHandle user, int flags,
+            @Nullable ClassLoader classLoader) {
         mOuterContext = this;
 
         // If creator didn't specify which storage to use, use the default
@@ -2241,6 +2252,7 @@
         mUser = user;
 
         mPackageInfo = packageInfo;
+        mSplitName = splitName;
         mClassLoader = classLoader;
         mResourcesManager = ResourcesManager.getInstance();
 
diff --git a/core/java/android/app/DexLoadReporter.java b/core/java/android/app/DexLoadReporter.java
new file mode 100644
index 0000000..13f288a
--- /dev/null
+++ b/core/java/android/app/DexLoadReporter.java
@@ -0,0 +1,168 @@
+/*
+ * 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.app;
+
+import android.os.FileUtils;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import dalvik.system.BaseDexClassLoader;
+import dalvik.system.VMRuntime;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A dex load reporter which will notify package manager of any dex file loaded
+ * with {@code BaseDexClassLoader}.
+ * The goals are:
+ *     1) discover secondary dex files so that they can be optimized during the
+ *        idle maintenance job.
+ *     2) determine whether or not a dex file is used by an app which does not
+ *        own it (in order to select the optimal compilation method).
+ * @hide
+ */
+/*package*/ class DexLoadReporter implements BaseDexClassLoader.Reporter {
+    private static final String TAG = "DexLoadReporter";
+
+    private static final DexLoadReporter INSTANCE = new DexLoadReporter();
+
+    private static final boolean DEBUG = false;
+
+    // We must guard the access to the list of data directories because
+    // we might have concurrent accesses. Apps might load dex files while
+    // new data dirs are registered (due to creation of LoadedApks via
+    // create createApplicationContext).
+    @GuardedBy("mDataDirs")
+    private final Set<String> mDataDirs;
+
+    private DexLoadReporter() {
+        mDataDirs = new HashSet<>();
+    }
+
+    /*package*/ static DexLoadReporter getInstance() {
+        return INSTANCE;
+    }
+
+    /**
+     * Register an application data directory with the reporter.
+     * The data directories are used to determine if a dex file is secondary dex or not.
+     * Note that this method may be called multiple times for the same app, registering
+     * different data directories. This may happen when apps share the same user id
+     * ({@code android:sharedUserId}). For example, if app1 and app2 share the same user
+     * id, and app1 loads app2 apk, then both data directories will be registered.
+     */
+    /*package*/ void registerAppDataDir(String packageName, String dataDir) {
+        if (DEBUG) {
+            Slog.i(TAG, "Package " + packageName + " registering data dir: " + dataDir);
+        }
+        // TODO(calin): A few code paths imply that the data dir
+        // might be null. Investigate when that can happen.
+        if (dataDir != null) {
+            synchronized (mDataDirs) {
+                mDataDirs.add(dataDir);
+            }
+        }
+    }
+
+    @Override
+    public void report(List<String> dexPaths) {
+        if (dexPaths.isEmpty()) {
+            return;
+        }
+        // Notify the package manager about the dex loads unconditionally.
+        // The load might be for either a primary or secondary dex file.
+        notifyPackageManager(dexPaths);
+        // Check for secondary dex files and register them for profiling if
+        // possible.
+        registerSecondaryDexForProfiling(dexPaths);
+    }
+
+    private void notifyPackageManager(List<String> dexPaths) {
+        String packageName = ActivityThread.currentPackageName();
+        try {
+            ActivityThread.getPackageManager().notifyDexLoad(
+                    packageName, dexPaths, VMRuntime.getRuntime().vmInstructionSet());
+        } catch (RemoteException re) {
+            Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re);
+        }
+    }
+
+    private void registerSecondaryDexForProfiling(List<String> dexPaths) {
+        if (!SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) {
+            return;
+        }
+        // Make a copy of the current data directories so that we don't keep the lock
+        // while registering for profiling. The registration will perform I/O to
+        // check for or create the profile.
+        String[] dataDirs;
+        synchronized (mDataDirs) {
+            dataDirs = mDataDirs.toArray(new String[0]);
+        }
+        for (String dexPath : dexPaths) {
+            registerSecondaryDexForProfiling(dexPath, dataDirs);
+        }
+    }
+
+    private void registerSecondaryDexForProfiling(String dexPath, String[] dataDirs) {
+        if (!isSecondaryDexFile(dexPath, dataDirs)) {
+            // The dex path is not a secondary dex file. Nothing to do.
+            return;
+        }
+        File secondaryProfile = getSecondaryProfileFile(dexPath);
+        try {
+            // Create the profile if not already there.
+            // Returns true if the file was created, false if the file already exists.
+            // or throws exceptions in case of errors.
+            boolean created = secondaryProfile.createNewFile();
+            if (DEBUG && created) {
+                Slog.i(TAG, "Created profile for secondary dex: " + secondaryProfile);
+            }
+        } catch (IOException ex) {
+            Slog.e(TAG, "Failed to create profile for secondary dex " + secondaryProfile +
+                    ":" + ex.getMessage());
+            // Don't move forward with the registration if we failed to create the profile.
+            return;
+        }
+
+        VMRuntime.registerAppInfo(secondaryProfile.getPath(), new String[] { dexPath });
+    }
+
+    // A dex file is a secondary dex file if it is in any of the registered app
+    // data directories.
+    private boolean isSecondaryDexFile(String dexPath, String[] dataDirs) {
+        for (String dataDir : dataDirs) {
+            if (FileUtils.contains(dataDir, dexPath)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // Secondary dex profiles are stored next to the dex file and have the same
+    // name with '.prof' appended.
+    // NOTE: Keep in sync with installd.
+    private File getSecondaryProfileFile(String dexPath) {
+        return new File(dexPath + ".prof");
+    }
+}
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..dbed1be 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -27,7 +27,8 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.content.pm.split.SplitDependencyLoaderHelper;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.split.SplitDependencyLoader;
 import android.content.res.AssetManager;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Resources;
@@ -49,13 +50,11 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.SparseIntArray;
 import android.view.Display;
 import android.view.DisplayAdjustments;
 
 import com.android.internal.util.ArrayUtils;
 
-import dalvik.system.BaseDexClassLoader;
 import dalvik.system.VMRuntime;
 
 import java.io.File;
@@ -305,7 +304,7 @@
                 final String[] splitPaths;
                 try {
                     splitPaths = getSplitPaths(null);
-                } catch (PackageManager.NameNotFoundException e) {
+                } catch (NameNotFoundException e) {
                     // This should NEVER fail.
                     throw new AssertionError("null split not found");
                 }
@@ -337,7 +336,7 @@
         mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs;
 
         if (aInfo.requestsIsolatedSplitLoading() && !ArrayUtils.isEmpty(mSplitNames)) {
-            mSplitLoader = new SplitDependencyLoader(aInfo.splitDependencies);
+            mSplitLoader = new SplitDependencyLoaderImpl(aInfo.splitDependencies);
         }
     }
 
@@ -466,110 +465,88 @@
         }
     }
 
-    private class SplitDependencyLoader
-            extends SplitDependencyLoaderHelper<PackageManager.NameNotFoundException> {
-        private String[] mCachedBaseResourcePath;
+    /*
+     * All indices received by the super class should be shifted by 1 when accessing mSplitNames,
+     * etc. The super class assumes the base APK is index 0, while the PackageManager APIs don't
+     * include the base APK in the list of splits.
+     */
+    private class SplitDependencyLoaderImpl extends SplitDependencyLoader<NameNotFoundException> {
         private final String[][] mCachedResourcePaths;
-        private final ClassLoader[] mCachedSplitClassLoaders;
+        private final ClassLoader[] mCachedClassLoaders;
 
-        SplitDependencyLoader(SparseIntArray dependencies) {
+        SplitDependencyLoaderImpl(@NonNull SparseArray<int[]> dependencies) {
             super(dependencies);
-            mCachedResourcePaths = new String[mSplitNames.length][];
-            mCachedSplitClassLoaders = new ClassLoader[mSplitNames.length];
+            mCachedResourcePaths = new String[mSplitNames.length + 1][];
+            mCachedClassLoaders = new ClassLoader[mSplitNames.length + 1];
         }
 
         @Override
         protected boolean isSplitCached(int splitIdx) {
-            if (splitIdx != -1) {
-                return mCachedSplitClassLoaders[splitIdx] != null;
-            }
-            return mClassLoader != null && mCachedBaseResourcePath != null;
-        }
-
-        private void addAllConfigSplits(String splitName, ArrayList<String> outAssetPaths) {
-            for (int i = 0; i < mSplitNames.length; i++) {
-                if (isConfigurationSplitOf(mSplitNames[i], splitName)) {
-                    outAssetPaths.add(mSplitResDirs[i]);
-                }
-            }
+            return mCachedClassLoaders[splitIdx] != null;
         }
 
         @Override
-        protected void constructSplit(int splitIdx, int parentSplitIdx) throws
-                PackageManager.NameNotFoundException {
+        protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
+                int parentSplitIdx) throws NameNotFoundException {
             final ArrayList<String> splitPaths = new ArrayList<>();
-            if (splitIdx == -1) {
+            if (splitIdx == 0) {
                 createOrUpdateClassLoaderLocked(null);
-                addAllConfigSplits(null, splitPaths);
-                mCachedBaseResourcePath = splitPaths.toArray(new String[splitPaths.size()]);
+                mCachedClassLoaders[0] = mClassLoader;
+
+                // Never add the base resources here, they always get added no matter what.
+                for (int configSplitIdx : configSplitIndices) {
+                    splitPaths.add(mSplitResDirs[configSplitIdx - 1]);
+                }
+                mCachedResourcePaths[0] = splitPaths.toArray(new String[splitPaths.size()]);
                 return;
             }
 
-            final ClassLoader parent;
-            if (parentSplitIdx == -1) {
-                // The parent is the base APK, so use its ClassLoader as parent
-                // and its configuration splits as part of our own too.
-                parent = mClassLoader;
-                Collections.addAll(splitPaths, mCachedBaseResourcePath);
-            } else {
-                parent = mCachedSplitClassLoaders[parentSplitIdx];
-                Collections.addAll(splitPaths, mCachedResourcePaths[parentSplitIdx]);
+            // Since we handled the special base case above, parentSplitIdx is always valid.
+            final ClassLoader parent = mCachedClassLoaders[parentSplitIdx];
+            mCachedClassLoaders[splitIdx] = ApplicationLoaders.getDefault().getClassLoader(
+                    mSplitAppDirs[splitIdx - 1], getTargetSdkVersion(), false, null, null, parent);
+
+            Collections.addAll(splitPaths, mCachedResourcePaths[parentSplitIdx]);
+            splitPaths.add(mSplitResDirs[splitIdx - 1]);
+            for (int configSplitIdx : configSplitIndices) {
+                splitPaths.add(mSplitResDirs[configSplitIdx - 1]);
             }
-
-            mCachedSplitClassLoaders[splitIdx] = ApplicationLoaders.getDefault().getClassLoader(
-                    mSplitAppDirs[splitIdx], getTargetSdkVersion(), false, null, null, parent);
-
-            splitPaths.add(mSplitResDirs[splitIdx]);
-            addAllConfigSplits(mSplitNames[splitIdx], splitPaths);
             mCachedResourcePaths[splitIdx] = splitPaths.toArray(new String[splitPaths.size()]);
         }
 
-        private int ensureSplitLoaded(String splitName)
-                throws PackageManager.NameNotFoundException {
-            final int idx;
-            if (splitName == null) {
-                idx = -1;
-            } else {
+        private int ensureSplitLoaded(String splitName) throws NameNotFoundException {
+            int idx = 0;
+            if (splitName != null) {
                 idx = Arrays.binarySearch(mSplitNames, splitName);
                 if (idx < 0) {
                     throw new PackageManager.NameNotFoundException(
                             "Split name '" + splitName + "' is not installed");
                 }
+                idx += 1;
             }
-
             loadDependenciesForSplit(idx);
             return idx;
         }
 
-        ClassLoader getClassLoaderForSplit(String splitName)
-                throws PackageManager.NameNotFoundException {
-            final int idx = ensureSplitLoaded(splitName);
-            if (idx < 0) {
-                return mClassLoader;
-            }
-            return mCachedSplitClassLoaders[idx];
+        ClassLoader getClassLoaderForSplit(String splitName) throws NameNotFoundException {
+            return mCachedClassLoaders[ensureSplitLoaded(splitName)];
         }
 
-        String[] getSplitPathsForSplit(String splitName)
-                throws PackageManager.NameNotFoundException {
-            final int idx = ensureSplitLoaded(splitName);
-            if (idx < 0) {
-                return mCachedBaseResourcePath;
-            }
-            return mCachedResourcePaths[idx];
+        String[] getSplitPathsForSplit(String splitName) throws NameNotFoundException {
+            return mCachedResourcePaths[ensureSplitLoaded(splitName)];
         }
     }
 
-    private SplitDependencyLoader mSplitLoader;
+    private SplitDependencyLoaderImpl mSplitLoader;
 
-    ClassLoader getSplitClassLoader(String splitName) throws PackageManager.NameNotFoundException {
+    ClassLoader getSplitClassLoader(String splitName) throws NameNotFoundException {
         if (mSplitLoader == null) {
             return mClassLoader;
         }
         return mSplitLoader.getClassLoaderForSplit(splitName);
     }
 
-    String[] getSplitPaths(String splitName) throws PackageManager.NameNotFoundException {
+    String[] getSplitPaths(String splitName) throws NameNotFoundException {
         if (mSplitLoader == null) {
             return mSplitResDirs;
         }
@@ -597,8 +574,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);
@@ -753,39 +729,10 @@
         VMRuntime.registerAppInfo(profileFile.getPath(),
                 codePaths.toArray(new String[codePaths.size()]));
 
-        // Setup the reporter to notify package manager of any relevant dex loads.
-        // At this point the primary apk is loaded and will not be reported.
-        // Anything loaded from now on will be tracked as a potential secondary
-        // or foreign dex file. The goal is to enable:
-        //    1) monitoring and compilation of secondary dex file
-        //    2) track whether or not a dex file is used by other apps (used to
-        //       determined the compilation filter of apks).
-        if (BaseDexClassLoader.getReporter() != DexLoadReporter.INSTANCE) {
-            // Set the dex load reporter if not already set.
-            // Note that during the app's life cycle different LoadedApks may be
-            // created and loaded (e.g. if two different apps share the same runtime).
-            BaseDexClassLoader.setReporter(DexLoadReporter.INSTANCE);
-        }
-    }
-
-    private static class DexLoadReporter implements BaseDexClassLoader.Reporter {
-        private static final DexLoadReporter INSTANCE = new DexLoadReporter();
-
-        private DexLoadReporter() {}
-
-        @Override
-        public void report(List<String> dexPaths) {
-            if (dexPaths.isEmpty()) {
-                return;
-            }
-            String packageName = ActivityThread.currentPackageName();
-            try {
-                ActivityThread.getPackageManager().notifyDexLoad(
-                        packageName, dexPaths, VMRuntime.getRuntime().vmInstructionSet());
-            } catch (RemoteException re) {
-                Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re);
-            }
-        }
+        // Register the app data directory with the reporter. It will
+        // help deciding whether or not a dex file is the primary apk or a
+        // secondary dex.
+        DexLoadReporter.getInstance().registerAppDataDir(mPackageName, mDataDir);
     }
 
     /**
@@ -956,7 +903,7 @@
             final String[] splitPaths;
             try {
                 splitPaths = getSplitPaths(null);
-            } catch (PackageManager.NameNotFoundException e) {
+            } catch (NameNotFoundException e) {
                 // This should never fail.
                 throw new AssertionError("null split not found");
             }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index a098591..aee9d386 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1935,7 +1935,9 @@
         if (this.actions != null) {
             that.actions = new Action[this.actions.length];
             for(int i=0; i<this.actions.length; i++) {
-                that.actions[i] = this.actions[i].clone();
+                if ( this.actions[i] != null) {
+                    that.actions[i] = this.actions[i].clone();
+                }
             }
         }
 
@@ -3432,7 +3434,9 @@
          * @param action The action to add.
          */
         public Builder addAction(Action action) {
-            mActions.add(action);
+            if (action != null) {
+                mActions.add(action);
+            }
             return this;
         }
 
@@ -3446,7 +3450,9 @@
         public Builder setActions(Action... actions) {
             mActions.clear();
             for (int i = 0; i < actions.length; i++) {
-                mActions.add(actions[i]);
+                if (actions[i] != null) {
+                    mActions.add(actions[i]);
+                }
             }
             return this;
         }
@@ -5651,11 +5657,13 @@
             static final String KEY_SENDER = "sender";
             static final String KEY_DATA_MIME_TYPE = "type";
             static final String KEY_DATA_URI= "uri";
+            static final String KEY_EXTRAS_BUNDLE = "extras";
 
             private final CharSequence mText;
             private final long mTimestamp;
             private final CharSequence mSender;
 
+            private Bundle mExtras = new Bundle();
             private String mDataMimeType;
             private Uri mDataUri;
 
@@ -5724,6 +5732,13 @@
             }
 
             /**
+             * Get the extras Bundle for this message.
+             */
+            public Bundle getExtras() {
+                return mExtras;
+            }
+
+            /**
              * Get the text used to display the contact's name in the messaging experience
              */
             public CharSequence getSender() {
@@ -5760,6 +5775,9 @@
                 if (mDataUri != null) {
                     bundle.putParcelable(KEY_DATA_URI, mDataUri);
                 }
+                if (mExtras != null) {
+                    bundle.putBundle(KEY_EXTRAS_BUNDLE, mExtras);
+                }
                 return bundle;
             }
 
@@ -5794,10 +5812,12 @@
                                 bundle.getLong(KEY_TIMESTAMP), bundle.getCharSequence(KEY_SENDER));
                         if (bundle.containsKey(KEY_DATA_MIME_TYPE) &&
                                 bundle.containsKey(KEY_DATA_URI)) {
-
                             message.setData(bundle.getString(KEY_DATA_MIME_TYPE),
                                     (Uri) bundle.getParcelable(KEY_DATA_URI));
                         }
+                        if (bundle.containsKey(KEY_EXTRAS_BUNDLE)) {
+                            message.getExtras().putAll(bundle.getBundle(KEY_EXTRAS_BUNDLE));
+                        }
                         return message;
                     }
                 } catch (ClassCastException e) {
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/RemoteAction.java b/core/java/android/app/RemoteAction.java
index 5958bc1..e7fe407 100644
--- a/core/java/android/app/RemoteAction.java
+++ b/core/java/android/app/RemoteAction.java
@@ -41,12 +41,14 @@
     private final CharSequence mTitle;
     private final CharSequence mContentDescription;
     private final PendingIntent mActionIntent;
+    private boolean mEnabled;
 
     RemoteAction(Parcel in) {
         mIcon = Icon.CREATOR.createFromParcel(in);
         mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
         mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
         mActionIntent = PendingIntent.CREATOR.createFromParcel(in);
+        mEnabled = in.readBoolean();
     }
 
     public RemoteAction(@NonNull Icon icon, @NonNull CharSequence title,
@@ -59,6 +61,21 @@
         mTitle = title;
         mContentDescription = contentDescription;
         mActionIntent = intent;
+        mEnabled = true;
+    }
+
+    /**
+     * Sets whether this action is enabled.
+     */
+    public void setEnabled(boolean enabled) {
+        mEnabled = enabled;
+    }
+
+    /**
+     * Return whether this action is enabled.
+     */
+    public boolean isEnabled() {
+        return mEnabled;
     }
 
     /**
@@ -91,7 +108,9 @@
 
     @Override
     public RemoteAction clone() {
-        return new RemoteAction(mIcon, mTitle, mContentDescription, mActionIntent);
+        RemoteAction action = new RemoteAction(mIcon, mTitle, mContentDescription, mActionIntent);
+        action.setEnabled(mEnabled);
+        return action;
     }
 
     @Override
@@ -105,11 +124,13 @@
         TextUtils.writeToParcel(mTitle, out, flags);
         TextUtils.writeToParcel(mContentDescription, out, flags);
         mActionIntent.writeToParcel(out, flags);
+        out.writeBoolean(mEnabled);
     }
 
     public void dump(String prefix, PrintWriter pw) {
         pw.print(prefix);
         pw.print("title=" + mTitle);
+        pw.print(" enabled=" + mEnabled);
         pw.print(" contentDescription=" + mContentDescription);
         pw.print(" icon=" + mIcon);
         pw.print(" action=" + mActionIntent.getIntent());
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..27bfb51 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();
             }
         }
 
@@ -1612,9 +1612,8 @@
             return newChild(index, false, 0, 0);
         }
 
-        // TODO(b/33197203, b/33802548): add CTS/unit test
         @Override
-        public ViewStructure newChildForAutofill(int index, int virtualId, int flags) {
+        public ViewStructure newChild(int index, int virtualId, int flags) {
             return newChild(index, true, virtualId, flags);
         }
 
@@ -1624,7 +1623,7 @@
         }
 
         @Override
-        public ViewStructure asyncNewChildForAutofill(int index, int virtualId, int flags) {
+        public ViewStructure asyncNewChild(int index, int virtualId, int flags) {
             return asyncNewChild(index, true, virtualId);
         }
 
@@ -1663,7 +1662,7 @@
         }
 
         @Override
-        public void setAutofillHint(@View.AutofillHint int hint) {
+        public void setAutofillHint(@Nullable String[] hint) {
             mNode.mAutofillHint = hint;
         }
 
@@ -1683,8 +1682,8 @@
         }
 
         @Override
-        public void setSanitized(boolean sanitized) {
-            mNode.mSanitized = sanitized;
+        public void setDataIsSensitive(boolean sensitive) {
+            mNode.mSanitized = !sensitive;
         }
 
         @Override
@@ -1812,7 +1811,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/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 488511b..4e1e42d 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1483,6 +1483,25 @@
     }
 
     /**
+     * Return the maximum LE advertising data length,
+     * if LE Extended Advertising feature is supported.
+     *
+     * @return the maximum LE advertising data length.
+     */
+    public int getLeMaximumAdvertisingDataLength() {
+        if (!getLeAccess()) return 0;
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null) return mService.getLeMaximumAdvertisingDataLength();
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to get getLeMaximumAdvertisingDataLength, error: ", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+        return 0;
+    }
+
+    /**
      * Return true if hardware has entries available for matching beacons
      *
      * @return true if there are hw entries available for matching beacons
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 76ca554..b337817 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -108,6 +108,7 @@
     boolean isLeCodedPhySupported();
     boolean isLeExtendedAdvertisingSupported();
     boolean isLePeriodicAdvertisingSupported();
+    int getLeMaximumAdvertisingDataLength();
     BluetoothActivityEnergyInfo reportActivityInfo();
 
     /**
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 33fedc7..c281c7f 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -50,20 +50,12 @@
     void stopScan(in int scannerId);
     void flushPendingBatchResults(in int scannerId);
 
-    void registerAdvertiser(in IAdvertiserCallback callback);
-    void unregisterAdvertiser(in int advertiserId);
-    void startMultiAdvertising(in int advertiserId,
-                               in AdvertiseData advertiseData,
-                               in AdvertiseData scanResponse,
-                               in AdvertiseSettings settings);
-    void stopMultiAdvertising(in int advertiserId);
-
     void startAdvertisingSet(in AdvertisingSetParameters parameters, in AdvertiseData advertiseData,
                                 in AdvertiseData scanResponse, in PeriodicAdvertisingParameters periodicParameters,
-                                in AdvertiseData periodicData, in IAdvertisingSetCallback callback);
+                                in AdvertiseData periodicData, in int timeout, in IAdvertisingSetCallback callback);
     void stopAdvertisingSet(in IAdvertisingSetCallback callback);
 
-    void enableAdverisingSet(in int advertiserId, in boolean enable);
+    void enableAdverisingSet(in int advertiserId, in boolean enable, in int timeout);
     void setAdvertisingData(in int advertiserId, in AdvertiseData data);
     void setScanResponseData(in int advertiserId, in AdvertiseData data);
     void setAdvertisingParameters(in int advertiserId, in AdvertisingSetParameters parameters);
diff --git a/core/java/android/bluetooth/le/AdvertisingSet.java b/core/java/android/bluetooth/le/AdvertisingSet.java
index 1524022..5524a2b 100644
--- a/core/java/android/bluetooth/le/AdvertisingSet.java
+++ b/core/java/android/bluetooth/le/AdvertisingSet.java
@@ -63,9 +63,9 @@
      * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
      *
      */
-    public void enableAdvertising(boolean enable) {
+    public void enableAdvertising(boolean enable, int timeout) {
         try {
-            gatt.enableAdverisingSet(this.advertiserId, enable);
+            gatt.enableAdverisingSet(this.advertiserId, enable, timeout);
         } catch (RemoteException e) {
             Log.e(TAG, "remote exception - ", e);
         }
diff --git a/core/java/android/bluetooth/le/AdvertisingSetParameters.java b/core/java/android/bluetooth/le/AdvertisingSetParameters.java
index 453dd70..59fef8d 100644
--- a/core/java/android/bluetooth/le/AdvertisingSetParameters.java
+++ b/core/java/android/bluetooth/le/AdvertisingSetParameters.java
@@ -118,13 +118,11 @@
     private final boolean connectable;
     private final int interval;
     private final int txPowerLevel;
-    private final int timeoutMillis;
 
     private AdvertisingSetParameters(boolean connectable, boolean isLegacy,
                                      boolean isAnonymous, boolean includeTxPower,
                                      int primaryPhy, int secondaryPhy,
-                                     int interval, int txPowerLevel,
-                                     int timeoutMillis) {
+                                     int interval, int txPowerLevel) {
         this.connectable = connectable;
         this.isLegacy = isLegacy;
         this.isAnonymous = isAnonymous;
@@ -133,7 +131,6 @@
         this.secondaryPhy = secondaryPhy;
         this.interval = interval;
         this.txPowerLevel = txPowerLevel;
-        this.timeoutMillis = timeoutMillis;
     }
 
     private AdvertisingSetParameters(Parcel in) {
@@ -145,7 +142,6 @@
         secondaryPhy = in.readInt();
         interval = in.readInt();
         txPowerLevel = in.readInt();
-        timeoutMillis = in.readInt();
     }
 
     /**
@@ -188,11 +184,6 @@
      */
     public int getTxPowerLevel() { return txPowerLevel; }
 
-    /**
-     * Returns the advertising time limit in milliseconds.
-     */
-    public int getTimeout() { return timeoutMillis; }
-
     @Override
     public String toString() {
         return "AdvertisingSetParameters [connectable=" + connectable
@@ -202,8 +193,7 @@
              + ", primaryPhy=" + primaryPhy
              + ", secondaryPhy=" + secondaryPhy
              + ", interval=" + interval
-             + ", txPowerLevel=" + txPowerLevel
-             + ", timeoutMillis=" + timeoutMillis + "]";
+             + ", txPowerLevel=" + txPowerLevel + "]";
     }
 
     @Override
@@ -221,7 +211,6 @@
         dest.writeInt(secondaryPhy);
         dest.writeInt(interval);
         dest.writeInt(txPowerLevel);
-        dest.writeInt(timeoutMillis);
     }
 
     public static final Parcelable.Creator<AdvertisingSetParameters> CREATOR =
@@ -250,7 +239,6 @@
         private int secondaryPhy = PHY_LE_1M;
         private int interval = INTERVAL_LOW;
         private int txPowerLevel = TX_POWER_MEDIUM;
-        private int timeoutMillis = 0;
 
         /**
          * Set whether the advertisement type should be connectable or
@@ -380,30 +368,12 @@
         }
 
         /**
-         * Limit advertising to a given amount of time.
-         * @param timeoutMillis Advertising time limit. May not exceed 180000
-         * milliseconds. A value of 0 will disable the time limit.
-         * @throws IllegalArgumentException If the provided timeout is over 180000
-         * ms.
-         */
-        public Builder setTimeout(int timeoutMillis) {
-            if (timeoutMillis < 0 || timeoutMillis > LIMITED_ADVERTISING_MAX_MILLIS) {
-                throw new IllegalArgumentException("timeoutMillis invalid (must be 0-" +
-                                                   LIMITED_ADVERTISING_MAX_MILLIS +
-                                                   " milliseconds)");
-            }
-            this.timeoutMillis = timeoutMillis;
-            return this;
-        }
-
-        /**
          * Build the {@link AdvertisingSetParameters} object.
          */
         public AdvertisingSetParameters build() {
             return new AdvertisingSetParameters(connectable, isLegacy, isAnonymous,
                                                 includeTxPower, primaryPhy,
-                                                secondaryPhy, interval, txPowerLevel,
-                                                timeoutMillis);
+                                                secondaryPhy, interval, txPowerLevel);
         }
     }
 }
\ No newline at end of file
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index e03c947..67fd1c8 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -28,6 +28,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
@@ -60,11 +61,12 @@
     private final IBluetoothManager mBluetoothManager;
     private final Handler mHandler;
     private BluetoothAdapter mBluetoothAdapter;
-    private final Map<AdvertiseCallback, AdvertiseCallbackWrapper>
-            mLeAdvertisers = new HashMap<AdvertiseCallback, AdvertiseCallbackWrapper>();
+    private final Map<AdvertiseCallback, AdvertisingSetCallback>
+            mLegacyAdvertisers = new HashMap<>();
     private final Map<AdvertisingSetCallback, IAdvertisingSetCallback>
-            advertisingSetCallbackWrappers = new HashMap<>();
-    private final Map<Integer, AdvertisingSet> advertisingSets = new HashMap<>();
+            mCallbackWrappers = Collections.synchronizedMap(new HashMap<>());
+    private final Map<Integer, AdvertisingSet>
+            mAdvertisingSets = Collections.synchronizedMap(new HashMap<>());
 
     /**
      * Use BluetoothAdapter.getLeAdvertiser() instead.
@@ -109,7 +111,7 @@
     public void startAdvertising(AdvertiseSettings settings,
             AdvertiseData advertiseData, AdvertiseData scanResponse,
             final AdvertiseCallback callback) {
-        synchronized (mLeAdvertisers) {
+        synchronized (mLegacyAdvertisers) {
             BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
             if (callback == null) {
                 throw new IllegalArgumentException("callback cannot be null");
@@ -120,25 +122,64 @@
                 postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE);
                 return;
             }
-            if (mLeAdvertisers.containsKey(callback)) {
+            if (mLegacyAdvertisers.containsKey(callback)) {
                 postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED);
                 return;
             }
 
-            IBluetoothGatt gatt;
-            try {
-                gatt = mBluetoothManager.getBluetoothGatt();
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
-                postStartFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
-                return;
+            AdvertisingSetParameters.Builder parameters = new AdvertisingSetParameters.Builder();
+            parameters.setLegacyMode(true);
+            parameters.setConnectable(isConnectable);
+            if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_POWER) {
+                parameters.setInterval(1600); // 1s
+            } else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_BALANCED) {
+                parameters.setInterval(400); // 250ms
+            } else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY) {
+                parameters.setInterval(160); // 100ms
             }
-            AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData,
-                    scanResponse, settings, gatt);
-            wrapper.startRegisteration();
+
+            if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW) {
+                parameters.setTxPowerLevel(-21);
+            } else if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_LOW) {
+                parameters.setTxPowerLevel(-15);
+            } else if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM) {
+                parameters.setTxPowerLevel(-7);
+            } else if (settings.getTxPowerLevel() == AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) {
+                parameters.setTxPowerLevel(1);
+            }
+
+            AdvertisingSetCallback wrapped = wrapOldCallback(callback, settings);
+            mLegacyAdvertisers.put(callback, wrapped);
+            startAdvertisingSet(parameters.build(), advertiseData, scanResponse, null, null,
+                                settings.getTimeout(), wrapped);
         }
     }
 
+    AdvertisingSetCallback wrapOldCallback(AdvertiseCallback callback, AdvertiseSettings settings) {
+        return new AdvertisingSetCallback() {
+            public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int status) {
+                if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) {
+                    postStartFailure(callback, status);
+                    return;
+                }
+
+                postStartSuccess(callback, settings);
+            }
+
+            /* Legacy advertiser is disabled on timeout */
+            public void onAdvertisingEnabled(int advertiserId, boolean enabled, int status) {
+                if (enabled == true) {
+                    Log.e(TAG, "Legacy advertiser should be only disabled on timeout," +
+                        " but was enabled!");
+                    return;
+                }
+
+                stopAdvertising(callback);
+            }
+
+        };
+    }
+
     /**
      * Stop Bluetooth LE advertising. The {@code callback} must be the same one use in
      * {@link BluetoothLeAdvertiser#startAdvertising}.
@@ -148,20 +189,21 @@
      * @param callback {@link AdvertiseCallback} identifies the advertising instance to stop.
      */
     public void stopAdvertising(final AdvertiseCallback callback) {
-        synchronized (mLeAdvertisers) {
+        synchronized (mLegacyAdvertisers) {
             if (callback == null) {
                 throw new IllegalArgumentException("callback cannot be null");
             }
-            AdvertiseCallbackWrapper wrapper = mLeAdvertisers.get(callback);
+            AdvertisingSetCallback wrapper = mLegacyAdvertisers.get(callback);
             if (wrapper == null) return;
-            wrapper.stopAdvertising();
+
+            stopAdvertisingSet(wrapper);
         }
     }
 
     /**
     * Creates a new advertising set. If operation succeed, device will start advertising. This
     * method returns immediately, the operation status is delivered through
-    * {@code callback.onNewAdvertisingSet()}.
+    * {@code callback.onAdvertisingSetStarted()}.
     * <p>
     * @param parameters advertising set parameters.
     * @param advertiseData Advertisement data to be broadcasted.
@@ -173,14 +215,14 @@
                                     AdvertiseData advertiseData, AdvertiseData scanResponse,
                                     PeriodicAdvertisingParameters periodicParameters,
                                     AdvertiseData periodicData, AdvertisingSetCallback callback) {
-        startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
-                            periodicData, callback, new Handler(Looper.getMainLooper()));
+            startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
+                            periodicData, 0, callback, new Handler(Looper.getMainLooper()));
     }
 
     /**
     * Creates a new advertising set. If operation succeed, device will start advertising. This
     * method returns immediately, the operation status is delivered through
-    * {@code callback.onNewAdvertisingSet()}.
+    * {@code callback.onAdvertisingSetStarted()}.
     * <p>
     * @param parameters advertising set parameters.
     * @param advertiseData Advertisement data to be broadcasted.
@@ -194,6 +236,49 @@
                                     PeriodicAdvertisingParameters periodicParameters,
                                     AdvertiseData periodicData, AdvertisingSetCallback callback,
                                     Handler handler) {
+        startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
+                            periodicData, 0, callback, handler);
+    }
+
+    /**
+    * Creates a new advertising set. If operation succeed, device will start advertising. This
+    * method returns immediately, the operation status is delivered through
+    * {@code callback.onAdvertisingSetStarted()}.
+    * <p>
+    * @param parameters advertising set parameters.
+    * @param advertiseData Advertisement data to be broadcasted.
+    * @param scanResponse Scan response associated with the advertisement data.
+    * @param periodicData Periodic advertising data.
+    * @param timeoutMillis Advertising time limit. May not exceed 180000
+    * @param callback Callback for advertising set.
+    */
+    public void startAdvertisingSet(AdvertisingSetParameters parameters,
+                                    AdvertiseData advertiseData, AdvertiseData scanResponse,
+                                    PeriodicAdvertisingParameters periodicParameters,
+                                    AdvertiseData periodicData, int timeoutMillis,
+                                    AdvertisingSetCallback callback) {
+        startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
+                            periodicData, timeoutMillis, callback, new Handler(Looper.getMainLooper()));
+    }
+
+    /**
+    * Creates a new advertising set. If operation succeed, device will start advertising. This
+    * method returns immediately, the operation status is delivered through
+    * {@code callback.onAdvertisingSetStarted()}.
+    * <p>
+    * @param parameters advertising set parameters.
+    * @param advertiseData Advertisement data to be broadcasted.
+    * @param scanResponse Scan response associated with the advertisement data.
+    * @param periodicData Periodic advertising data.
+    * @param timeoutMillis Advertising time limit. May not exceed 180000
+    * @param callback Callback for advertising set.
+    * @param handler thread upon which the callbacks will be invoked.
+    */
+    public void startAdvertisingSet(AdvertisingSetParameters parameters,
+                                    AdvertiseData advertiseData, AdvertiseData scanResponse,
+                                    PeriodicAdvertisingParameters periodicParameters,
+                                    AdvertiseData periodicData, int timeoutMillis,
+                                    AdvertisingSetCallback callback, Handler handler) {
         BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
 
         if (callback == null) {
@@ -209,11 +294,14 @@
         }
 
         IAdvertisingSetCallback wrapped = wrap(callback, handler);
-        advertisingSetCallbackWrappers.put(callback, wrapped);
+        if (mCallbackWrappers.putIfAbsent(callback, wrapped) != null) {
+            throw new IllegalArgumentException(
+                "callback instance already associated with advertising");
+        }
 
         try {
             gatt.startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
-                                     periodicData, wrapped);
+                                     periodicData, timeoutMillis, wrapped);
         } catch (RemoteException e) {
           Log.e(TAG, "Failed to start advertising set - ", e);
           throw new IllegalStateException("Failed to start advertising set");
@@ -229,10 +317,9 @@
           throw new IllegalArgumentException("callback cannot be null");
         }
 
-        IAdvertisingSetCallback wrapped = advertisingSetCallbackWrappers.remove(callback);
+        IAdvertisingSetCallback wrapped = mCallbackWrappers.remove(callback);
         if (wrapped == null) {
-            throw new IllegalArgumentException(
-                "callback does not represent valid registered callback.");
+            return;
         }
 
         IBluetoothGatt gatt;
@@ -251,7 +338,9 @@
      * @hide
      */
     public void cleanup() {
-        mLeAdvertisers.clear();
+        mLegacyAdvertisers.clear();
+        mCallbackWrappers.clear();
+        mAdvertisingSets.clear();
     }
 
     // Compute the size of advertisement data or scan resp
@@ -317,13 +406,13 @@
                     public void run() {
                         if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) {
                             callback.onAdvertisingSetStarted(null, status);
-                            advertisingSetCallbackWrappers.remove(callback);
+                            mCallbackWrappers.remove(callback);
                             return;
                         }
 
                         AdvertisingSet advertisingSet =
                             new AdvertisingSet(advertiserId, mBluetoothManager);
-                        advertisingSets.put(advertiserId, advertisingSet);
+                        mAdvertisingSets.put(advertiserId, advertisingSet);
                         callback.onAdvertisingSetStarted(advertisingSet, status);
                     }
                 });
@@ -333,10 +422,10 @@
                 handler.post(new Runnable() {
                     @Override
                     public void run() {
-                        AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
                         callback.onAdvertisingSetStopped(advertisingSet);
-                        advertisingSets.remove(advertiserId);
-                        advertisingSetCallbackWrappers.remove(callback);
+                        mAdvertisingSets.remove(advertiserId);
+                        mCallbackWrappers.remove(callback);
                     }
                 });
             }
@@ -345,7 +434,7 @@
                 handler.post(new Runnable() {
                     @Override
                     public void run() {
-                        AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
                         callback.onAdvertisingEnabled(advertisingSet, enabled, status);
                     }
                 });
@@ -355,7 +444,7 @@
                 handler.post(new Runnable() {
                     @Override
                     public void run() {
-                        AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
                         callback.onAdvertisingDataSet(advertisingSet, status);
                     }
                 });
@@ -365,7 +454,7 @@
                 handler.post(new Runnable() {
                     @Override
                     public void run() {
-                        AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
                         callback.onScanResponseDataSet(advertisingSet, status);
                     }
                 });
@@ -375,7 +464,7 @@
                 handler.post(new Runnable() {
                     @Override
                     public void run() {
-                        AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
                         callback.onAdvertisingParametersUpdated(advertisingSet, status);
                     }
                 });
@@ -385,7 +474,7 @@
                 handler.post(new Runnable() {
                     @Override
                     public void run() {
-                        AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
                         callback.onPeriodicAdvertisingParametersUpdated(advertisingSet, status);
                     }
                 });
@@ -395,7 +484,7 @@
                 handler.post(new Runnable() {
                     @Override
                     public void run() {
-                        AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
                         callback.onPeriodicAdvertisingDataSet(advertisingSet, status);
                     }
                 });
@@ -405,7 +494,7 @@
                 handler.post(new Runnable() {
                     @Override
                     public void run() {
-                        AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+                        AdvertisingSet advertisingSet = mAdvertisingSets.get(advertiserId);
                         callback.onPeriodicAdvertisingEnable(advertisingSet, enable, status);
                     }
                 });
@@ -413,144 +502,6 @@
         };
     }
 
-    /**
-     * Bluetooth GATT interface callbacks for advertising.
-     */
-    private class AdvertiseCallbackWrapper extends IAdvertiserCallback.Stub {
-        private static final int LE_CALLBACK_TIMEOUT_MILLIS = 2000;
-        private final AdvertiseCallback mAdvertiseCallback;
-        private final AdvertiseData mAdvertisement;
-        private final AdvertiseData mScanResponse;
-        private final AdvertiseSettings mSettings;
-        private final IBluetoothGatt mBluetoothGatt;
-
-        // mAdvertiserId -1: not registered
-        // -2: advertise stopped or registration timeout
-        // >=0: registered and advertising started
-        private int mAdvertiserId;
-        private boolean mIsAdvertising = false;
-        private int registrationError = AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR;
-
-        public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback,
-                AdvertiseData advertiseData, AdvertiseData scanResponse,
-                AdvertiseSettings settings,
-                IBluetoothGatt bluetoothGatt) {
-            mAdvertiseCallback = advertiseCallback;
-            mAdvertisement = advertiseData;
-            mScanResponse = scanResponse;
-            mSettings = settings;
-            mBluetoothGatt = bluetoothGatt;
-            mAdvertiserId = -1;
-        }
-
-        public void startRegisteration() {
-            synchronized (this) {
-                if (mAdvertiserId == -2) return;
-
-                try {
-                    mBluetoothGatt.registerAdvertiser(this);
-                    wait(LE_CALLBACK_TIMEOUT_MILLIS);
-                } catch (InterruptedException | RemoteException e) {
-                    Log.e(TAG, "Failed to start registeration", e);
-                }
-                if (mAdvertiserId >= 0 && mIsAdvertising) {
-                    mLeAdvertisers.put(mAdvertiseCallback, this);
-                } else if (mAdvertiserId < 0) {
-
-                    // Registration timeout, reset mClientIf to -2 so no subsequent operations can
-                    // proceed.
-                    if (mAdvertiserId == -1) mAdvertiserId = -2;
-                    // Post internal error if registration failed.
-                    postStartFailure(mAdvertiseCallback, registrationError);
-                } else {
-                    // Unregister application if it's already registered but advertise failed.
-                    try {
-                        mBluetoothGatt.unregisterAdvertiser(mAdvertiserId);
-                        mAdvertiserId = -2;
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "remote exception when unregistering", e);
-                    }
-                }
-            }
-        }
-
-        public void stopAdvertising() {
-            synchronized (this) {
-                try {
-                    mBluetoothGatt.stopMultiAdvertising(mAdvertiserId);
-                    wait(LE_CALLBACK_TIMEOUT_MILLIS);
-                } catch (InterruptedException | RemoteException e) {
-                    Log.e(TAG, "Failed to stop advertising", e);
-                }
-                // Advertise callback should have been removed from LeAdvertisers when
-                // onMultiAdvertiseCallback was called. In case onMultiAdvertiseCallback is never
-                // invoked and wait timeout expires, remove callback here.
-                if (mLeAdvertisers.containsKey(mAdvertiseCallback)) {
-                    mLeAdvertisers.remove(mAdvertiseCallback);
-                }
-            }
-        }
-
-        /**
-         * Advertiser interface registered - app is ready to go
-         */
-        @Override
-        public void onAdvertiserRegistered(int status, int advertiserId) {
-            Log.d(TAG, "onAdvertiserRegistered() - status=" + status + " advertiserId=" + advertiserId);
-            synchronized (this) {
-                if (status == BluetoothGatt.GATT_SUCCESS) {
-                    try {
-                        if (mAdvertiserId == -2) {
-                            // Registration succeeds after timeout, unregister advertiser.
-                            mBluetoothGatt.unregisterAdvertiser(advertiserId);
-                        } else {
-                            mAdvertiserId = advertiserId;
-                            mBluetoothGatt.startMultiAdvertising(mAdvertiserId, mAdvertisement,
-                                    mScanResponse, mSettings);
-                        }
-                        return;
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "failed to start advertising", e);
-                    }
-                } else if (status == AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS) {
-                    registrationError = status;
-                }
-                // Registration failed.
-                mAdvertiserId = -2;
-                notifyAll();
-            }
-        }
-
-        @Override
-        public void onMultiAdvertiseCallback(int status, boolean isStart,
-                AdvertiseSettings settings) {
-            synchronized (this) {
-                if (isStart) {
-                    if (status == AdvertiseCallback.ADVERTISE_SUCCESS) {
-                        // Start success
-                        mIsAdvertising = true;
-                        postStartSuccess(mAdvertiseCallback, settings);
-                    } else {
-                        // Start failure.
-                        postStartFailure(mAdvertiseCallback, status);
-                    }
-                } else {
-                    // unregister advertiser for stop.
-                    try {
-                        mBluetoothGatt.unregisterAdvertiser(mAdvertiserId);
-                        mAdvertiserId = -2;
-                        mIsAdvertising = false;
-                        mLeAdvertisers.remove(mAdvertiseCallback);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "remote exception when unregistering", e);
-                    }
-                }
-                notifyAll();
-            }
-
-        }
-    }
-
     private void postStartFailure(final AdvertiseCallback callback, final int error) {
         mHandler.post(new Runnable() {
             @Override
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..bd31b03 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;
@@ -1103,18 +1104,7 @@
      * @hide
      */
     public static final String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED";
-    /**
-     * Activity action: Activate the current SIM card.  If SIM cards do not require activation,
-     * sending this intent is a no-op.
-     * <p>Input: No data should be specified.  get*Extra may have an optional
-     * {@link #EXTRA_SIM_ACTIVATION_RESPONSE} field containing a PendingIntent through which to
-     * send the activation result.
-     * <p>Output: nothing.
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_SIM_ACTIVATION_REQUEST =
-            "android.intent.action.SIM_ACTIVATION_REQUEST";
+
     /**
      * Activity Action: Main entry point for carrier setup apps.
      * <p>Carrier apps that provide an implementation for this action may be invoked to configure
@@ -1992,6 +1982,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/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index b4d77a0..0b3742f 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -31,7 +31,7 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Printer;
-import android.util.SparseIntArray;
+import android.util.SparseArray;
 
 import com.android.internal.util.ArrayUtils;
 
@@ -656,13 +656,15 @@
      *
      * The keys and values are all indices into the {@link #splitNames}, {@link #splitSourceDirs},
      * and {@link #splitPublicSourceDirs} arrays.
-     * Each key represents a split and its value is its parent split.
+     * Each key represents a split and its value is an array of splits. The first element of this
+     * array is the parent split, and the rest are configuration splits. These configuration splits
+     * have no dependencies themselves.
      * Cycles do not exist because they are illegal and screened for during installation.
      *
      * May be null if no splits are installed, or if no dependencies exist between them.
      * @hide
      */
-    public SparseIntArray splitDependencies;
+    public SparseArray<int[]> splitDependencies;
 
     /**
      * Full paths to the locations of extra resource packages (runtime overlays)
@@ -1182,7 +1184,7 @@
         dest.writeStringArray(splitNames);
         dest.writeStringArray(splitSourceDirs);
         dest.writeStringArray(splitPublicSourceDirs);
-        dest.writeSparseIntArray(splitDependencies);
+        dest.writeSparseArray((SparseArray) splitDependencies);
         dest.writeString(nativeLibraryDir);
         dest.writeString(secondaryNativeLibraryDir);
         dest.writeString(nativeLibraryRootDir);
@@ -1244,7 +1246,7 @@
         splitNames = source.readStringArray();
         splitSourceDirs = source.readStringArray();
         splitPublicSourceDirs = source.readStringArray();
-        splitDependencies = source.readSparseIntArray();
+        splitDependencies = source.readSparseArray(null);
         nativeLibraryDir = source.readString();
         secondaryNativeLibraryDir = source.readString();
         nativeLibraryRootDir = source.readString();
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/InstrumentationInfo.java b/core/java/android/content/pm/InstrumentationInfo.java
index 59c5307..f6f1be6 100644
--- a/core/java/android/content/pm/InstrumentationInfo.java
+++ b/core/java/android/content/pm/InstrumentationInfo.java
@@ -82,13 +82,15 @@
      *
      * The keys and values are all indices into the {@link #splitNames}, {@link #splitSourceDirs},
      * and {@link #splitPublicSourceDirs} arrays.
-     * Each key represents a split and its value is its parent split.
+     * Each key represents a split and its value is an array of splits. The first element of this
+     * array is the parent split, and the rest are configuration splits. These configuration splits
+     * have no dependencies themselves.
      * Cycles do not exist because they are illegal and screened for during installation.
      *
      * May be null if no splits are installed, or if no dependencies exist between them.
      * @hide
      */
-    public SparseIntArray splitDependencies;
+    public SparseArray<int[]> splitDependencies;
 
     /**
      * Full path to a directory assigned to the package for its persistent data.
@@ -155,7 +157,7 @@
         dest.writeStringArray(splitNames);
         dest.writeStringArray(splitSourceDirs);
         dest.writeStringArray(splitPublicSourceDirs);
-        dest.writeSparseIntArray(splitDependencies);
+        dest.writeSparseArray((SparseArray) splitDependencies);
         dest.writeString(dataDir);
         dest.writeString(deviceProtectedDataDir);
         dest.writeString(credentialProtectedDataDir);
@@ -185,7 +187,7 @@
         splitNames = source.readStringArray();
         splitSourceDirs = source.readStringArray();
         splitPublicSourceDirs = source.readStringArray();
-        splitDependencies = source.readSparseIntArray();
+        splitDependencies = source.readSparseArray(null);
         dataDir = source.readString();
         deviceProtectedDataDir = source.readString();
         credentialProtectedDataDir = source.readString();
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 3a875bc..10ffab2 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2355,6 +2355,13 @@
             = "android.hardware.vr.high_performance";
 
     /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device implements headtracking suitable for a VR device.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_VR_HEADTRACKING = "android.hardware.vr.headtracking";
+
+    /**
      * Action to external storage service to clean out removed apps.
      * @hide
      */
@@ -5286,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..99aa1bc 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -76,7 +76,7 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
-import android.util.SparseIntArray;
+import android.util.SparseArray;
 import android.util.TypedValue;
 import android.util.apk.ApkSignatureSchemeV2Verifier;
 import android.util.jar.StrictJarFile;
@@ -109,7 +109,6 @@
 import java.security.spec.X509EncodedKeySpec;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.BitSet;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Iterator;
@@ -211,6 +210,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;
@@ -360,8 +367,12 @@
         /** Names of any split APKs, ordered by parsed splitName */
         public final String[] splitNames;
 
+        /** Names of any split APKs that are features. Ordered by splitName */
+        public final boolean[] isFeatureSplits;
+
         /** Dependencies of any split APKs, ordered by parsed splitName */
         public final String[] usesSplitNames;
+        public final String[] configForSplit;
 
         /**
          * Path where this package was found on disk. For monolithic packages
@@ -388,14 +399,16 @@
         public final boolean isolatedSplits;
 
         public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
-                String[] usesSplitNames, String[] splitCodePaths,
-                int[] splitRevisionCodes) {
+                boolean[] isFeatureSplits, String[] usesSplitNames, String[] configForSplit,
+                String[] splitCodePaths, int[] splitRevisionCodes) {
             this.packageName = baseApk.packageName;
             this.versionCode = baseApk.versionCode;
             this.installLocation = baseApk.installLocation;
             this.verifiers = baseApk.verifiers;
             this.splitNames = splitNames;
+            this.isFeatureSplits = isFeatureSplits;
             this.usesSplitNames = usesSplitNames;
+            this.configForSplit = configForSplit;
             this.codePath = codePath;
             this.baseCodePath = baseApk.codePath;
             this.splitCodePaths = splitCodePaths;
@@ -426,6 +439,8 @@
         public final String codePath;
         public final String packageName;
         public final String splitName;
+        public boolean isFeatureSplit;
+        public final String configForSplit;
         public final String usesSplitName;
         public final int versionCode;
         public final int revisionCode;
@@ -440,14 +455,17 @@
         public final boolean extractNativeLibs;
         public final boolean isolatedSplits;
 
-        public ApkLite(String codePath, String packageName, String splitName, String usesSplitName,
-                int versionCode, int revisionCode, int installLocation, List<VerifierInfo> verifiers,
-                Signature[] signatures, Certificate[][] certificates, boolean coreApp,
-                boolean debuggable, boolean multiArch, boolean use32bitAbi,
-                boolean extractNativeLibs, boolean isolatedSplits) {
+        public ApkLite(String codePath, String packageName, String splitName, boolean isFeatureSplit,
+                String configForSplit, String usesSplitName, int versionCode, int revisionCode,
+                int installLocation, List<VerifierInfo> verifiers, Signature[] signatures,
+                Certificate[][] certificates, boolean coreApp, boolean debuggable,
+                boolean multiArch, boolean use32bitAbi, boolean extractNativeLibs,
+                boolean isolatedSplits) {
             this.codePath = codePath;
             this.packageName = packageName;
             this.splitName = splitName;
+            this.isFeatureSplit = isFeatureSplit;
+            this.configForSplit = configForSplit;
             this.usesSplitName = usesSplitName;
             this.versionCode = versionCode;
             this.revisionCode = revisionCode;
@@ -603,6 +621,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) {
@@ -802,10 +821,10 @@
         final ApkLite baseApk = parseApkLite(packageFile, flags);
         final String packagePath = packageFile.getAbsolutePath();
         Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-        return new PackageLite(packagePath, baseApk, null, null, null, null);
+        return new PackageLite(packagePath, baseApk, null, null, null, null, null, null);
     }
 
-    private static PackageLite parseClusterPackageLite(File packageDir, int flags)
+    static PackageLite parseClusterPackageLite(File packageDir, int flags)
             throws PackageParserException {
         final File[] files = packageDir.listFiles();
         if (ArrayUtils.isEmpty(files)) {
@@ -860,12 +879,16 @@
         final int size = apks.size();
 
         String[] splitNames = null;
+        boolean[] isFeatureSplits = null;
         String[] usesSplitNames = null;
+        String[] configForSplits = null;
         String[] splitCodePaths = null;
         int[] splitRevisionCodes = null;
         if (size > 0) {
             splitNames = new String[size];
+            isFeatureSplits = new boolean[size];
             usesSplitNames = new String[size];
+            configForSplits = new String[size];
             splitCodePaths = new String[size];
             splitRevisionCodes = new int[size];
 
@@ -875,14 +898,16 @@
             for (int i = 0; i < size; i++) {
                 final ApkLite apk = apks.get(splitNames[i]);
                 usesSplitNames[i] = apk.usesSplitName;
+                isFeatureSplits[i] = apk.isFeatureSplit;
+                configForSplits[i] = apk.configForSplit;
                 splitCodePaths[i] = apk.codePath;
                 splitRevisionCodes[i] = apk.revisionCode;
             }
         }
 
         final String codePath = packageDir.getAbsolutePath();
-        return new PackageLite(codePath, baseApk, splitNames, usesSplitNames, splitCodePaths,
-                splitRevisionCodes);
+        return new PackageLite(codePath, baseApk, splitNames, isFeatureSplits, usesSplitNames,
+                configForSplits, splitCodePaths, splitRevisionCodes);
     }
 
     /**
@@ -1060,42 +1085,6 @@
         }
     }
 
-    private static SparseIntArray buildSplitDependencyTree(PackageLite pkg)
-            throws PackageParserException {
-        SparseIntArray splitDependencies = new SparseIntArray();
-        for (int splitIdx = 0; splitIdx < pkg.splitNames.length; splitIdx++) {
-            final String splitDependency = pkg.usesSplitNames[splitIdx];
-            if (splitDependency != null) {
-                final int depIdx = Arrays.binarySearch(pkg.splitNames, splitDependency);
-                if (depIdx < 0) {
-                    throw new PackageParserException(
-                            PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
-                            "Split '" + pkg.splitNames[splitIdx] + "' requires split '"
-                                    + splitDependency + "', which is missing.");
-                }
-                splitDependencies.put(splitIdx, depIdx);
-            }
-        }
-
-        // Verify that there are no cycles.
-        final BitSet bitset = new BitSet();
-        for (int i = 0; i < splitDependencies.size(); i++) {
-            int splitIdx = splitDependencies.keyAt(i);
-
-            bitset.clear();
-            while (splitIdx != -1) {
-                if (bitset.get(splitIdx)) {
-                    throw new PackageParserException(
-                            PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
-                            "Cycle detected in split dependencies.");
-                }
-                bitset.set(splitIdx);
-                splitIdx = splitDependencies.get(splitIdx, -1);
-            }
-        }
-        return splitDependencies.size() != 0 ? splitDependencies : null;
-    }
-
     /**
      * Parse all APKs contained in the given directory, treating them as a
      * single package. This also performs sanity checking, such as requiring
@@ -1113,11 +1102,15 @@
         }
 
         // Build the split dependency tree.
-        SparseIntArray splitDependencies = null;
+        SparseArray<int[]> splitDependencies = null;
         final SplitAssetLoader assetLoader;
         if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
-            splitDependencies = buildSplitDependencyTree(lite);
-            assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
+            try {
+                splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
+                assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
+            } catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
+            }
         } else {
             assetLoader = new DefaultSplitAssetLoader(lite, flags);
         }
@@ -1753,6 +1746,8 @@
         boolean use32bitAbi = false;
         boolean extractNativeLibs = true;
         boolean isolatedSplits = false;
+        boolean isFeatureSplit = false;
+        String configForSplit = null;
         String usesSplitName = null;
 
         for (int i = 0; i < attrs.getAttributeCount(); i++) {
@@ -1768,6 +1763,10 @@
                 coreApp = attrs.getAttributeBooleanValue(i, false);
             } else if (attr.equals("isolatedSplits")) {
                 isolatedSplits = attrs.getAttributeBooleanValue(i, false);
+            } else if (attr.equals("configForSplit")) {
+                configForSplit = attrs.getAttributeValue(i);
+            } else if (attr.equals("isFeatureSplit")) {
+                isFeatureSplit = attrs.getAttributeBooleanValue(i, false);
             }
         }
 
@@ -1822,10 +1821,10 @@
             }
         }
 
-        return new ApkLite(codePath, packageSplit.first, packageSplit.second, usesSplitName,
-                versionCode, revisionCode, installLocation, verifiers, signatures,
-                certificates, coreApp, debuggable, multiArch, use32bitAbi, extractNativeLibs,
-                isolatedSplits);
+        return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
+                configForSplit, usesSplitName, versionCode, revisionCode, installLocation,
+                verifiers, signatures, certificates, coreApp, debuggable, multiArch, use32bitAbi,
+                extractNativeLibs, isolatedSplits);
     }
 
     /**
@@ -2097,6 +2096,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 +2106,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)) {
@@ -4196,6 +4201,7 @@
                 sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToInstantApps, false);
         if (visibleToEphemeral) {
             a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+            owner.visibleToInstantApps = true;
         }
 
         sa.recycle();
@@ -4240,6 +4246,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*/,
@@ -4689,6 +4707,7 @@
                 sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToInstantApps, false);
         if (visibleToEphemeral) {
             p.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+            owner.visibleToInstantApps = true;
         }
 
         sa.recycle();
@@ -5005,6 +5024,7 @@
                 sa.getBoolean(R.styleable.AndroidManifestService_visibleToInstantApps, false);
         if (visibleToEphemeral) {
             s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+            owner.visibleToInstantApps = true;
         }
 
         sa.recycle();
@@ -5580,6 +5600,7 @@
         public String mRequiredAccountType;
 
         public String mOverlayTarget;
+        public boolean mIsStaticOverlay;
         public boolean mTrustedOverlay;
 
         /**
@@ -5607,6 +5628,11 @@
 
         public byte[] restrictUpdateHash;
 
+        /**
+         * Set if the app or any of its components are visible to Instant Apps.
+         */
+        public boolean visibleToInstantApps;
+
         public Package(String packageName) {
             this.packageName = packageName;
             this.manifestPackageName = packageName;
@@ -6056,6 +6082,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 +6198,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/pm/split/SplitAssetDependencyLoader.java b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
index 4df90eb..16023f0 100644
--- a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
+++ b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
@@ -18,10 +18,11 @@
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
 
+import android.annotation.NonNull;
 import android.content.pm.PackageParser;
 import android.content.res.AssetManager;
 import android.os.Build;
-import android.util.SparseIntArray;
+import android.util.SparseArray;
 
 import libcore.io.IoUtils;
 
@@ -34,49 +35,31 @@
  * @hide
  */
 public class SplitAssetDependencyLoader
-        extends SplitDependencyLoaderHelper<PackageParser.PackageParserException>
+        extends SplitDependencyLoader<PackageParser.PackageParserException>
         implements SplitAssetLoader {
-    private static final int BASE_ASSET_PATH_IDX = -1;
-    private final String mBasePath;
-    private final String[] mSplitNames;
     private final String[] mSplitPaths;
     private final int mFlags;
 
-    private String[] mCachedBasePaths;
-    private AssetManager mCachedBaseAssetManager;
-
-    private String[][] mCachedSplitPaths;
+    private String[][] mCachedPaths;
     private AssetManager[] mCachedAssetManagers;
 
-    public SplitAssetDependencyLoader(PackageParser.PackageLite pkg, SparseIntArray dependencies,
-            int flags) {
+    public SplitAssetDependencyLoader(PackageParser.PackageLite pkg,
+            SparseArray<int[]> dependencies, int flags) {
         super(dependencies);
-        mBasePath = pkg.baseCodePath;
-        mSplitNames = pkg.splitNames;
-        mSplitPaths = pkg.splitCodePaths;
+
+        // The base is inserted into index 0, so we need to shift all the splits by 1.
+        mSplitPaths = new String[pkg.splitCodePaths.length + 1];
+        mSplitPaths[0] = pkg.baseCodePath;
+        System.arraycopy(pkg.splitCodePaths, 0, mSplitPaths, 1, pkg.splitCodePaths.length);
+
         mFlags = flags;
-        mCachedBasePaths = null;
-        mCachedBaseAssetManager = null;
-        mCachedSplitPaths = new String[mSplitNames.length][];
-        mCachedAssetManagers = new AssetManager[mSplitNames.length];
+        mCachedPaths = new String[mSplitPaths.length][];
+        mCachedAssetManagers = new AssetManager[mSplitPaths.length];
     }
 
     @Override
     protected boolean isSplitCached(int splitIdx) {
-        if (splitIdx != -1) {
-            return mCachedAssetManagers[splitIdx] != null;
-        }
-        return mCachedBaseAssetManager != null;
-    }
-
-    // Adds all non-code configuration splits for this split name. The split name is expected
-    // to represent a feature split.
-    private void addAllConfigSplits(String splitName, ArrayList<String> outAssetPaths) {
-        for (int i = 0; i < mSplitNames.length; i++) {
-            if (isConfigurationSplitOf(mSplitNames[i], splitName)) {
-                outAssetPaths.add(mSplitPaths[i]);
-            }
-        }
+        return mCachedAssetManagers[splitIdx] != null;
     }
 
     private static AssetManager createAssetManagerWithPaths(String[] assetPaths, int flags)
@@ -107,45 +90,38 @@
     }
 
     @Override
-    protected void constructSplit(int splitIdx, int parentSplitIdx) throws
-            PackageParser.PackageParserException {
+    protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
+            int parentSplitIdx) throws PackageParser.PackageParserException {
         final ArrayList<String> assetPaths = new ArrayList<>();
-        if (splitIdx == BASE_ASSET_PATH_IDX) {
-            assetPaths.add(mBasePath);
-            addAllConfigSplits(null, assetPaths);
-            mCachedBasePaths = assetPaths.toArray(new String[assetPaths.size()]);
-            mCachedBaseAssetManager = createAssetManagerWithPaths(mCachedBasePaths, mFlags);
-            return;
-        }
-
-        if (parentSplitIdx == BASE_ASSET_PATH_IDX) {
-            Collections.addAll(assetPaths, mCachedBasePaths);
-        } else {
-            Collections.addAll(assetPaths, mCachedSplitPaths[parentSplitIdx]);
+        if (parentSplitIdx >= 0) {
+            Collections.addAll(assetPaths, mCachedPaths[parentSplitIdx]);
         }
 
         assetPaths.add(mSplitPaths[splitIdx]);
-        addAllConfigSplits(mSplitNames[splitIdx], assetPaths);
-        mCachedSplitPaths[splitIdx] = assetPaths.toArray(new String[assetPaths.size()]);
-        mCachedAssetManagers[splitIdx] = createAssetManagerWithPaths(mCachedSplitPaths[splitIdx],
+        for (int configSplitIdx : configSplitIndices) {
+            assetPaths.add(mSplitPaths[configSplitIdx]);
+        }
+        mCachedPaths[splitIdx] = assetPaths.toArray(new String[assetPaths.size()]);
+        mCachedAssetManagers[splitIdx] = createAssetManagerWithPaths(mCachedPaths[splitIdx],
                 mFlags);
     }
 
     @Override
     public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException {
-        loadDependenciesForSplit(BASE_ASSET_PATH_IDX);
-        return mCachedBaseAssetManager;
+        loadDependenciesForSplit(0);
+        return mCachedAssetManagers[0];
     }
 
     @Override
     public AssetManager getSplitAssetManager(int idx) throws PackageParser.PackageParserException {
-        loadDependenciesForSplit(idx);
-        return mCachedAssetManagers[idx];
+        // Since we insert the base at position 0, and PackageParser keeps splits separate from
+        // the base, we need to adjust the index.
+        loadDependenciesForSplit(idx + 1);
+        return mCachedAssetManagers[idx + 1];
     }
 
     @Override
     public void close() throws Exception {
-        IoUtils.closeQuietly(mCachedBaseAssetManager);
         for (AssetManager assets : mCachedAssetManagers) {
             IoUtils.closeQuietly(assets);
         }
diff --git a/core/java/android/content/pm/split/SplitDependencyLoader.java b/core/java/android/content/pm/split/SplitDependencyLoader.java
new file mode 100644
index 0000000..3586546
--- /dev/null
+++ b/core/java/android/content/pm/split/SplitDependencyLoader.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm.split;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.content.pm.PackageParser;
+import android.util.IntArray;
+import android.util.SparseArray;
+
+import libcore.util.EmptyArray;
+
+import java.util.Arrays;
+import java.util.BitSet;
+
+/**
+ * A helper class that implements the dependency tree traversal for splits. Callbacks
+ * are implemented by subclasses to notify whether a split has already been constructed
+ * and is cached, and to actually create the split requested.
+ *
+ * This helper is meant to be subclassed so as to reduce the number of allocations
+ * needed to make use of it.
+ *
+ * All inputs and outputs are assumed to be indices into an array of splits.
+ *
+ * @hide
+ */
+public abstract class SplitDependencyLoader<E extends Exception> {
+    private final @NonNull SparseArray<int[]> mDependencies;
+
+    /**
+     * Construct a new SplitDependencyLoader. Meant to be called from the
+     * subclass constructor.
+     * @param dependencies The dependency tree of splits.
+     */
+    protected SplitDependencyLoader(@NonNull SparseArray<int[]> dependencies) {
+        mDependencies = dependencies;
+    }
+
+    /**
+     * Traverses the dependency tree and constructs any splits that are not already
+     * cached. This routine short-circuits and skips the creation of splits closer to the
+     * root if they are cached, as reported by the subclass implementation of
+     * {@link #isSplitCached(int)}. The construction of splits is delegated to the subclass
+     * implementation of {@link #constructSplit(int, int[], int)}.
+     * @param splitIdx The index of the split to load. 0 represents the base Application.
+     */
+    protected void loadDependenciesForSplit(@IntRange(from = 0) int splitIdx) throws E {
+        // Quick check before any allocations are done.
+        if (isSplitCached(splitIdx)) {
+            return;
+        }
+
+        // Special case the base, since it has no dependencies.
+        if (splitIdx == 0) {
+            final int[] configSplitIndices = collectConfigSplitIndices(0);
+            constructSplit(0, configSplitIndices, -1);
+            return;
+        }
+
+        // Build up the dependency hierarchy.
+        final IntArray linearDependencies = new IntArray();
+        linearDependencies.add(splitIdx);
+
+        // Collect all the dependencies that need to be constructed.
+        // They will be listed from leaf to root.
+        while (true) {
+            // Only follow the first index into the array. The others are config splits and
+            // get loaded with the split.
+            final int[] deps = mDependencies.get(splitIdx);
+            if (deps != null && deps.length > 0) {
+                splitIdx = deps[0];
+            } else {
+                splitIdx = -1;
+            }
+
+            if (splitIdx < 0 || isSplitCached(splitIdx)) {
+                break;
+            }
+
+            linearDependencies.add(splitIdx);
+        }
+
+        // Visit each index, from right to left (root to leaf).
+        int parentIdx = splitIdx;
+        for (int i = linearDependencies.size() - 1; i >= 0; i--) {
+            final int idx = linearDependencies.get(i);
+            final int[] configSplitIndices = collectConfigSplitIndices(idx);
+            constructSplit(idx, configSplitIndices, parentIdx);
+            parentIdx = idx;
+        }
+    }
+
+    private @NonNull int[] collectConfigSplitIndices(int splitIdx) {
+        // The config splits appear after the first element.
+        final int[] deps = mDependencies.get(splitIdx);
+        if (deps == null || deps.length <= 1) {
+            return EmptyArray.INT;
+        }
+        return Arrays.copyOfRange(deps, 1, deps.length);
+    }
+
+    /**
+     * Subclass to report whether the split at `splitIdx` is cached and need not be constructed.
+     * It is assumed that if `splitIdx` is cached, any parent of `splitIdx` is also cached.
+     * @param splitIdx The index of the split to check for in the cache.
+     * @return true if the split is cached and does not need to be constructed.
+     */
+    protected abstract boolean isSplitCached(@IntRange(from = 0) int splitIdx);
+
+    /**
+     * Subclass to construct a split at index `splitIdx` with parent split `parentSplitIdx`.
+     * The result is expected to be cached by the subclass in its own structures.
+     * @param splitIdx The index of the split to construct. 0 represents the base Application.
+     * @param configSplitIndices The array of configuration splits to load along with this split.
+     *                           May be empty (length == 0) but never null.
+     * @param parentSplitIdx The index of the parent split. -1 if there is no parent.
+     * @throws E Subclass defined exception representing failure to construct a split.
+     */
+    protected abstract void constructSplit(@IntRange(from = 0) int splitIdx,
+            @NonNull @IntRange(from = 1) int[] configSplitIndices,
+            @IntRange(from = -1) int parentSplitIdx) throws E;
+
+    public static class IllegalDependencyException extends Exception {
+        private IllegalDependencyException(String message) {
+            super(message);
+        }
+    }
+
+    private static int[] append(int[] src, int elem) {
+        if (src == null) {
+            return new int[] { elem };
+        }
+        int[] dst = Arrays.copyOf(src, src.length + 1);
+        dst[src.length] = elem;
+        return dst;
+    }
+
+    public static @NonNull SparseArray<int[]> createDependenciesFromPackage(
+            PackageParser.PackageLite pkg) throws IllegalDependencyException {
+        // The data structure that holds the dependencies. In PackageParser, splits are stored
+        // in their own array, separate from the base. We treat all paths as equals, so
+        // we need to insert the base as index 0, and shift all other splits.
+        final SparseArray<int[]> splitDependencies = new SparseArray<>();
+
+        // The base depends on nothing.
+        splitDependencies.put(0, new int[] {-1});
+
+        // First write out the <uses-split> dependencies. These must appear first in the
+        // array of ints, as is convention in this class.
+        for (int splitIdx = 0; splitIdx < pkg.splitNames.length; splitIdx++) {
+            if (!pkg.isFeatureSplits[splitIdx]) {
+                // Non-feature splits don't have dependencies.
+                continue;
+            }
+
+            // Implicit dependency on the base.
+            final int targetIdx;
+            final String splitDependency = pkg.usesSplitNames[splitIdx];
+            if (splitDependency != null) {
+                final int depIdx = Arrays.binarySearch(pkg.splitNames, splitDependency);
+                if (depIdx < 0) {
+                    throw new IllegalDependencyException("Split '" + pkg.splitNames[splitIdx]
+                            + "' requires split '" + splitDependency + "', which is missing.");
+                }
+                targetIdx = depIdx + 1;
+            } else {
+                // Implicitly depend on the base.
+                targetIdx = 0;
+            }
+            splitDependencies.put(splitIdx + 1, new int[] {targetIdx});
+        }
+
+        // Write out the configForSplit reverse-dependencies. These appear after the <uses-split>
+        // dependencies and are considered leaves.
+        //
+        // At this point, all splits in splitDependencies have the first element in their array set.
+        for (int splitIdx = 0; splitIdx < pkg.splitNames.length; splitIdx++) {
+            if (pkg.isFeatureSplits[splitIdx]) {
+                // Feature splits are not configForSplits.
+                continue;
+            }
+
+            // Implicit feature for the base.
+            final int targetSplitIdx;
+            final String configForSplit = pkg.configForSplit[splitIdx];
+            if (configForSplit != null) {
+                final int depIdx = Arrays.binarySearch(pkg.splitNames, configForSplit);
+                if (depIdx < 0) {
+                    throw new IllegalDependencyException("Split '" + pkg.splitNames[splitIdx]
+                            + "' targets split '" + configForSplit + "', which is missing.");
+                }
+
+                if (!pkg.isFeatureSplits[depIdx]) {
+                    throw new IllegalDependencyException("Split '" + pkg.splitNames[splitIdx]
+                            + "' declares itself as configuration split for a non-feature split '"
+                            + pkg.splitNames[depIdx] + "'");
+                }
+                targetSplitIdx = depIdx + 1;
+            } else {
+                targetSplitIdx = 0;
+            }
+            splitDependencies.put(targetSplitIdx,
+                    append(splitDependencies.get(targetSplitIdx), splitIdx + 1));
+        }
+
+        // Verify that there are no cycles.
+        final BitSet bitset = new BitSet();
+        for (int i = 0, size = splitDependencies.size(); i < size; i++) {
+            int splitIdx = splitDependencies.keyAt(i);
+
+            bitset.clear();
+            while (splitIdx != -1) {
+                // Check if this split has been visited yet.
+                if (bitset.get(splitIdx)) {
+                    throw new IllegalDependencyException("Cycle detected in split dependencies.");
+                }
+
+                // Mark the split so that if we visit it again, we no there is a cycle.
+                bitset.set(splitIdx);
+
+                // Follow the first dependency only, the others are leaves by definition.
+                final int[] deps = splitDependencies.get(splitIdx);
+                splitIdx = deps != null ? deps[0] : -1;
+            }
+        }
+        return splitDependencies;
+    }
+}
diff --git a/core/java/android/content/pm/split/SplitDependencyLoaderHelper.java b/core/java/android/content/pm/split/SplitDependencyLoaderHelper.java
deleted file mode 100644
index b493480..0000000
--- a/core/java/android/content/pm/split/SplitDependencyLoaderHelper.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.content.pm.split;
-
-import android.annotation.Nullable;
-import android.util.IntArray;
-import android.util.SparseIntArray;
-
-/**
- * A helper class that implements the dependency tree traversal for splits. Callbacks
- * are implemented by subclasses to notify whether a split has already been constructed
- * and is cached, and to actually create the split requested.
- *
- * This helper is meant to be subclassed so as to reduce the number of allocations
- * needed to make use of it.
- *
- * All inputs and outputs are assumed to be indices into an array of splits.
- *
- * @hide
- */
-public abstract class SplitDependencyLoaderHelper<E extends Exception> {
-    @Nullable private final SparseIntArray mDependencies;
-
-    /**
-     * Construct a new SplitDependencyLoaderHelper. Meant to be called from the
-     * subclass constructor.
-     * @param dependencies The dependency tree of splits. Can be null, which leads to
-     *                     just the implicit dependency of all splits on the base.
-     */
-    protected SplitDependencyLoaderHelper(@Nullable SparseIntArray dependencies) {
-        mDependencies = dependencies;
-    }
-
-    /**
-     * Traverses the dependency tree and constructs any splits that are not already
-     * cached. This routine short-circuits and skips the creation of splits closer to the
-     * root if they are cached, as reported by the subclass implementation of
-     * {@link #isSplitCached(int)}. The construction of splits is delegated to the subclass
-     * implementation of {@link #constructSplit(int, int)}.
-     * @param splitIdx The index of the split to load. Can be -1, which represents the
-     *                 base Application.
-     */
-    protected void loadDependenciesForSplit(int splitIdx) throws E {
-        // Quick check before any allocations are done.
-        if (isSplitCached(splitIdx)) {
-            return;
-        }
-
-        final IntArray linearDependencies = new IntArray();
-        linearDependencies.add(splitIdx);
-
-        // Collect all the dependencies that need to be constructed.
-        // They will be listed from leaf to root.
-        while (splitIdx >= 0) {
-            splitIdx = mDependencies != null ? mDependencies.get(splitIdx, -1) : -1;
-            if (isSplitCached(splitIdx)) {
-                break;
-            }
-            linearDependencies.add(splitIdx);
-        }
-
-        // Visit each index, from right to left (root to leaf).
-        int parentIdx = splitIdx;
-        for (int i = linearDependencies.size() - 1; i >= 0; i--) {
-            final int idx = linearDependencies.get(i);
-            constructSplit(idx, parentIdx);
-            parentIdx = idx;
-        }
-    }
-
-    /**
-     * Subclass to report whether the split at `splitIdx` is cached and need not be constructed.
-     * It is assumed that if `splitIdx` is cached, any parent of `splitIdx` is also cached.
-     * @param splitIdx The index of the split to check for in the cache.
-     * @return true if the split is cached and does not need to be constructed.
-     */
-    protected abstract boolean isSplitCached(int splitIdx);
-
-    /**
-     * Subclass to construct a split at index `splitIdx` with parent split `parentSplitIdx`.
-     * The result is expected to be cached by the subclass in its own structures.
-     * @param splitIdx The index of the split to construct. Can be -1, which represents the
-     *                 base Application.
-     * @param parentSplitIdx The index of the parent split. Can be -1, which represents the
-     *                       base Application.
-     * @throws E
-     */
-    protected abstract void constructSplit(int splitIdx, int parentSplitIdx) throws E;
-
-    /**
-     * Returns true if `splitName` represents a Configuration split of `featureSplitName`.
-     *
-     * A Configuration split's name is prefixed with the associated Feature split's name
-     * or the empty string if the split is for the base Application APK. It is then followed by the
-     * dollar sign character "$" and some unique string that should represent the configurations
-     * the split contains.
-     *
-     * Example:
-     * <table>
-     *     <tr>
-     *         <th>Feature split name</th>
-     *         <th>Configuration split name: xhdpi</th>
-     *         <th>Configuration split name: fr-rFR</th>
-     *     </tr>
-     *     <tr>
-     *         <td>(base APK)</td>
-     *         <td><code>$xhdpi</code></td>
-     *         <td><code>$fr-rFR</code></td>
-     *     </tr>
-     *     <tr>
-     *         <td><code>Extras</code></td>
-     *         <td><code>Extras$xhdpi</code></td>
-     *         <td><code>Extras$fr-rFR</code></td>
-     *     </tr>
-     * </table>
-     *
-     * @param splitName The name of the split to check.
-     * @param featureSplitName The name of the Feature split. May be null or "" if checking
-     *                         the base Application APK.
-     * @return true if the splitName represents a Configuration split of featureSplitName.
-     */
-    protected static boolean isConfigurationSplitOf(String splitName, String featureSplitName) {
-        if (featureSplitName == null || featureSplitName.length() == 0) {
-            // We are looking for configuration splits of the base, which have some legacy support.
-            if (splitName.startsWith("config_")) {
-                return true;
-            } else if (splitName.startsWith("$")) {
-                return true;
-            } else {
-                return false;
-            }
-        } else {
-            return splitName.startsWith(featureSplitName + "$");
-        }
-    }
-}
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/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 8bc65af..e3b97e8 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -100,6 +100,8 @@
     private final CameraCharacteristics mCharacteristics;
     private final int mTotalPartialCount;
 
+    private static final long NANO_PER_SECOND = 1000000000; //ns
+
     /**
      * A list tracking request and its expected last regular frame number and last reprocess frame
      * number. Updated when calling ICameraDeviceUser methods.
@@ -1239,6 +1241,14 @@
         private final List<CaptureRequest> mRequestList;
         private final Handler mHandler;
         private final int mSessionId;
+        /**
+         * <p>Determine if the callback holder is for a constrained high speed request list that
+         * expects batched capture results. Capture results will be batched if the request list
+         * is interleaved with preview and video requests. Capture results won't be batched if the
+         * request list only contains preview requests, or if the request doesn't belong to a
+         * constrained high speed list.
+         */
+        private final boolean mHasBatchedOutputs;
 
         CaptureCallbackHolder(CaptureCallback callback, List<CaptureRequest> requestList,
                 Handler handler, boolean repeating, int sessionId) {
@@ -1251,6 +1261,25 @@
             mRequestList = new ArrayList<CaptureRequest>(requestList);
             mCallback = callback;
             mSessionId = sessionId;
+
+            // Check whether this callback holder is for batched outputs.
+            // The logic here should match createHighSpeedRequestList.
+            boolean hasBatchedOutputs = true;
+            for (int i = 0; i < requestList.size(); i++) {
+                CaptureRequest request = requestList.get(i);
+                if (!request.isPartOfCRequestList()) {
+                    hasBatchedOutputs = false;
+                    break;
+                }
+                if (i == 0) {
+                    Collection<Surface> targets = request.getTargets();
+                    if (targets.size() != 2) {
+                        hasBatchedOutputs = false;
+                        break;
+                    }
+                }
+            }
+            mHasBatchedOutputs = hasBatchedOutputs;
         }
 
         public boolean isRepeating() {
@@ -1288,6 +1317,14 @@
         public int getSessionId() {
             return mSessionId;
         }
+
+        public int getRequestCount() {
+            return mRequestList.size();
+        }
+
+        public boolean hasBatchedOutputs() {
+            return mHasBatchedOutputs;
+        }
     }
 
     /**
@@ -1777,10 +1814,27 @@
                         @Override
                         public void run() {
                             if (!CameraDeviceImpl.this.isClosed()) {
-                                holder.getCallback().onCaptureStarted(
-                                    CameraDeviceImpl.this,
-                                    holder.getRequest(resultExtras.getSubsequenceId()),
-                                    timestamp, frameNumber);
+                                final int subsequenceId = resultExtras.getSubsequenceId();
+                                final CaptureRequest request = holder.getRequest(subsequenceId);
+
+                                if (holder.hasBatchedOutputs()) {
+                                    // Send derived onCaptureStarted for requests within the batch
+                                    final Range<Integer> fpsRange =
+                                        request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
+                                    for (int i = 0; i < holder.getRequestCount(); i++) {
+                                        holder.getCallback().onCaptureStarted(
+                                            CameraDeviceImpl.this,
+                                            holder.getRequest(i),
+                                            timestamp - (subsequenceId - i) *
+                                            NANO_PER_SECOND/fpsRange.getUpper(),
+                                            frameNumber - (subsequenceId - i));
+                                    }
+                                } else {
+                                    holder.getCallback().onCaptureStarted(
+                                        CameraDeviceImpl.this,
+                                        holder.getRequest(resultExtras.getSubsequenceId()),
+                                        timestamp, frameNumber);
+                                }
                             }
                         }
                     });
@@ -1845,46 +1899,91 @@
                 Runnable resultDispatch = null;
 
                 CaptureResult finalResult;
+                // Make a copy of the native metadata before it gets moved to a CaptureResult
+                // object.
+                final CameraMetadataNative resultCopy;
+                if (holder.hasBatchedOutputs()) {
+                    resultCopy = new CameraMetadataNative(result);
+                } else {
+                    resultCopy = null;
+                }
 
                 // Either send a partial result or the final capture completed result
                 if (isPartialResult) {
                     final CaptureResult resultAsCapture =
                             new CaptureResult(result, request, resultExtras);
-
                     // Partial result
                     resultDispatch = new Runnable() {
                         @Override
                         public void run() {
-                            if (!CameraDeviceImpl.this.isClosed()){
-                                holder.getCallback().onCaptureProgressed(
-                                    CameraDeviceImpl.this,
-                                    request,
-                                    resultAsCapture);
+                            if (!CameraDeviceImpl.this.isClosed()) {
+                                if (holder.hasBatchedOutputs()) {
+                                    // Send derived onCaptureProgressed for requests within
+                                    // the batch.
+                                    for (int i = 0; i < holder.getRequestCount(); i++) {
+                                        CameraMetadataNative resultLocal =
+                                                new CameraMetadataNative(resultCopy);
+                                        CaptureResult resultInBatch = new CaptureResult(
+                                                resultLocal, holder.getRequest(i), resultExtras);
+
+                                        holder.getCallback().onCaptureProgressed(
+                                            CameraDeviceImpl.this,
+                                            holder.getRequest(i),
+                                            resultInBatch);
+                                    }
+                                } else {
+                                    holder.getCallback().onCaptureProgressed(
+                                        CameraDeviceImpl.this,
+                                        request,
+                                        resultAsCapture);
+                                }
                             }
                         }
                     };
-
                     finalResult = resultAsCapture;
                 } else {
                     List<CaptureResult> partialResults =
                             mFrameNumberTracker.popPartialResults(frameNumber);
 
+                    final long sensorTimestamp =
+                            result.get(CaptureResult.SENSOR_TIMESTAMP);
+                    final Range<Integer> fpsRange =
+                            request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
+                    final int subsequenceId = resultExtras.getSubsequenceId();
                     final TotalCaptureResult resultAsCapture = new TotalCaptureResult(result,
                             request, resultExtras, partialResults, holder.getSessionId());
-
                     // Final capture result
                     resultDispatch = new Runnable() {
                         @Override
                         public void run() {
                             if (!CameraDeviceImpl.this.isClosed()){
-                                holder.getCallback().onCaptureCompleted(
-                                    CameraDeviceImpl.this,
-                                    request,
-                                    resultAsCapture);
+                                if (holder.hasBatchedOutputs()) {
+                                    // Send derived onCaptureCompleted for requests within
+                                    // the batch.
+                                    for (int i = 0; i < holder.getRequestCount(); i++) {
+                                        resultCopy.set(CaptureResult.SENSOR_TIMESTAMP,
+                                                sensorTimestamp - (subsequenceId - i) *
+                                                NANO_PER_SECOND/fpsRange.getUpper());
+                                        CameraMetadataNative resultLocal =
+                                                new CameraMetadataNative(resultCopy);
+                                        TotalCaptureResult resultInBatch = new TotalCaptureResult(
+                                            resultLocal, holder.getRequest(i), resultExtras,
+                                            partialResults, holder.getSessionId());
+
+                                        holder.getCallback().onCaptureCompleted(
+                                            CameraDeviceImpl.this,
+                                            holder.getRequest(i),
+                                            resultInBatch);
+                                    }
+                                } else {
+                                    holder.getCallback().onCaptureCompleted(
+                                        CameraDeviceImpl.this,
+                                        request,
+                                        resultAsCapture);
+                                }
                             }
                         }
                     };
-
                     finalResult = resultAsCapture;
                 }
 
diff --git a/core/java/android/hardware/radio/RadioModule.java b/core/java/android/hardware/radio/RadioModule.java
index fc7d0d2..8964893 100644
--- a/core/java/android/hardware/radio/RadioModule.java
+++ b/core/java/android/hardware/radio/RadioModule.java
@@ -16,6 +16,8 @@
 
 package android.hardware.radio;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
@@ -23,6 +25,7 @@
 import android.os.Looper;
 import android.os.Message;
 import java.lang.ref.WeakReference;
+import java.util.List;
 import java.util.UUID;
 
 /**
@@ -76,6 +79,8 @@
 
     public native int getProgramInformation(RadioManager.ProgramInfo[] info);
 
+    public native @NonNull List<RadioManager.ProgramInfo> getProgramList(@Nullable String filter);
+
     public native boolean isAntennaConnected();
 
     public native boolean hasControl();
diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java
index 5c82555..c8034eb 100644
--- a/core/java/android/hardware/radio/RadioTuner.java
+++ b/core/java/android/hardware/radio/RadioTuner.java
@@ -16,6 +16,8 @@
 
 package android.hardware.radio;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
@@ -23,6 +25,7 @@
 import android.os.Looper;
 import android.os.Message;
 import java.lang.ref.WeakReference;
+import java.util.List;
 import java.util.UUID;
 
 /**
@@ -209,6 +212,20 @@
     public abstract int getProgramInformation(RadioManager.ProgramInfo[] info);
 
     /**
+     * Get the list of discovered radio stations.
+     *
+     * To get the full list, set filter to null or empty string. Otherwise, client application
+     * must verify vendor product/name before setting this parameter to anything else.
+     *
+     * @param filter vendor-specific selector for radio stations.
+     * @return a list of radio stations.
+     * @throws IllegalStateException if the scan is in progress or has not been started.
+     * @throws IllegalArgumentException if the filter argument is not valid.
+     * @hide FutureFeature
+     */
+    public abstract @NonNull List<RadioManager.ProgramInfo> getProgramList(@Nullable String filter);
+
+    /**
      * Get current antenna connection state for current configuration.
      * Only valid if a configuration has been applied.
      * @return {@code true} if the antenna is connected, {@code false} otherwise.
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/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/Binder.java b/core/java/android/os/Binder.java
index 0136979..7906707 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -66,7 +66,8 @@
      * of classes can potentially create leaks.
      */
     private static final boolean FIND_POTENTIAL_LEAKS = false;
-    private static final boolean CHECK_PARCEL_SIZE = false;
+    /** @hide */
+    public static final boolean CHECK_PARCEL_SIZE = false;
     static final String TAG = "Binder";
 
     /** @hide */
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/StrictMode.java b/core/java/android/os/StrictMode.java
index caea202..f503b3a 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -2638,7 +2638,7 @@
             dest.writeString(broadcastIntentAction);
             dest.writeStringArray(tags);
             int total = dest.dataPosition()-start;
-            if (total > 10*1024) {
+            if (Binder.CHECK_PARCEL_SIZE && total > 10*1024) {
                 Slog.d(TAG, "VIO: policy=" + policy + " dur=" + durationMillis
                         + " numLoop=" + violationNumThisLoop
                         + " anim=" + numAnimationsRunning
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/UserManager.java b/core/java/android/os/UserManager.java
index a638cd4..f6712f8 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.app.Activity;
@@ -763,6 +764,16 @@
     public static final int PIN_VERIFICATION_SUCCESS = -1;
 
     /**
+     * Sent when user restrictions have changed.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi // To allow seeing it from CTS.
+    public static final String ACTION_USER_RESTRICTIONS_CHANGED =
+            "android.os.action.USER_RESTRICTIONS_CHANGED";
+
+    /**
      * Error result indicating that this user is not allowed to add other users on this device.
      * This is a result code returned from the activity created by the intent
      * {@link #createUserCreationIntent(String, String, String, PersistableBundle)}.
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/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 8a2a14c..7b84f689 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5605,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
          */
@@ -5616,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
          */
@@ -6618,6 +6630,8 @@
          * This value is only used for managed profiles.
          * @hide
          */
+        @TestApi
+        @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
         public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
 
         /** @hide */
@@ -6950,6 +6964,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,
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/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 70e0461..f55c7cf 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -149,13 +149,13 @@
     // Notification cancellation reasons
 
     /** Notification was canceled by the status bar reporting a notification click. */
-    public static final int REASON_DELEGATE_CLICK = 1;
+    public static final int REASON_CLICK = 1;
     /** Notification was canceled by the status bar reporting a user dismissal. */
-    public static final int REASON_DELEGATE_CANCEL = 2;
+    public static final int REASON_CANCEL = 2;
     /** Notification was canceled by the status bar reporting a user dismiss all. */
-    public static final int REASON_DELEGATE_CANCEL_ALL = 3;
+    public static final int REASON_CANCEL_ALL = 3;
     /** Notification was canceled by the status bar reporting an inflation error. */
-    public static final int REASON_DELEGATE_ERROR = 4;
+    public static final int REASON_ERROR = 4;
     /** Notification was canceled by the package manager modifying the package. */
     public static final int REASON_PACKAGE_CHANGED = 5;
     /** Notification was canceled by the owning user context being stopped. */
diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl
index 10e4177..6034c18 100644
--- a/core/java/android/service/vr/IVrManager.aidl
+++ b/core/java/android/service/vr/IVrManager.aidl
@@ -50,5 +50,13 @@
      * @param enabled true if the device should be placed in persistent VR mode.
      */
     void setPersistentVrModeEnabled(in boolean enabled);
+
+    /**
+     * Return current virtual display id.
+     *
+     * @return {@link android.view.Display.INVALID_DISPLAY} if there is no virtual display
+     * currently, else return the display id of the virtual display
+     */
+    int getCompatibilityDisplayId();
 }
 
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 483a49b..6bbb0ff 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -18,11 +18,11 @@
 
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.util.MergedConfiguration;
 import android.view.WindowInsets;
 
 import com.android.internal.R;
 import com.android.internal.os.HandlerCaller;
-import com.android.internal.util.ScreenShapeHelper;
 import com.android.internal.view.BaseIWindow;
 import com.android.internal.view.BaseSurfaceHolder;
 
@@ -32,7 +32,6 @@
 import android.app.WallpaperManager;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Configuration;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
@@ -55,7 +54,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
-import android.view.WindowManager.LayoutParams;
 import android.view.WindowManagerGlobal;
 
 import java.io.FileDescriptor;
@@ -169,7 +167,7 @@
         final Rect mFinalSystemInsets = new Rect();
         final Rect mFinalStableInsets = new Rect();
         final Rect mBackdropFrame = new Rect();
-        final Configuration mConfiguration = new Configuration();
+        final MergedConfiguration mMergedConfiguration = new MergedConfiguration();
 
         final WindowManager.LayoutParams mLayout
                 = new WindowManager.LayoutParams();
@@ -288,7 +286,7 @@
             @Override
             public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
                     Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
-                    Configuration newConfig, Rect backDropRect, boolean forceLayout,
+                    MergedConfiguration mergedConfiguration, Rect backDropRect, boolean forceLayout,
                     boolean alwaysConsumeNavBar, int displayId) {
                 Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED,
                         reportDraw ? 1 : 0, outsets);
@@ -568,7 +566,8 @@
                     out.print(mVisibleInsets.toShortString());
                     out.print(" mWinFrame="); out.print(mWinFrame.toShortString());
                     out.print(" mContentInsets="); out.println(mContentInsets.toShortString());
-            out.print(prefix); out.print("mConfiguration="); out.println(mConfiguration);
+            out.print(prefix); out.print("mConfiguration=");
+                    out.println(mMergedConfiguration.getMergedConfiguration());
             out.print(prefix); out.print("mLayout="); out.println(mLayout);
             synchronized (mLock) {
                 out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset);
@@ -695,7 +694,7 @@
                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                             View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets,
                             mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
-                            mConfiguration, mSurfaceHolder.mSurface);
+                            mMergedConfiguration, mSurfaceHolder.mSurface);
 
                     if (DEBUG) Log.v(TAG, "New surface: " + mSurfaceHolder.mSurface
                             + ", frame=" + mWinFrame);
diff --git a/core/java/android/text/Emoji.java b/core/java/android/text/Emoji.java
index 83810b0..a07caf4 100644
--- a/core/java/android/text/Emoji.java
+++ b/core/java/android/text/Emoji.java
@@ -132,12 +132,11 @@
         0x1F910, 0x1F911, 0x1F912, 0x1F913, 0x1F914, 0x1F915, 0x1F916, 0x1F917, 0x1F918, 0x1F919,
         0x1F91A, 0x1F91B, 0x1F91C, 0x1F91D, 0x1F91E, 0x1F920, 0x1F921, 0x1F922, 0x1F923, 0x1F924,
         0x1F925, 0x1F926, 0x1F927, 0x1F930, 0x1F933, 0x1F934, 0x1F935, 0x1F936, 0x1F937, 0x1F938,
-        0x1F939, 0x1F93A, 0x1F93B, 0x1F93C, 0x1F93D, 0x1F93E, 0x1F940, 0x1F941, 0x1F942, 0x1F943,
-        0x1F944, 0x1F945, 0x1F946, 0x1F947, 0x1F948, 0x1F949, 0x1F94A, 0x1F94B, 0x1F950, 0x1F951,
-        0x1F952, 0x1F953, 0x1F954, 0x1F955, 0x1F956, 0x1F957, 0x1F958, 0x1F959, 0x1F95A, 0x1F95B,
-        0x1F95C, 0x1F95D, 0x1F95E, 0x1F980, 0x1F981, 0x1F982, 0x1F983, 0x1F984, 0x1F985, 0x1F986,
-        0x1F987, 0x1F988, 0x1F989, 0x1F98A, 0x1F98B, 0x1F98C, 0x1F98D, 0x1F98E, 0x1F98F, 0x1F990,
-        0x1F991, 0x1F9C0
+        0x1F939, 0x1F93A, 0x1F93C, 0x1F93D, 0x1F93E, 0x1F940, 0x1F941, 0x1F942, 0x1F943, 0x1F944,
+        0x1F945, 0x1F947, 0x1F948, 0x1F949, 0x1F94A, 0x1F94B, 0x1F950, 0x1F951, 0x1F952, 0x1F953,
+        0x1F954, 0x1F955, 0x1F956, 0x1F957, 0x1F958, 0x1F959, 0x1F95A, 0x1F95B, 0x1F95C, 0x1F95D,
+        0x1F95E, 0x1F980, 0x1F981, 0x1F982, 0x1F983, 0x1F984, 0x1F985, 0x1F986, 0x1F987, 0x1F988,
+        0x1F989, 0x1F98A, 0x1F98B, 0x1F98C, 0x1F98D, 0x1F98E, 0x1F98F, 0x1F990, 0x1F991, 0x1F9C0
     };
 
     // See http://www.unicode.org/Public/emoji/3.0/emoji-data.txt
@@ -150,7 +149,7 @@
         0x1F596, 0x1F645, 0x1F646, 0x1F647, 0x1F64B, 0x1F64C, 0x1F64D, 0x1F64E, 0x1F64F, 0x1F6A3,
         0x1F6B4, 0x1F6B5, 0x1F6B6, 0x1F6C0, 0x1F918, 0x1F919, 0x1F91A, 0x1F91B, 0x1F91C, 0x1F91D,
         0x1F91E, 0x1F926, 0x1F930, 0x1F933, 0x1F934, 0x1F935, 0x1F936, 0x1F937, 0x1F938, 0x1F939,
-        0x1F93B, 0x1F93C, 0x1F93D, 0x1F93E
+        0x1F93C, 0x1F93D, 0x1F93E
     };
 
     // See http://www.unicode.org/emoji/charts/emoji-zwj-sequences.html
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/util/LauncherIcons.java b/core/java/android/util/LauncherIcons.java
new file mode 100644
index 0000000..e5aa2b5
--- /dev/null
+++ b/core/java/android/util/LauncherIcons.java
@@ -0,0 +1,113 @@
+/*
+ * 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.util;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PaintFlagsDrawFilter;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+
+/**
+ * Utility class to handle icon treatments (e.g., shadow generation) for the Launcher icons.
+ * @hide
+ */
+public final class LauncherIcons {
+
+    private final Paint mPaint = new Paint();
+    private final Canvas mCanvas = new Canvas();
+
+    private static final int KEY_SHADOW_ALPHA = 61;
+    private static final int AMBIENT_SHADOW_ALPHA = 30;
+    private static final float BLUR_FACTOR = 0.5f / 48;
+    private int mShadowInset;
+    private Bitmap mShadowBitmap;
+    private int mIconSize;
+    private Resources mRes;
+
+    public LauncherIcons(Context context) {
+        mRes = context.getResources();
+        DisplayMetrics metrics = mRes.getDisplayMetrics();
+        mShadowInset = (int) metrics.density / DisplayMetrics.DENSITY_DEFAULT;
+        mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
+            Paint.FILTER_BITMAP_FLAG));
+        mIconSize = (int) mRes.getDimensionPixelSize(android.R.dimen.app_icon_size);
+    }
+
+    /**
+     * Draw the drawable into a bitmap.
+     */
+    public Bitmap createIconBitmap(Drawable icon) {
+        final Bitmap bitmap = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888);
+        mPaint.setAlpha(255);
+        mCanvas.setBitmap(bitmap);
+        int iconInset = 0;
+        if (mShadowBitmap != null) {
+            mCanvas.drawBitmap(mShadowBitmap, 0, 0, mPaint);
+            iconInset = mShadowInset;
+        }
+
+        icon.setBounds(iconInset, iconInset, mIconSize - iconInset,
+            mIconSize - iconInset);
+        icon.draw(mCanvas);
+        mCanvas.setBitmap(null);
+        return bitmap;
+    }
+
+    public Drawable wrapIconDrawableWithShadow(Drawable drawable) {
+        if (!(drawable instanceof AdaptiveIconDrawable)) {
+            return drawable;
+        }
+        AdaptiveIconDrawable d =
+            (AdaptiveIconDrawable) drawable.getConstantState().newDrawable().mutate();
+        getShadowBitmap(d);
+        Bitmap iconbitmap = createIconBitmap(d);
+        return new BitmapDrawable(mRes, iconbitmap);
+    }
+
+    private Bitmap getShadowBitmap(AdaptiveIconDrawable d) {
+        if (mShadowBitmap != null) {
+            return mShadowBitmap;
+        }
+
+        int shadowSize = mIconSize - mShadowInset;
+        mShadowBitmap = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ALPHA_8);
+        mCanvas.setBitmap(mShadowBitmap);
+
+        // Draw key shadow
+        mPaint.setColor(Color.TRANSPARENT);
+        float blur = BLUR_FACTOR * mIconSize;
+        mPaint.setShadowLayer(blur, 0, mShadowInset, KEY_SHADOW_ALPHA << 24);
+        d.setBounds(mShadowInset, mShadowInset, mIconSize - mShadowInset, mIconSize - mShadowInset);
+        mCanvas.drawPath(d.getIconMask(), mPaint);
+
+        // Draw ambient shadow
+        mPaint.setShadowLayer(blur, 0, 0, AMBIENT_SHADOW_ALPHA << 24);
+        d.setBounds(mShadowInset, 2 * mShadowInset, mIconSize - mShadowInset, mIconSize);
+        mCanvas.drawPath(d.getIconMask(), mPaint);
+        mPaint.clearShadowLayer();
+
+        return mShadowBitmap;
+    }
+}
diff --git a/core/java/android/util/MergedConfiguration.aidl b/core/java/android/util/MergedConfiguration.aidl
new file mode 100644
index 0000000..c24dbbe
--- /dev/null
+++ b/core/java/android/util/MergedConfiguration.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.util;
+
+parcelable MergedConfiguration;
\ No newline at end of file
diff --git a/core/java/android/util/MergedConfiguration.java b/core/java/android/util/MergedConfiguration.java
new file mode 100644
index 0000000..d94af8a
--- /dev/null
+++ b/core/java/android/util/MergedConfiguration.java
@@ -0,0 +1,122 @@
+/*
+ * 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.util;
+
+import android.annotation.NonNull;
+import android.content.res.Configuration;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Container that holds global and override config and their merge product.
+ * Merged configuration updates automatically whenever global or override configs are updated via
+ * setters.
+ *
+ * {@hide}
+ */
+public class MergedConfiguration implements Parcelable {
+
+    private Configuration mGlobalConfig = new Configuration();
+    private Configuration mOverrideConfig = new Configuration();
+    private Configuration mMergedConfig = new Configuration();
+
+    public MergedConfiguration() {
+    }
+
+    public MergedConfiguration(Configuration globalConfig, Configuration overrideConfig) {
+        setConfiguration(globalConfig, overrideConfig);
+    }
+
+    public MergedConfiguration(MergedConfiguration mergedConfiguration) {
+        setConfiguration(mergedConfiguration.getGlobalConfiguration(),
+                mergedConfiguration.getOverrideConfiguration());
+    }
+
+    private MergedConfiguration(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(mGlobalConfig, flags);
+        dest.writeParcelable(mOverrideConfig, flags);
+        dest.writeParcelable(mMergedConfig, flags);
+    }
+
+    public void readFromParcel(Parcel source) {
+        mGlobalConfig = source.readParcelable(Configuration.class.getClassLoader());
+        mOverrideConfig = source.readParcelable(Configuration.class.getClassLoader());
+        mMergedConfig = source.readParcelable(Configuration.class.getClassLoader());
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<MergedConfiguration> CREATOR = new Creator<MergedConfiguration>() {
+        @Override
+        public MergedConfiguration createFromParcel(Parcel in) {
+            return new MergedConfiguration(in);
+        }
+
+        @Override
+        public MergedConfiguration[] newArray(int size) {
+            return new MergedConfiguration[size];
+        }
+    };
+
+    /**
+     * Update global and override configurations.
+     * Merged configuration will automatically be updated.
+     * @param globalConfig New global configuration.
+     * @param overrideConfig New override configuration.
+     */
+    public void setConfiguration(Configuration globalConfig, Configuration overrideConfig) {
+        mGlobalConfig.setTo(globalConfig);
+        mOverrideConfig.setTo(overrideConfig);
+        updateMergedConfig();
+    }
+
+    /**
+     * @return Stored global configuration value.
+     */
+    @NonNull
+    public Configuration getGlobalConfiguration() {
+        return mGlobalConfig;
+    }
+
+    /**
+     * @return Stored override configuration value.
+     */
+    public Configuration getOverrideConfiguration() {
+        return mOverrideConfig;
+    }
+
+    /**
+     * @return Stored merged configuration value.
+     */
+    public Configuration getMergedConfiguration() {
+        return mMergedConfig;
+    }
+
+    /** Update merged config when global or override config changes. */
+    private void updateMergedConfig() {
+        mMergedConfig.setTo(mGlobalConfig);
+        mMergedConfig.updateFrom(mOverrideConfig);
+    }
+}
diff --git a/core/java/android/view/AccessibilityIterators.java b/core/java/android/view/AccessibilityIterators.java
index e59937d..ca54bef 100644
--- a/core/java/android/view/AccessibilityIterators.java
+++ b/core/java/android/view/AccessibilityIterators.java
@@ -16,7 +16,6 @@
 
 package android.view;
 
-import android.content.ComponentCallbacks;
 import android.content.res.Configuration;
 
 import java.text.BreakIterator;
@@ -65,7 +64,7 @@
     }
 
     static class CharacterTextSegmentIterator extends AbstractTextSegmentIterator
-            implements ComponentCallbacks {
+            implements ViewRootImpl.ConfigChangedCallback {
         private static CharacterTextSegmentIterator sInstance;
 
         private Locale mLocale;
@@ -144,19 +143,14 @@
         }
 
         @Override
-        public void onConfigurationChanged(Configuration newConfig) {
-            Locale locale = newConfig.locale;
+        public void onConfigurationChanged(Configuration globalConfig) {
+            final Locale locale = globalConfig.getLocales().get(0);
             if (!mLocale.equals(locale)) {
                 mLocale = locale;
                 onLocaleChanged(locale);
             }
         }
 
-        @Override
-        public void onLowMemory() {
-            /* ignore */
-        }
-
         protected void onLocaleChanged(Locale locale) {
             mImpl = BreakIterator.getCharacterInstance(locale);
         }
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 14b2abe..611cc63 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -17,7 +17,6 @@
 
 package android.view;
 
-import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
@@ -26,6 +25,7 @@
 import android.view.MotionEvent;
 
 import com.android.internal.os.IResultReceiver;
+import android.util.MergedConfiguration;
 
 /**
  * API back to a client window that the Window Manager uses to inform it of
@@ -49,8 +49,8 @@
 
     void resized(in Rect frame, in Rect overscanInsets, in Rect contentInsets,
             in Rect visibleInsets, in Rect stableInsets, in Rect outsets, boolean reportDraw,
-            in Configuration newConfig, in Rect backDropFrame, boolean forceLayout,
-            boolean alwaysConsumeNavBar, int displayId);
+            in MergedConfiguration newMergedConfiguration, in Rect backDropFrame,
+            boolean forceLayout, boolean alwaysConsumeNavBar, int displayId);
     void moved(int newX, int newY);
     void dispatchAppVisibility(boolean visible);
     void dispatchGetNewSurface();
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index 1facc10..51d6514 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -11,17 +11,17 @@
 ** 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 
+** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
 
 package android.view;
 
 import android.content.ClipData;
-import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Bundle;
+import android.util.MergedConfiguration;
 import android.view.InputChannel;
 import android.view.IWindow;
 import android.view.IWindowId;
@@ -83,9 +83,9 @@
      * treat as real display. Example of such area is a chin in some models of wearable devices.
      * @param outBackdropFrame Rect which is used draw the resizing background during a resize
      * operation.
-     * @param outConfiguration New configuration of window, if it is now
-     * becoming visible and the global configuration has changed since it
-     * was last displayed.
+     * @param outMergedConfiguration New config container that holds global, override and merged
+     * config for window, if it is now becoming visible and the merged configuration has changed
+     * since it was last displayed.
      * @param outSurface Object in which is placed the new display surface.
      *
      * @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_SHOW_FOCUS},
@@ -95,33 +95,8 @@
             int requestedWidth, int requestedHeight, int viewVisibility,
             int flags, out Rect outFrame, out Rect outOverscanInsets,
             out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
-            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);
+            out Rect outOutsets, out Rect outBackdropFrame,
+            out MergedConfiguration outMergedConfiguration, out Surface outSurface);
 
     /*
      * Notify the window manager that an application is relaunching and
@@ -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 6d320ef..824e035 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -16,8 +16,9 @@
 
 package android.view;
 
-import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_SUBLAYER;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
 import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_OVERLAY_SUBLAYER;
+import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_SUBLAYER;
 import static android.view.WindowManagerPolicy.APPLICATION_PANEL_SUBLAYER;
 
 import android.content.Context;
@@ -28,6 +29,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
 import android.os.SystemClock;
@@ -777,6 +779,31 @@
     }
 
     /**
+     * This method still exists only for compatibility reasons because some applications have relied
+     * on this method via reflection. See Issue 36345857 for details.
+     *
+     * @deprecated No platform code is using this method anymore.
+     * @hide
+     */
+    @Deprecated
+    public void setWindowType(int type) {
+        if (getContext().getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.N_MR1) {
+            throw new UnsupportedOperationException(
+                    "SurfaceView#setWindowType() has never been a public API.");
+        }
+
+        if (type == TYPE_APPLICATION_PANEL) {
+            Log.e(TAG, "If you are calling SurfaceView#setWindowType(TYPE_APPLICATION_PANEL) "
+                    + "just to make the SurfaceView to be placed on top of its window, you must "
+                    + "call setZOrderOnTop(true) instead.", new Throwable());
+            setZOrderOnTop(true);
+            return;
+        }
+        Log.e(TAG, "SurfaceView#setWindowType(int) is deprecated and now does nothing. "
+                + "type=" + type, new Throwable());
+    }
+
+    /**
      * Check to see if the surface has fixed size dimensions or if the surface's
      * dimensions are dimensions are dependent on its current layout.
      *
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 029caf9..583dad4 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({
@@ -2768,13 +2749,8 @@
      *                 x                 * NO LONGER NEEDED, SHOULD BE REUSED *
      *                1                  PFLAG3_FINGER_DOWN
      *               1                   PFLAG3_FOCUSED_BY_DEFAULT
-<<<<<<< HEAD
      *             11                    PFLAG3_AUTO_FILL_MODE_MASK
      *           11                      PFLAG3_IMPORTANT_FOR_AUTOFILL
-=======
-     *             11                    PFLAG3_AUTOFILL_MODE_MASK
-     *           xx                      * NO LONGER NEEDED, SHOULD BE REUSED *
->>>>>>> Replaced auto-fill by autofill to keep it consistent with API style.
      *          1                        PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE
      *         1                         PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED
      *        1                          PFLAG3_TEMPORARY_DETACH
@@ -5049,7 +5025,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:
@@ -7257,9 +7263,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>
@@ -7372,13 +7379,13 @@
      * <p>When implementing this method, subclasses must follow the rules below:
      *
      * <ol>
-     * <li>Also implement {@link #autofillVirtual(int, AutofillValue)} to autofill the virtual
+     * <li>Also implement {@link #autofill(int, AutofillValue)} to autofill the virtual
      * children.
      * <li>Call
-     * {@link android.view.autofill.AutofillManager#notifyVirtualViewEntered} and
-     * {@link android.view.autofill.AutofillManager#notifyVirtualViewExited(View, int)}
+     * {@link android.view.autofill.AutofillManager#notifyViewEntered} and
+     * {@link android.view.autofill.AutofillManager#notifyViewExited(View, int)}
      * when the focus inside the view changed.
-     * <li>Call {@link android.view.autofill.AutofillManager#notifyVirtualValueChanged(View, int,
+     * <li>Call {@link android.view.autofill.AutofillManager#notifyValueChanged(View, int,
      * AutofillValue)} when the value of a child changed.
      * <li>Call {@link AutofillManager#commit()} when the autofill context
      * of the view structure changed and you want the current autofill interaction if such
@@ -7433,8 +7440,11 @@
      * </pre>
      *
      * @param value value to be autofilled.
+     *
+     * @return {@code true} if the view was successfully autofilled, {@code false} otherwise
      */
-    public void autofill(@SuppressWarnings("unused") AutofillValue value) {
+    public boolean autofill(@SuppressWarnings("unused") AutofillValue value) {
+        return false;
     }
 
     /**
@@ -7445,14 +7455,17 @@
      *
      * @param value value to be autofilled.
      * @param virtualId id identifying the virtual child inside the custom view.
+     *
+     * @return {@code true} if the view was successfully autofilled, {@code false} otherwise
      */
-    public void autofillVirtual(@SuppressWarnings("unused") int virtualId,
+    public boolean autofill(@SuppressWarnings("unused") int virtualId,
             @SuppressWarnings("unused") AutofillValue value) {
+        return false;
     }
 
     /**
      * Describes the autofill type that should be used on calls to
-     * {@link #autofill(AutofillValue)} and {@link #autofillVirtual(int, AutofillValue)}.
+     * {@link #autofill(AutofillValue)} and {@link #autofill(int, AutofillValue)}.
      *
      * <p>By default returns {@link #AUTOFILL_TYPE_NONE}, but views should override it (and
      * {@link #autofill(AutofillValue)} to support the Autofill Framework.
@@ -7464,12 +7477,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;
     }
 
@@ -9099,11 +9112,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;
+        }
     }
 
     /**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 580888c..1681787 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import static android.view.Display.INVALID_DISPLAY;
 import static android.view.View.PFLAG_DRAW_ANIMATION;
 import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER;
 import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
@@ -28,10 +29,10 @@
 import android.animation.LayoutTransition;
 import android.annotation.NonNull;
 import android.app.ActivityManager;
+import android.app.ActivityThread;
 import android.app.ResourcesManager;
 import android.content.ClipData;
 import android.content.ClipDescription;
-import android.content.ComponentCallbacks;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.CompatibilityInfo;
@@ -67,6 +68,7 @@
 import android.util.AndroidRuntimeException;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.MergedConfiguration;
 import android.util.Slog;
 import android.util.TimeUtils;
 import android.util.TypedValue;
@@ -161,7 +163,55 @@
     static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList();
     static boolean sFirstDrawComplete = false;
 
-    static final ArrayList<ComponentCallbacks> sConfigCallbacks = new ArrayList();
+    /**
+     * Callback for notifying about global configuration changes.
+     */
+    public interface ConfigChangedCallback {
+
+        /** Notifies about global config change. */
+        void onConfigurationChanged(Configuration globalConfig);
+    }
+
+    private static final ArrayList<ConfigChangedCallback> sConfigCallbacks = new ArrayList<>();
+
+    /**
+     * Callback for notifying activities about override configuration changes.
+     */
+    public interface ActivityConfigCallback {
+
+        /**
+         * Notifies about override config change and/or move to different display.
+         * @param overrideConfig New override config to apply to activity.
+         * @param newDisplayId New display id, {@link Display#INVALID_DISPLAY} if not changed.
+         */
+        void onConfigurationChanged(Configuration overrideConfig, int newDisplayId);
+    }
+
+    /**
+     * Callback used to notify corresponding activity about override configuration change and make
+     * sure that all resources are set correctly before updating the ViewRootImpl's internal state.
+     */
+    private ActivityConfigCallback mActivityConfigCallback;
+
+    /**
+     * Used when configuration change first updates the config of corresponding activity.
+     * In that case we receive a call back from {@link ActivityThread} and this flag is used to
+     * preserve the initial value.
+     *
+     * @see #performConfigurationChange(Configuration, Configuration, boolean, int)
+     */
+    private boolean mForceNextConfigUpdate;
+
+    /**
+     * 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
@@ -333,8 +383,12 @@
 
     private WindowInsets mLastWindowInsets;
 
-    final Configuration mLastConfiguration = new Configuration();
-    final Configuration mPendingConfiguration = new Configuration();
+    /** Last applied configuration obtained from resources. */
+    private final Configuration mLastConfigurationFromResources = new Configuration();
+    /** Last configuration reported from WM or via {@link #MSG_UPDATE_CONFIGURATION}. */
+    private final MergedConfiguration mLastReportedMergedConfiguration = new MergedConfiguration();
+    /** Configurations waiting to be applied. */
+    private final MergedConfiguration mPendingMergedConfiguration = new MergedConfiguration();
 
     boolean mScrollMayChange;
     @SoftInputModeFlags
@@ -451,6 +505,13 @@
         mFallbackEventHandler = new PhoneFallbackEventHandler(context);
         mChoreographer = Choreographer.getInstance();
         mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
+
+        if (!sCompatibilityDone) {
+            sAlwaysAssignFocus = true;
+
+            sCompatibilityDone = true;
+        }
+
         loadSystemProperties();
     }
 
@@ -462,12 +523,18 @@
         }
     }
 
-    public static void addConfigCallback(ComponentCallbacks callback) {
+    /** Add static config callback to be notified about global config changes. */
+    public static void addConfigCallback(ConfigChangedCallback callback) {
         synchronized (sConfigCallbacks) {
             sConfigCallbacks.add(callback);
         }
     }
 
+    /** Add activity config callback to be notified about override config changes. */
+    public void setActivityConfigCallback(ActivityConfigCallback callback) {
+        mActivityConfigCallback = callback;
+    }
+
     public void addWindowCallbacks(WindowCallbacks callback) {
         if (USE_MT_RENDERER) {
             synchronized (mWindowCallbacks) {
@@ -1540,6 +1607,7 @@
             mFullRedrawNeeded = true;
             mLayoutRequested = true;
 
+            final Configuration config = mContext.getResources().getConfiguration();
             if (shouldUseDisplaySize(lp)) {
                 // NOTE -- system code, won't try to do compat mode.
                 Point size = new Point();
@@ -1547,7 +1615,6 @@
                 desiredWindowWidth = size.x;
                 desiredWindowHeight = size.y;
             } else {
-                Configuration config = mContext.getResources().getConfiguration();
                 desiredWindowWidth = dipToPx(config.screenWidthDp);
                 desiredWindowHeight = dipToPx(config.screenHeightDp);
             }
@@ -1559,11 +1626,11 @@
             mAttachInfo.mHasWindowFocus = false;
             mAttachInfo.mWindowVisibility = viewVisibility;
             mAttachInfo.mRecomputeGlobalAttributes = false;
-            mLastConfiguration.setTo(host.getResources().getConfiguration());
+            mLastConfigurationFromResources.setTo(config);
             mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
             // Set the layout direction if it has not been set before (inherit is the default)
             if (mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
-                host.setLayoutDirection(mLastConfiguration.getLayoutDirection());
+                host.setLayoutDirection(config.getLayoutDirection());
             }
             host.dispatchAttachedToWindow(mAttachInfo, 0);
             mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
@@ -1808,11 +1875,14 @@
                         + " outsets=" + mPendingOutsets.toShortString()
                         + " surface=" + mSurface);
 
-                if (mPendingConfiguration.seq != 0) {
+                final Configuration pendingMergedConfig =
+                        mPendingMergedConfiguration.getMergedConfiguration();
+                if (pendingMergedConfig.seq != 0) {
                     if (DEBUG_CONFIGURATION) Log.v(mTag, "Visible with new config: "
-                            + mPendingConfiguration);
-                    updateConfiguration(new Configuration(mPendingConfiguration), !mFirst);
-                    mPendingConfiguration.seq = 0;
+                            + pendingMergedConfig);
+                    performConfigurationChange(mPendingMergedConfiguration, !mFirst,
+                            INVALID_DISPLAY /* same display */);
+                    pendingMergedConfig.seq = 0;
                     updatedConfiguration = true;
                 }
 
@@ -2180,7 +2250,7 @@
             }
         }
 
-        if (mFirst) {
+        if (mFirst && sAlwaysAssignFocus) {
             // handle first focus request
             if (DEBUG_INPUT_RESIZE) Log.v(mTag, "First: mView.hasFocus()="
                     + mView.hasFocus());
@@ -3290,7 +3360,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
@@ -3368,43 +3440,82 @@
         unscheduleTraversals();
     }
 
-    void updateConfiguration(Configuration config, boolean force) {
-        if (DEBUG_CONFIGURATION) Log.v(mTag,
-                "Applying new config to window "
-                + mWindowAttributes.getTitle()
-                + ": " + config);
+    /**
+     * Notifies all callbacks that configuration and/or display has changed and updates internal
+     * state.
+     * @param mergedConfiguration New global and override config in {@link MergedConfiguration}
+     *                            container.
+     * @param force Flag indicating if we should force apply the config.
+     * @param newDisplayId Id of new display if moved, {@link Display#INVALID_DISPLAY} if not
+     *                     changed.
+     */
+    private void performConfigurationChange(MergedConfiguration mergedConfiguration, boolean force,
+            int newDisplayId) {
+        if (mergedConfiguration == null) {
+            throw new IllegalArgumentException("No merged config provided.");
+        }
 
-        CompatibilityInfo ci = mDisplay.getDisplayAdjustments().getCompatibilityInfo();
+        Configuration globalConfig = mergedConfiguration.getGlobalConfiguration();
+        final Configuration overrideConfig = mergedConfiguration.getOverrideConfiguration();
+        if (DEBUG_CONFIGURATION) Log.v(mTag,
+                "Applying new config to window " + mWindowAttributes.getTitle()
+                        + ", globalConfig: " + globalConfig
+                        + ", overrideConfig: " + overrideConfig);
+
+        final CompatibilityInfo ci = mDisplay.getDisplayAdjustments().getCompatibilityInfo();
         if (!ci.equals(CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO)) {
-            config = new Configuration(config);
-            ci.applyToConfiguration(mNoncompatDensity, config);
+            globalConfig = new Configuration(globalConfig);
+            ci.applyToConfiguration(mNoncompatDensity, globalConfig);
         }
 
         synchronized (sConfigCallbacks) {
             for (int i=sConfigCallbacks.size()-1; i>=0; i--) {
-                sConfigCallbacks.get(i).onConfigurationChanged(config);
+                sConfigCallbacks.get(i).onConfigurationChanged(globalConfig);
             }
         }
-        if (mView != null) {
-            // At this point the resources have been updated to
-            // have the most recent config, whatever that is.  Use
-            // the one in them which may be newer.
-            final Resources localResources = mView.getResources();
-            config = localResources.getConfiguration();
-            if (force || mLastConfiguration.diff(config) != 0) {
-                // Update the display with new DisplayAdjustments.
-                mDisplay = ResourcesManager.getInstance().getAdjustedDisplay(
-                        mDisplay.getDisplayId(), localResources);
 
-                final int lastLayoutDirection = mLastConfiguration.getLayoutDirection();
-                final int currentLayoutDirection = config.getLayoutDirection();
-                mLastConfiguration.setTo(config);
-                if (lastLayoutDirection != currentLayoutDirection &&
-                        mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
-                    mView.setLayoutDirection(currentLayoutDirection);
-                }
-                mView.dispatchConfigurationChanged(config);
+        mLastReportedMergedConfiguration.setConfiguration(globalConfig, overrideConfig);
+
+        mForceNextConfigUpdate = force;
+        if (mActivityConfigCallback != null) {
+            // An activity callback is set - notify it about override configuration update.
+            // This basically initiates a round trip to ActivityThread and back, which will ensure
+            // that corresponding activity and resources are updated before updating inner state of
+            // ViewRootImpl. Eventually it will call #updateConfiguration().
+            mActivityConfigCallback.onConfigurationChanged(overrideConfig, newDisplayId);
+        } else {
+            // There is no activity callback - update the configuration right away.
+            updateConfiguration();
+        }
+        mForceNextConfigUpdate = false;
+    }
+
+    /**
+     * Update display and views if last applied merged configuration changed.
+     */
+    public void updateConfiguration() {
+        if (mView == null) {
+            return;
+        }
+
+        // At this point the resources have been updated to
+        // have the most recent config, whatever that is.  Use
+        // the one in them which may be newer.
+        final Resources localResources = mView.getResources();
+        final Configuration config = localResources.getConfiguration();
+        if (mForceNextConfigUpdate || mLastConfigurationFromResources.diff(config) != 0) {
+            // Update the display with new DisplayAdjustments.
+            mDisplay = ResourcesManager.getInstance().getAdjustedDisplay(
+                    mDisplay.getDisplayId(), localResources);
+
+            final int lastLayoutDirection = mLastConfigurationFromResources.getLayoutDirection();
+            final int currentLayoutDirection = config.getLayoutDirection();
+            mLastConfigurationFromResources.setTo(config);
+            if (lastLayoutDirection != currentLayoutDirection
+                    && mViewLayoutDirectionInitial == View.LAYOUT_DIRECTION_INHERIT) {
+                mView.setLayoutDirection(currentLayoutDirection);
             }
+            mView.dispatchConfigurationChanged(config);
         }
     }
 
@@ -3562,13 +3673,16 @@
                 if (mAdded) {
                     SomeArgs args = (SomeArgs) msg.obj;
 
-                    if (mDisplay.getDisplayId() != args.argi3) {
-                        onMovedToDisplay(args.argi3);
+                    final int displayId = args.argi3;
+                    final boolean displayChanged = mDisplay.getDisplayId() != displayId;
+                    if (displayChanged) {
+                        onMovedToDisplay(displayId);
                     }
 
-                    Configuration config = (Configuration) args.arg4;
-                    if (config != null) {
-                        updateConfiguration(config, false);
+                    final MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg4;
+                    if (mergedConfiguration != null) {
+                        performConfigurationChange(mergedConfiguration, false /* force */,
+                                displayChanged ? displayId : INVALID_DISPLAY /* same display */);
                     }
 
                     final boolean framesChanged = !mWinFrame.equals(args.arg1)
@@ -3739,11 +3853,19 @@
                 handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo) msg.obj);
             } break;
             case MSG_UPDATE_CONFIGURATION: {
-                Configuration config = (Configuration)msg.obj;
-                if (config.isOtherSeqNewer(mLastConfiguration)) {
-                    config = mLastConfiguration;
+                Configuration config = (Configuration) msg.obj;
+                if (config.isOtherSeqNewer(
+                        mLastReportedMergedConfiguration.getMergedConfiguration())) {
+                    // If we already have a newer merged config applied - use its global part.
+                    config = mLastReportedMergedConfiguration.getGlobalConfiguration();
                 }
-                updateConfiguration(config, false);
+
+                // Use the newer global config and last reported override config.
+                mPendingMergedConfiguration.setConfiguration(config,
+                        mLastReportedMergedConfiguration.getOverrideConfiguration());
+
+                performConfigurationChange(mPendingMergedConfiguration, false /* force */,
+                        INVALID_DISPLAY /* same display */);
             } break;
             case MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST: {
                 setAccessibilityFocus(null, null);
@@ -4463,9 +4585,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 +4595,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.
@@ -5883,7 +6004,7 @@
         if (params != null) {
             if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params);
         }
-        mPendingConfiguration.seq = 0;
+        mPendingMergedConfiguration.getMergedConfiguration().seq = 0;
         //Log.d(mTag, ">>>>>> CALLING relayout");
         if (params != null && mOrigWindowType != params.type) {
             // For compatibility with old apps, don't crash here.
@@ -5899,8 +6020,8 @@
                 (int) (mView.getMeasuredHeight() * appScale + 0.5f),
                 viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                 mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
-                mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingConfiguration,
-                mSurface);
+                mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame,
+                mPendingMergedConfiguration, mSurface);
 
         mPendingAlwaysConsumeNavBar =
                 (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;
@@ -6180,9 +6301,9 @@
         }
     }
 
-    public void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
+    private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
             Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
-            Configuration newConfig, Rect backDropFrame, boolean forceLayout,
+            MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
             boolean alwaysConsumeNavBar, int displayId) {
         if (DEBUG_LAYOUT) Log.v(mTag, "Resizing " + this + ": frame=" + frame.toShortString()
                 + " contentInsets=" + contentInsets.toShortString()
@@ -6214,7 +6335,8 @@
         args.arg1 = sameProcessCall ? new Rect(frame) : frame;
         args.arg2 = sameProcessCall ? new Rect(contentInsets) : contentInsets;
         args.arg3 = sameProcessCall ? new Rect(visibleInsets) : visibleInsets;
-        args.arg4 = sameProcessCall && newConfig != null ? new Configuration(newConfig) : newConfig;
+        args.arg4 = sameProcessCall && mergedConfiguration != null
+                ? new MergedConfiguration(mergedConfiguration) : null;
         args.arg5 = sameProcessCall ? new Rect(overscanInsets) : overscanInsets;
         args.arg6 = sameProcessCall ? new Rect(stableInsets) : stableInsets;
         args.arg7 = sameProcessCall ? new Rect(outsets) : outsets;
@@ -7224,13 +7346,13 @@
         @Override
         public void resized(Rect frame, Rect overscanInsets, Rect contentInsets,
                 Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
-                Configuration newConfig, Rect backDropFrame, boolean forceLayout,
+                MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
                 boolean alwaysConsumeNavBar, int displayId) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
                 viewAncestor.dispatchResized(frame, overscanInsets, contentInsets,
-                        visibleInsets, stableInsets, outsets, reportDraw, newConfig, backDropFrame,
-                        forceLayout, alwaysConsumeNavBar, displayId);
+                        visibleInsets, stableInsets, outsets, reportDraw, mergedConfiguration,
+                        backDropFrame, forceLayout, alwaysConsumeNavBar, displayId);
             }
         }
 
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index bccaca2..4168756 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;
@@ -274,13 +275,12 @@
      *            {@link #addChildCount(int)} and {@link #setChildCount(int)}.
      * @param virtualId an opaque ID to the Android System (although it could be meaningful to the
      *            {@link View} creating the {@link ViewStructure}), but it's the same id used on
-     *            {@link View#autofillVirtual(int, AutofillValue)}.
+     *            {@link View#autofill(int, AutofillValue)}.
      * @param flags currently {@code 0}.
      *
      * @return Returns an fresh {@link ViewStructure} ready to be filled in.
      */
-    // TODO(b/33197203, b/33802548): add CTS/unit test
-    public abstract ViewStructure newChildForAutofill(int index, int virtualId, int flags);
+    public abstract ViewStructure newChild(int index, int virtualId, int flags);
 
     /**
      * Like {@link #newChild}, but allows the caller to asynchronously populate the returned
@@ -293,7 +293,7 @@
     public abstract ViewStructure asyncNewChild(int index);
 
     /**
-     * Like {@link #newChildForAutofill(int, int, int)}, but allows the caller to asynchronously
+     * Like {@link #newChild(int, int, int)}, but allows the caller to asynchronously
      * populate the returned child.
      *
      * <p>It can transfer the returned {@link ViewStructure} to another thread for it to build its
@@ -306,13 +306,12 @@
      *            {@link #addChildCount(int)} and {@link #setChildCount(int)}.
      * @param virtualId an opaque ID to the Android System (although it could be meaningful to the
      *            {@link View} creating the {@link ViewStructure}), but it's the same id used on
-     *            {@link View#autofillVirtual(int, AutofillValue)}.
+     *            {@link View#autofill(int, AutofillValue)}.
      * @param flags currently {@code 0}.
      *
      * @return Returns an fresh {@link ViewStructure} ready to be filled in.
      */
-    // TODO(b/33197203, b/33802548): add CTS/unit test
-    public abstract ViewStructure asyncNewChildForAutofill(int index, int virtualId, int flags);
+    public abstract ViewStructure asyncNewChild(int index, int virtualId, int flags);
 
     /**
      * Sets the {@link View#getAutofillType()} that can be used to autofill this node.
@@ -323,7 +322,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 +345,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/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 c4f90dc..9ed6371 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -71,12 +71,19 @@
     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_VIEW_ENTERED = 0x2;
-    /** @hide */ public static final int FLAG_VIEW_EXITED = 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 final Rect mTempRect = new Rect();
+    // 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;
+
+    @NonNull private final Rect mTempRect = new Rect();
 
     private final IAutoFillManager mService;
     private IAutoFillManagerClient mServiceClient;
@@ -121,11 +128,28 @@
     }
 
     /**
-     * Called when a {@link View} that supports autofill is entered.
+     * Checkes whether autofill is enabled for the current user.
      *
-     * @param view {@link View} that was entered.
+     * <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 notifyViewEntered(@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) {
@@ -137,9 +161,55 @@
         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) {
+            if (mCallback != null) {
+                mCallback.onAutofillEvent(view, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
+            }
+            return;
+        }
+
+        final Rect bounds = mTempRect;
+        view.getBoundsOnScreen(bounds);
+        final AutofillId id = getAutofillId(view);
+        final AutofillValue value = view.getAutofillValue();
+
         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_VIEW_ENTERED);
@@ -169,11 +239,13 @@
      * @param childId id identifying the virtual child inside the view.
      * @param bounds child boundaries, relative to the top window.
      */
-    public void notifyVirtualViewEntered(@NonNull View view, int childId,
-            @NonNull Rect bounds) {
+    public void notifyViewEntered(@NonNull View view, int childId, @NonNull Rect bounds) {
         ensureServiceClientAddedIfNeeded();
 
         if (!mEnabled) {
+            if (mCallback != null) {
+                mCallback.onAutofillEvent(view, childId, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
+            }
             return;
         }
 
@@ -181,7 +253,7 @@
 
         if (!mHasSession) {
             // Starts new session.
-            startSession(id, view.getWindowToken(), bounds, null);
+            startSession(id, view.getWindowToken(), bounds, null, 0);
         } else {
             // Update focus on existing session.
             updateSession(id, bounds, null, FLAG_VIEW_ENTERED);
@@ -194,7 +266,7 @@
      * @param view the {@link View} whose descendant is the virtual view.
      * @param childId id identifying the virtual child inside the view.
      */
-    public void notifyVirtualViewExited(@NonNull View view, int childId) {
+    public void notifyViewExited(@NonNull View view, int childId) {
         ensureServiceClientAddedIfNeeded();
 
         if (mEnabled && mHasSession) {
@@ -224,16 +296,16 @@
     /**
      * 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 notifyVirtualValueChanged(View parent, int childId, AutofillValue value) {
+    public void notifyValueChanged(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);
     }
 
@@ -304,17 +376,18 @@
         return new AutofillId(parent.getAccessibilityViewId(), childId);
     }
 
-    private void startSession(AutofillId id, IBinder windowToken, Rect bounds,
-            AutofillValue value) {
+    private void startSession(@NonNull AutofillId id, @NonNull IBinder windowToken,
+            @NonNull Rect bounds, @NonNull 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);
-            final AutofillClient client = getClient();
+                    mCallback != null, flags, mContext.getOpPackageName());
+            AutofillClient client = getClient();
             if (client != null) {
                 client.resetableStateAvailable();
             }
@@ -435,7 +508,7 @@
             return;
         }
         if (id.isVirtual()) {
-            mCallback.onAutofillEventVirtual(view, id.getVirtualChildId(), event);
+            mCallback.onAutofillEvent(view, id.getVirtualChildId(), event);
         } else {
             mCallback.onAutofillEvent(view, event);
         }
@@ -471,13 +544,23 @@
         public static final int EVENT_INPUT_HIDDEN = 2;
 
         /**
+         * The auto-fill input UI affordance associated with the view won't be shown because
+         * autofill is not available.
+         *
+         * <p>If the view provides its own auto-complete UI affordance but was not displaying it
+         * to avoid flickering, it could shown it upon receiving this event.
+         */
+        public static final int EVENT_INPUT_UNAVAILABLE = 3;
+
+        /**
          * Called after a change in the autofill state associated with a view.
          *
          * @param view view associated with the change.
          *
          * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
          */
-        public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {}
+        public void onAutofillEvent(@NonNull View view, @AutofillEventType int event) {
+        }
 
         /**
          * Called after a change in the autofill state associated with a virtual view.
@@ -487,8 +570,8 @@
          *
          * @param event currently either {@link #EVENT_INPUT_SHOWN} or {@link #EVENT_INPUT_HIDDEN}.
          */
-        public void onAutofillEventVirtual(@NonNull View view, int childId,
-                @AutofillEventType int event) {}
+        public void onAutofillEvent(@NonNull View view, int childId, @AutofillEventType int event) {
+        }
     }
 
     private static final class AutofillManagerClient extends IAutoFillManagerClient.Stub {
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 86a4965..97210cc 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -32,7 +32,7 @@
     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, String packageName);
     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);
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..fae5742 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;
@@ -511,11 +514,15 @@
     }
 
     @Override
-    public void autofill(AutofillValue value) {
-        if (!isEnabled()) return;
+    public boolean autofill(AutofillValue value) {
+        if (!isEnabled()) return false;
 
-        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);
+        }
+        return true;
     }
 
     @Override
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index d246405..9dc61ab 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;
@@ -578,14 +580,20 @@
     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;
+    public boolean autofill(AutofillValue value) {
+        if (!isEnabled()) return false;
 
-        setChecked(value.getToggleValue());
+        if (value.isToggle()) {
+            setChecked(value.getToggleValue());
+        } else {
+            Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+        }
+
+        return true;
     }
 
     @Override
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 31a88d4..7d04f35 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.
@@ -772,10 +775,16 @@
     }
 
     @Override
-    public void autofill(AutofillValue value) {
-        if (!isEnabled()) return;
+    public boolean autofill(AutofillValue value) {
+        if (!isEnabled()) return false;
 
-        mDelegate.updateDate(value.getDateValue());
+        if (value.isDate()) {
+            mDelegate.updateDate(value.getDateValue());
+        } else {
+            Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+        }
+
+        return true;
     }
 
     @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 dc9976d..a7574c7 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;
@@ -421,20 +422,28 @@
     @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;
+    public boolean autofill(AutofillValue value) {
+        if (!isEnabled()) return false;
 
-        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 false;
+        }
+
         final View child = getChildAt(index);
         if (child == null) {
             Log.w(VIEW_LOG_TAG, "RadioGroup.autoFill(): no child with index " + index);
-            return;
+            return false;
         }
         check(child.getId());
+        return true;
     }
 
     @Override
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 3822138..519a7dd 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -58,6 +58,7 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.AdapterView.OnItemSelectedListener;
@@ -159,20 +160,6 @@
     private SearchableInfo mSearchable;
     private Bundle mAppSearchData;
 
-    /*
-     * SearchView can be set expanded before the IME is ready to be shown during
-     * initial UI setup. The show operation is asynchronous to account for this.
-     */
-    private Runnable mShowImeRunnable = new Runnable() {
-        public void run() {
-            InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
-
-            if (imm != null) {
-                imm.showSoftInputUnchecked(0, null);
-            }
-        }
-    };
-
     private Runnable mUpdateDrawableStateRunnable = new Runnable() {
         public void run() {
             updateFocusedState();
@@ -497,9 +484,9 @@
     @Override
     public void clearFocus() {
         mClearingFocus = true;
-        setImeVisibility(false);
         super.clearFocus();
         mSearchSrcTextView.clearFocus();
+        mSearchSrcTextView.setImeVisibility(false);
         mClearingFocus = false;
     }
 
@@ -967,19 +954,6 @@
         super.onDetachedFromWindow();
     }
 
-    private void setImeVisibility(final boolean visible) {
-        if (visible) {
-            post(mShowImeRunnable);
-        } else {
-            removeCallbacks(mShowImeRunnable);
-            InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
-
-            if (imm != null) {
-                imm.hideSoftInputFromWindow(getWindowToken(), 0);
-            }
-        }
-    }
-
     /**
      * Called by the SuggestionsAdapter
      * @hide
@@ -1286,7 +1260,7 @@
                 if (mSearchable != null) {
                     launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, query.toString());
                 }
-                setImeVisibility(false);
+                mSearchSrcTextView.setImeVisibility(false);
                 dismissSuggestions();
             }
         }
@@ -1311,7 +1285,7 @@
         } else {
             mSearchSrcTextView.setText("");
             mSearchSrcTextView.requestFocus();
-            setImeVisibility(true);
+            mSearchSrcTextView.setImeVisibility(true);
         }
 
     }
@@ -1319,7 +1293,7 @@
     private void onSearchClicked() {
         updateViewsVisibility(false);
         mSearchSrcTextView.requestFocus();
-        setImeVisibility(true);
+        mSearchSrcTextView.setImeVisibility(true);
         if (mOnSearchClickListener != null) {
             mOnSearchClickListener.onClick(this);
         }
@@ -1477,7 +1451,7 @@
         if (mOnSuggestionListener == null
                 || !mOnSuggestionListener.onSuggestionClick(position)) {
             launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null);
-            setImeVisibility(false);
+            mSearchSrcTextView.setImeVisibility(false);
             dismissSuggestions();
             return true;
         }
@@ -1910,6 +1884,9 @@
         private int mThreshold;
         private SearchView mSearchView;
 
+        private boolean mHasPendingShowSoftInputRequest;
+        final Runnable mRunShowSoftInputIfNecessary = () -> showSoftInputIfNecessary();
+
         public SearchAutoComplete(Context context) {
             super(context);
             mThreshold = getThreshold();
@@ -1983,11 +1960,13 @@
             super.onWindowFocusChanged(hasWindowFocus);
 
             if (hasWindowFocus && mSearchView.hasFocus() && getVisibility() == VISIBLE) {
-                InputMethodManager inputManager =
-                        getContext().getSystemService(InputMethodManager.class);
-                inputManager.showSoftInput(this, 0);
-                // If in landscape mode, then make sure that
-                // the ime is in front of the dropdown.
+                // Since InputMethodManager#onPostWindowFocus() will be called after this callback,
+                // it is a bit too early to call InputMethodManager#showSoftInput() here. We still
+                // need to wait until the system calls back onCreateInputConnection() to call
+                // InputMethodManager#showSoftInput().
+                mHasPendingShowSoftInputRequest = true;
+
+                // If in landscape mode, then make sure that the ime is in front of the dropdown.
                 if (isLandscapeMode(getContext())) {
                     ensureImeVisible(true);
                 }
@@ -2027,7 +2006,7 @@
                     }
                     if (event.isTracking() && !event.isCanceled()) {
                         mSearchView.clearFocus();
-                        mSearchView.setImeVisibility(false);
+                        setImeVisibility(false);
                         return true;
                     }
                 }
@@ -2051,5 +2030,51 @@
             };
             return 160;
         }
+
+        /**
+         * We override {@link View#onCreateInputConnection(EditorInfo)} as a signal to schedule a
+         * pending {@link InputMethodManager#showSoftInput(View, int)} request (if any).
+         */
+        @Override
+        public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
+            final InputConnection ic = super.onCreateInputConnection(editorInfo);
+            if (mHasPendingShowSoftInputRequest) {
+                removeCallbacks(mRunShowSoftInputIfNecessary);
+                post(mRunShowSoftInputIfNecessary);
+            }
+            return ic;
+        }
+
+        private void showSoftInputIfNecessary() {
+            if (mHasPendingShowSoftInputRequest) {
+                final InputMethodManager imm =
+                        getContext().getSystemService(InputMethodManager.class);
+                imm.showSoftInput(this, 0);
+                mHasPendingShowSoftInputRequest = false;
+            }
+        }
+
+        private void setImeVisibility(final boolean visible) {
+            final InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
+            if (!visible) {
+                mHasPendingShowSoftInputRequest = false;
+                removeCallbacks(mRunShowSoftInputIfNecessary);
+                imm.hideSoftInputFromWindow(getWindowToken(), 0);
+                return;
+            }
+
+            if (imm.isActive(this)) {
+                // This means that SearchAutoComplete is already connected to the IME.
+                // InputMethodManager#showSoftInput() is guaranteed to pass client-side focus check.
+                mHasPendingShowSoftInputRequest = false;
+                removeCallbacks(mRunShowSoftInputIfNecessary);
+                imm.showSoftInput(this, 0);
+                return;
+            }
+
+            // Otherwise, InputMethodManager#showSoftInput() should be deferred after
+            // onCreateInputConnection().
+            mHasPendingShowSoftInputRequest = true;
+        }
     }
 }
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 d591316f..26edc43 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9900,7 +9900,7 @@
         final boolean isPassword = hasPasswordTransformationMethod()
                 || isPasswordInputType(getInputType());
         if (forAutofill) {
-            structure.setSanitized(mTextFromResource);
+            structure.setDataIsSensitive(!mTextFromResource);
         }
 
         if (!isPassword || forAutofill) {
@@ -10012,13 +10012,33 @@
 
     // TODO(b/33197203): add unit/CTS tests for autofill methods
 
-    @Override
-    public void autofill(AutofillValue value) {
-        final CharSequence text = value.getTextValue();
-
-        if (text != null && isTextEditable()) {
-            setText(text, mBufferType, true, 0);
+    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 boolean autofill(AutofillValue value) {
+        if (value.isText()) {
+            if (isTextEditable()) {
+                setText(value.getTextValue(), mBufferType, true, 0);
+                return true;
+            }
+        } else {
+            Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+        }
+
+        return false;
     }
 
     @Override
@@ -10479,6 +10499,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 +10564,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 9825f1e..1e97e3b 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.
@@ -527,10 +530,16 @@
     }
 
     @Override
-    public void autofill(AutofillValue value) {
-        if (!isEnabled()) return;
+    public boolean autofill(AutofillValue value) {
+        if (!isEnabled()) return false;
 
-        mDelegate.setDate(value.getDateValue());
+        if (value.isDate()) {
+            mDelegate.setDate(value.getDateValue());
+        } else {
+            Log.w(LOG_TAG, value + " could not be autofilled into " + this);
+        }
+
+        return true;
     }
 
     @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/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/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/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index ce51dc4..361fd3da 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -16,12 +16,12 @@
 
 package com.android.internal.view;
 
-import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.hardware.input.InputManager;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.util.MergedConfiguration;
 import android.view.DragEvent;
 import android.view.IWindow;
 import android.view.IWindowSession;
@@ -39,8 +39,9 @@
 
     @Override
     public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
-            Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig,
-            Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId) {
+            Rect stableInsets, Rect outsets, boolean reportDraw,
+            MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
+            boolean alwaysConsumeNavBar, int displayId) {
         if (reportDraw) {
             try {
                 mSession.finishDrawing(this);
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/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp
index 79439e2..5553a3e 100644
--- a/core/jni/android/graphics/ColorFilter.cpp
+++ b/core/jni/android/graphics/ColorFilter.cpp
@@ -30,7 +30,7 @@
 
 class SkColorFilterGlue {
 public:
-    static void finalizer(JNIEnv* env, jobject clazz, jlong skFilterHandle) {
+    static void SafeUnref(JNIEnv* env, jobject clazz, jlong skFilterHandle) {
         SkColorFilter* filter = reinterpret_cast<SkColorFilter *>(skFilterHandle);
         SkSafeUnref(filter);
     }
@@ -57,19 +57,19 @@
 };
 
 static const JNINativeMethod colorfilter_methods[] = {
-    {"destroyFilter", "(J)V", (void*) SkColorFilterGlue::finalizer}
+    {"nSafeUnref", "(J)V", (void*) SkColorFilterGlue::SafeUnref}
 };
 
 static const JNINativeMethod porterduff_methods[] = {
-    { "native_CreatePorterDuffFilter", "(II)J", (void*) SkColorFilterGlue::CreatePorterDuffFilter   },
+    { "native_CreatePorterDuffFilter", "(II)J", (void*) SkColorFilterGlue::CreatePorterDuffFilter },
 };
 
 static const JNINativeMethod lighting_methods[] = {
-    { "native_CreateLightingFilter", "(II)J", (void*) SkColorFilterGlue::CreateLightingFilter   },
+    { "native_CreateLightingFilter", "(II)J", (void*) SkColorFilterGlue::CreateLightingFilter },
 };
 
 static const JNINativeMethod colormatrix_methods[] = {
-    { "nativeColorMatrixFilter", "([F)J", (void*) SkColorFilterGlue::CreateColorMatrixFilter   },
+    { "nativeColorMatrixFilter", "([F)J", (void*) SkColorFilterGlue::CreateColorMatrixFilter },
 };
 
 int register_android_graphics_ColorFilter(JNIEnv* env) {
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/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 9660de4..956b724 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -26,7 +26,9 @@
 #include <sys/un.h>
 #include <unistd.h>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
 // Static whitelist of open paths that the zygote is allowed to keep open.
@@ -65,9 +67,10 @@
       return true;
   }
 
-  static const std::string kFrameworksPrefix = "/system/framework/";
-  static const std::string kJarSuffix = ".jar";
-  if (StartsWith(path, kFrameworksPrefix) && EndsWith(path, kJarSuffix)) {
+  static const char* kFrameworksPrefix = "/system/framework/";
+  static const char* kJarSuffix = ".jar";
+  if (android::base::StartsWith(path, kFrameworksPrefix)
+      && android::base::EndsWith(path, kJarSuffix)) {
     return true;
   }
 
@@ -79,28 +82,31 @@
   // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
   // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
   // See AssetManager.cpp for more details on overlay-subdir.
-  static const std::string kOverlayDir = "/system/vendor/overlay/";
-  static const std::string kVendorOverlayDir = "/vendor/overlay";
-  static const std::string kOverlaySubdir = "/system/vendor/overlay-subdir/";
-  static const std::string kApkSuffix = ".apk";
+  static const char* kOverlayDir = "/system/vendor/overlay/";
+  static const char* kVendorOverlayDir = "/vendor/overlay";
+  static const char* kOverlaySubdir = "/system/vendor/overlay-subdir/";
+  static const char* kApkSuffix = ".apk";
 
-  if ((StartsWith(path, kOverlayDir) || StartsWith(path, kOverlaySubdir)
-       || StartsWith(path, kVendorOverlayDir))
-      && EndsWith(path, kApkSuffix)
+  if ((android::base::StartsWith(path, kOverlayDir)
+       || android::base::StartsWith(path, kOverlaySubdir)
+       || android::base::StartsWith(path, kVendorOverlayDir))
+      && android::base::EndsWith(path, kApkSuffix)
       && path.find("/../") == std::string::npos) {
     return true;
   }
 
-  static const std::string kOverlayIdmapPrefix = "/data/resource-cache/";
-  static const std::string kOverlayIdmapSuffix = ".apk@idmap";
-  if (StartsWith(path, kOverlayIdmapPrefix) && EndsWith(path, kOverlayIdmapSuffix)
+  static const char* kOverlayIdmapPrefix = "/data/resource-cache/";
+  static const char* kOverlayIdmapSuffix = ".apk@idmap";
+  if (android::base::StartsWith(path, kOverlayIdmapPrefix)
+      && android::base::EndsWith(path, kOverlayIdmapSuffix)
       && path.find("/../") == std::string::npos) {
     return true;
   }
 
   // All regular files that are placed under this path are whitelisted automatically.
-  static const std::string kZygoteWhitelistPath = "/vendor/zygote_whitelist/";
-  if (StartsWith(path, kZygoteWhitelistPath) && path.find("/../") == std::string::npos) {
+  static const char* kZygoteWhitelistPath = "/vendor/zygote_whitelist/";
+  if (android::base::StartsWith(path, kZygoteWhitelistPath)
+      && path.find("/../") == std::string::npos) {
     return true;
   }
 
@@ -111,24 +117,6 @@
     : whitelist_() {
 }
 
-// TODO: Call android::base::StartsWith instead of copying the code here.
-// static
-bool FileDescriptorWhitelist::StartsWith(const std::string& str,
-                                         const std::string& prefix) {
-  return str.compare(0, prefix.size(), prefix) == 0;
-}
-
-// TODO: Call android::base::EndsWith instead of copying the code here.
-// static
-bool FileDescriptorWhitelist::EndsWith(const std::string& str,
-                                       const std::string& suffix) {
-  if (suffix.size() > str.size()) {
-    return false;
-  }
-
-  return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
-}
-
 FileDescriptorWhitelist* FileDescriptorWhitelist::instance_ = nullptr;
 
 // static
@@ -174,7 +162,8 @@
   }
 
   std::string file_path;
-  if (!Readlink(fd, &file_path)) {
+  const std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
+  if (!android::base::Readlink(fd_path, &file_path)) {
     return NULL;
   }
 
@@ -299,30 +288,6 @@
   is_sock(false) {
 }
 
-// TODO: Call android::base::Readlink instead of copying the code here.
-// static
-bool FileDescriptorInfo::Readlink(const int fd, std::string* result) {
-  char path[64];
-  snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
-
-  // Code copied from android::base::Readlink starts here :
-
-  // Annoyingly, the readlink system call returns EINVAL for a zero-sized buffer,
-  // and truncates to whatever size you do supply, so it can't be used to query.
-  // We could call lstat first, but that would introduce a race condition that
-  // we couldn't detect.
-  // ext2 and ext4 both have PAGE_SIZE limitations, so we assume that here.
-  char buf[4096];
-  ssize_t len = readlink(path, buf, sizeof(buf));
-  if (len == -1) {
-    PLOG(ERROR) << "Readlink on " << fd << " failed.";
-    return false;
-  }
-
-  result->assign(buf, len);
-  return true;
-}
-
 // static
 bool FileDescriptorInfo::GetSocketName(const int fd, std::string* result) {
   sockaddr_storage ss;
diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h
index 03298c3..a39e387 100644
--- a/core/jni/fd_utils.h
+++ b/core/jni/fd_utils.h
@@ -59,10 +59,6 @@
  private:
   FileDescriptorWhitelist();
 
-  static bool StartsWith(const std::string& str, const std::string& prefix);
-
-  static bool EndsWith(const std::string& str, const std::string& suffix);
-
   static FileDescriptorWhitelist* instance_;
 
   std::vector<std::string> whitelist_;
@@ -99,8 +95,6 @@
   FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
                      int fd_flags, int fs_flags, off_t offset);
 
-  static bool Readlink(const int fd, std::string* result);
-
   // Returns the locally-bound name of the socket |fd|. Returns true
   // iff. all of the following hold :
   //
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/values/attrs.xml b/core/res/res/values/attrs.xml
index 248fd15..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. -->
@@ -8407,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..cf6bd9e 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1014,6 +1014,15 @@
          <p>The default value of this attribute is <code>false</code>. -->
     <attr name="isolatedSplits" format="boolean" />
 
+    <!-- If set to <code>true</code>, indicates to the platform that this APK is
+         a 'feature' split and that it implicitly depends on the base APK. This distinguishes
+         this split APK from a 'configuration' split, which provides resource overrides
+         for a particular 'feature' split. Only useful when the base APK specifies
+         <code>android:isolatedSplits="true"</code>.
+
+         <p>The default value of this attribute is <code>false</code>. -->
+    <attr name="isFeatureSplit" format="boolean" />
+
     <!-- Extra options for an activity's UI. Applies to either the {@code <activity>} or
          {@code <application>} tag. If specified on the {@code <application>}
          tag these will be considered defaults for all activities in the
@@ -1286,6 +1295,7 @@
         <attr name="sharedUserLabel" />
         <attr name="installLocation" />
         <attr name="isolatedSplits" />
+        <attr name="isFeatureSplit" />
         <attr name="targetSandboxVersion" />
     </declare-styleable>
 
@@ -2401,6 +2411,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/config.xml b/core/res/res/values/config.xml
index 6e0d9dc..385f256 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1386,6 +1386,9 @@
     <!-- Boolean indicating if current platform supports BLE peripheral mode -->
     <bool name="config_bluetooth_le_peripheral_mode_supported">false</bool>
 
+    <!-- Boolean indicating if current platform supports HFP inband ringing -->
+    <bool name="config_bluetooth_hfp_inband_ringing_support">false</bool>
+
     <!-- Max number of scan filters supported by blutooth controller. 0 if the
          device does not support hardware scan filters-->
     <integer translatable="false" name="config_bluetooth_max_scan_filters">0</integer>
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 bb3f1c3..f965c69 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2806,6 +2806,8 @@
         <public name="fontProviderPackage" />
         <public name="importantForAutofill" />
         <public name="recycleEnabled"/>
+        <public name="isStatic" />
+        <public name="isFeatureSplit" />
     </public-group>
 
     <public-group type="style" first-id="0x010302e0">
@@ -2814,6 +2816,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..d1c14e9 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>
 
@@ -4564,22 +4567,25 @@
     <!-- Accessibility string used for describing the button in time picker that changes the dialog to circular clock mode. [CHAR LIMIT=NONE] -->
     <string name="time_picker_radial_mode_description">Switch to clock mode for the time input.</string>
 
-    <!-- Title for the auto-fill save dialog shown when the the contents of the activity can be saved
-         by an auto-fill service, but the service does not know what the activity represents [CHAR LIMIT=NONE] -->
+    <!-- Accessibility title for the autofill dialog used to select a list of options to autofill an activity. [CHAR LIMIT=NONE] -->
+    <string name="autofill_picker_accessibility_title">Autofill options</string>
+
+    <!-- Title for the autofill save dialog shown when the the contents of the activity can be saved
+         by an autofill service, but the service does not know what the activity represents [CHAR LIMIT=NONE] -->
     <string name="autofill_save_title">Save to <xliff:g id="label" example="MyPass">%1$s</xliff:g>?</string>
-    <!-- Title for the auto-fill save dialog shown when the the contents of the activity can be saved
-         by an auto-fill service, and the service does knows what the activity represents (for example, credit card info) [CHAR LIMIT=NONE] -->
+    <!-- Title for the autofill save dialog shown when the the contents of the activity can be saved
+         by an autofill service, and the service does knows what the activity represents (for example, credit card info) [CHAR LIMIT=NONE] -->
     <string name="autofill_save_title_with_type">Save <xliff:g id="type" example="Credit Card">%1$s</xliff:g> to <xliff:g id="label" example="MyPass">%2$s</xliff:g>?</string>
-    <!-- Label for the auto-fill save button [CHAR LIMIT=NONE] -->
+    <!-- Label for the autofill save button [CHAR LIMIT=NONE] -->
     <string name="autofill_save_yes">Save</string>
-    <!-- Label for the auto-fill cancel button [CHAR LIMIT=NONE] -->
+    <!-- Label for the autofill cancel button [CHAR LIMIT=NONE] -->
     <string name="autofill_save_no">No thanks</string>
 
-    <!-- Label for the type of data being saved for auto-fill when it represent user credentials with a password [CHAR LIMIT=NONE] -->
+    <!-- Label for the type of data being saved for autofill when it represent user credentials with a password [CHAR LIMIT=NONE] -->
     <string name="autofill_save_type_password">password</string>
-    <!-- Label for the type of data being saved for auto-fill when it represent an address (street, city, etc.) [CHAR LIMIT=NONE] -->
+    <!-- Label for the type of data being saved for autofill when it represent an address (street, city, etc.) [CHAR LIMIT=NONE] -->
     <string name="autofill_save_type_address">address</string>
-    <!-- Label for the type of data being saved for auto-fill when it represents a credit card [CHAR LIMIT=NONE] -->
+    <!-- Label for the type of data being saved for autofill when it represents a credit card [CHAR LIMIT=NONE] -->
     <string name="autofill_save_type_credit_card">credit card</string>
 
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0c318cf..0d4a407 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -242,6 +242,7 @@
   <java-symbol type="bool" name="config_bluetooth_address_validation" />
   <java-symbol type="bool" name="config_bluetooth_sco_off_call" />
   <java-symbol type="bool" name="config_bluetooth_le_peripheral_mode_supported" />
+  <java-symbol type="bool" name="config_bluetooth_hfp_inband_ringing_support" />
   <java-symbol type="bool" name="config_cellBroadcastAppLinks" />
   <java-symbol type="bool" name="config_duplicate_port_omadm_wappush" />
   <java-symbol type="bool" name="config_enableAutoPowerModes" />
@@ -2425,6 +2426,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 +2842,14 @@
   <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_picker_accessibility_title " />
   <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/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/docs/html/topic/libraries/support-library/revisions.jd b/docs/html/topic/libraries/support-library/revisions.jd
index 9a24d15..adb1af5 100644
--- a/docs/html/topic/libraries/support-library/revisions.jd
+++ b/docs/html/topic/libraries/support-library/revisions.jd
@@ -316,14 +316,13 @@
   implementations, as well as any calls to this method, should be removed.
   </li>
 
-  <li>{@link android.support.v4.media.session.MediaSessionCompat#obtain
+  <li>{@code
   MediaSessionCompat.obtain()} has been deprecated and replaced with the more
   appropriately-named method
   <a href="/reference/android/support/v4/media/session/MediaSessionCompat.html#fromMediaSession"><code>fromMediaSession()</code></a>.
   </li>
 
-  <li>{@link
-  android.support.v4.media.session.MediaSessionCompat.QueueItem#obtain
+  <li>{@code
   MediaSessionCompat.QueueItem.obtain()} has been deprecated and replaced with
   the more appropriately-named method
   <a href="/reference/android/support/v4/media/session/MediaSessionCompat.QueueItem.html#fromQueueItem"><code>fromQueueItem()</code></a>.
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/ColorFilter.java b/graphics/java/android/graphics/ColorFilter.java
index ac62bf4..0ca3729 100644
--- a/graphics/java/android/graphics/ColorFilter.java
+++ b/graphics/java/android/graphics/ColorFilter.java
@@ -28,21 +28,51 @@
  */
 public class ColorFilter {
     /**
-     * Holds the pointer to the native SkColorFilter instance.
-     *
-     * @hide
+     * @deprecated Use subclass constructors directly instead.
      */
-    public long native_instance;
+    @Deprecated
+    public ColorFilter() {}
+
+    /**
+     * Holds the pointer to the native SkColorFilter instance.
+     */
+    private long mNativeInstance;
+
+    long createNativeInstance() {
+        return 0;
+    }
+
+    void discardNativeInstance() {
+        if (mNativeInstance != 0) {
+            nSafeUnref(mNativeInstance);
+            mNativeInstance = 0;
+        }
+    }
 
     @Override
     protected void finalize() throws Throwable {
         try {
-            super.finalize();
+            if (mNativeInstance != 0) {
+                nSafeUnref(mNativeInstance);
+            }
+            mNativeInstance = -1;
         } finally {
-            destroyFilter(native_instance);
-            native_instance = 0;
+            super.finalize();
         }
     }
 
-    static native void destroyFilter(long native_instance);
+    /** @hide */
+    public long getNativeInstance() {
+        if (mNativeInstance == -1) {
+            throw new IllegalStateException("attempting to use a finalized ColorFilter");
+        }
+
+        if (mNativeInstance == 0) {
+            mNativeInstance = createNativeInstance();
+        }
+        return mNativeInstance;
+
+    }
+
+    static native void nSafeUnref(long native_instance);
 }
diff --git a/graphics/java/android/graphics/ColorMatrix.java b/graphics/java/android/graphics/ColorMatrix.java
index 1b1849e..6299b2c 100644
--- a/graphics/java/android/graphics/ColorMatrix.java
+++ b/graphics/java/android/graphics/ColorMatrix.java
@@ -268,4 +268,21 @@
         m[5] = 1;   m[6] = -0.34414f;   m[7] = -0.71414f;
         m[10] = 1;  m[11] = 1.772f;     m[12] = 0;
     }
+
+    @Override
+    public boolean equals(Object obj) {
+        // if (obj == this) return true; -- NaN value would mean matrix != itself
+        if (!(obj instanceof ColorMatrix)) {
+            return false;
+        }
+
+        // we don't use Arrays.equals(), since that considers NaN == NaN
+        final float[] other = ((ColorMatrix) obj).mArray;
+        for (int i = 0; i < 20; i++) {
+            if (other[i] != mArray[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/graphics/java/android/graphics/ColorMatrixColorFilter.java b/graphics/java/android/graphics/ColorMatrixColorFilter.java
index 291c8ff..61f6cc5 100644
--- a/graphics/java/android/graphics/ColorMatrixColorFilter.java
+++ b/graphics/java/android/graphics/ColorMatrixColorFilter.java
@@ -16,6 +16,9 @@
 
 package android.graphics;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
 /**
  * A color filter that transforms colors through a 4x5 color matrix. This filter
  * can be used to change the saturation of pixels, convert from YUV to RGB, etc.
@@ -32,9 +35,8 @@
      *               the filter, so changes made to the matrix after the filter
      *               is constructed will not be reflected in the filter.
      */
-    public ColorMatrixColorFilter(ColorMatrix matrix) {
+    public ColorMatrixColorFilter(@NonNull ColorMatrix matrix) {
         mMatrix.set(matrix);
-        update();
     }
 
     /**
@@ -44,84 +46,76 @@
      *              matrix. The first 20 entries of the array are copied into
      *              the filter. See ColorMatrix.
      */
-    public ColorMatrixColorFilter(float[] array) {
+    public ColorMatrixColorFilter(@NonNull float[] array) {
         if (array.length < 20) {
             throw new ArrayIndexOutOfBoundsException();
         }
         mMatrix.set(array);
-        update();
     }
 
     /**
-     * Returns the {@link ColorMatrix} used by this filter. The returned
-     * value is never null. Modifying the returned matrix does not have
-     * any effect until you call {@link #setColorMatrix(ColorMatrix)}.
+     * Copies the ColorMatrix from the filter into the passed ColorMatrix.
      *
-     * @see #setColorMatrix(ColorMatrix)
-     *
-     * @hide
+     * @param colorMatrix Set to the current value of the filter's ColorMatrix.
      */
-    public ColorMatrix getColorMatrix() {
-        return mMatrix;
+    public void getColorMatrix(ColorMatrix colorMatrix) {
+        colorMatrix.set(mMatrix);
     }
 
     /**
-     * Specifies the color matrix used by this filter. If the specified
-     * color matrix is null, this filter's color matrix will be reset to
-     * the identity matrix.
+     * Copies the provided color matrix to be used by this filter.
+     *
+     * If the specified color matrix is null, this filter's color matrix will be reset to the
+     * identity matrix.
      *
      * @param matrix A {@link ColorMatrix} or null
      *
-     * @see #getColorMatrix()
-     * @see android.graphics.ColorMatrix#reset()
-     * @see #setColorMatrix(float[])
-     *
-     * @hide
+     * @see #getColorMatrix(ColorMatrix)
+     * @see #setColorMatrixArray(float[])
+     * @see ColorMatrix#reset()
      */
-    public void setColorMatrix(ColorMatrix matrix) {
+    public void setColorMatrix(@Nullable ColorMatrix matrix) {
+        discardNativeInstance();
         if (matrix == null) {
             mMatrix.reset();
-        } else if (matrix != mMatrix) {
+        } else {
             mMatrix.set(matrix);
         }
-        update();
     }
 
     /**
-     * Specifies the color matrix used by this filter. If the specified
-     * color matrix is null, this filter's color matrix will be reset to
-     * the identity matrix.
+     * Copies the provided color matrix to be used by this filter.
+     *
+     * If the specified color matrix is null, this filter's color matrix will be reset to the
+     * identity matrix.
      *
      * @param array Array of floats used to transform colors, treated as a 4x5
      *              matrix. The first 20 entries of the array are copied into
      *              the filter. See {@link ColorMatrix}.
      *
-     * @see #getColorMatrix()
-     * @see android.graphics.ColorMatrix#reset()
+     * @see #getColorMatrix(ColorMatrix)
      * @see #setColorMatrix(ColorMatrix)
+     * @see ColorMatrix#reset()
      *
      * @throws ArrayIndexOutOfBoundsException if the specified array's
      *         length is < 20
-     *
-     * @hide
      */
-    public void setColorMatrix(float[] array) {
+    public void setColorMatrixArray(@Nullable float[] array) {
+        // called '...Array' so that passing null isn't ambiguous
+        discardNativeInstance();
         if (array == null) {
             mMatrix.reset();
         } else {
             if (array.length < 20) {
                 throw new ArrayIndexOutOfBoundsException();
             }
-
             mMatrix.set(array);
         }
-        update();
     }
 
-    private void update() {
-        final float[] colorMatrix = mMatrix.getArray();
-        destroyFilter(native_instance);
-        native_instance = nativeColorMatrixFilter(colorMatrix);
+    @Override
+    long createNativeInstance() {
+        return nativeColorMatrixFilter(mMatrix.getArray());
     }
 
     private static native long nativeColorMatrixFilter(float[] array);
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/LightingColorFilter.java b/graphics/java/android/graphics/LightingColorFilter.java
index ad78430..b0c145b 100644
--- a/graphics/java/android/graphics/LightingColorFilter.java
+++ b/graphics/java/android/graphics/LightingColorFilter.java
@@ -21,6 +21,8 @@
 
 package android.graphics;
 
+import android.annotation.ColorInt;
+
 /**
  * A color filter that can be used to simulate simple lighting effects.
  * A <code>LightingColorFilter</code> is defined by two parameters, one
@@ -37,7 +39,9 @@
  * The result is pinned to the <code>[0..255]</code> range for each channel.
  */
 public class LightingColorFilter extends ColorFilter {
+    @ColorInt
     private int mMul;
+    @ColorInt
     private int mAdd;
 
     /**
@@ -45,10 +49,9 @@
      * and then adds a second color. The alpha components of the mul and add
      * arguments are ignored.
      */
-    public LightingColorFilter(int mul, int add) {
+    public LightingColorFilter(@ColorInt int mul, @ColorInt int add) {
         mMul = mul;
         mAdd = add;
-        update();
     }
 
     /**
@@ -56,9 +59,8 @@
      * color filter is applied.
      *
      * @see #setColorMultiply(int)
-     *
-     * @hide
      */
+    @ColorInt
     public int getColorMultiply() {
         return mMul;
     }
@@ -69,12 +71,12 @@
      * The alpha channel of this color is ignored.
      *
      * @see #getColorMultiply()
-     *
-     * @hide
      */
-    public void setColorMultiply(int mul) {
-        mMul = mul;
-        update();
+    public void setColorMultiply(@ColorInt int mul) {
+        if (mMul != mul) {
+            mMul = mul;
+            discardNativeInstance();
+        }
     }
 
     /**
@@ -82,9 +84,8 @@
      * when the color filter is applied.
      *
      * @see #setColorAdd(int)
-     *
-     * @hide
      */
+    @ColorInt
     public int getColorAdd() {
         return mAdd;
     }
@@ -95,17 +96,17 @@
      * The alpha channel of this color is ignored.
      *
      * @see #getColorAdd()
-     *
-     * @hide
      */
-    public void setColorAdd(int add) {
-        mAdd = add;
-        update();
+    public void setColorAdd(@ColorInt int add) {
+        if (mAdd != add) {
+            mAdd = add;
+            discardNativeInstance();
+        }
     }
 
-    private void update() {
-        destroyFilter(native_instance);
-        native_instance = native_CreateLightingFilter(mMul, mAdd);
+    @Override
+    long createNativeInstance() {
+        return native_CreateLightingFilter(mMul, mAdd);
     }
 
     private static native long native_CreateLightingFilter(int mul, int add);
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index b1d51ec..5d6aa8a 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -42,7 +42,8 @@
 public class Paint {
 
     private long mNativePaint;
-    private long mNativeShader = 0;
+    private long mNativeShader;
+    private long mNativeColorFilter;
 
     // The approximate size of a native paint object.
     private static final long NATIVE_PAINT_SIZE = 98;
@@ -584,6 +585,11 @@
             mNativeShader = newNativeShader;
             nSetShader(mNativePaint, mNativeShader);
         }
+        long newNativeColorFilter = mColorFilter == null ? 0 : mColorFilter.getNativeInstance();
+        if (newNativeColorFilter != mNativeColorFilter) {
+            mNativeColorFilter = newNativeColorFilter;
+            nSetColorFilter(mNativePaint, mNativeColorFilter);
+        }
         return mNativePaint;
     }
 
@@ -1044,10 +1050,13 @@
      * @return       filter
      */
     public ColorFilter setColorFilter(ColorFilter filter) {
-        long filterNative = 0;
-        if (filter != null)
-            filterNative = filter.native_instance;
-        nSetColorFilter(mNativePaint, filterNative);
+        // If mColorFilter changes, cached value of native shader aren't valid, since
+        // old shader's pointer may be reused by another shader allocation later
+        if (mColorFilter != filter) {
+            mNativeColorFilter = -1;
+        }
+
+        // Defer setting the filter natively until getNativeInstance() is called
         mColorFilter = filter;
         return filter;
     }
@@ -2294,7 +2303,7 @@
             throw new IndexOutOfBoundsException();
         }
 
-        return nGetTextRunCursor(mNativePaint, text,
+        return nGetTextRunCursor(mNativePaint, mNativeTypeface, text,
                 contextStart, contextLength, dir, offset, cursorOpt);
     }
 
@@ -2380,7 +2389,7 @@
             throw new IndexOutOfBoundsException();
         }
 
-        return nGetTextRunCursor(mNativePaint, text,
+        return nGetTextRunCursor(mNativePaint, mNativeTypeface, text,
                 contextStart, contextEnd, dir, offset, cursorOpt);
     }
 
@@ -2686,9 +2695,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/PorterDuffColorFilter.java b/graphics/java/android/graphics/PorterDuffColorFilter.java
index 69d6891..ccc6ead 100644
--- a/graphics/java/android/graphics/PorterDuffColorFilter.java
+++ b/graphics/java/android/graphics/PorterDuffColorFilter.java
@@ -24,6 +24,7 @@
  * color and a specific {@link PorterDuff Porter-Duff composite mode}.
  */
 public class PorterDuffColorFilter extends ColorFilter {
+    @ColorInt
     private int mColor;
     private PorterDuff.Mode mMode;
 
@@ -40,7 +41,6 @@
     public PorterDuffColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
         mColor = color;
         mMode = mode;
-        update();
     }
 
     /**
@@ -49,9 +49,8 @@
      *
      * @see Color
      * @see #setColor(int)
-     *
-     * @hide
      */
+    @ColorInt
     public int getColor() {
         return mColor;
     }
@@ -65,12 +64,12 @@
      * @see Color
      * @see #getColor()
      * @see #getMode()
-     *
-     * @hide
      */
-    public void setColor(int color) {
-        mColor = color;
-        update();
+    public void setColor(@ColorInt int color) {
+        if (mColor != color) {
+            mColor = color;
+            discardNativeInstance();
+        }
     }
 
     /**
@@ -79,8 +78,6 @@
      *
      * @see PorterDuff
      * @see #setMode(android.graphics.PorterDuff.Mode)
-     *
-     * @hide
      */
     public PorterDuff.Mode getMode() {
         return mMode;
@@ -93,17 +90,18 @@
      * @see PorterDuff
      * @see #getMode()
      * @see #getColor()
-     *
-     * @hide
      */
     public void setMode(@NonNull PorterDuff.Mode mode) {
+        if (mode == null) {
+            throw new IllegalArgumentException("mode must be non-null");
+        }
         mMode = mode;
-        update();
+        discardNativeInstance();
     }
 
-    private void update() {
-        destroyFilter(native_instance);
-        native_instance = native_CreatePorterDuffFilter(mColor, mMode.nativeInt);
+    @Override
+    long createNativeInstance() {
+        return native_CreatePorterDuffFilter(mColor, mMode.nativeInt);
     }
 
     @Override
@@ -115,10 +113,7 @@
             return false;
         }
         final PorterDuffColorFilter other = (PorterDuffColorFilter) object;
-        if (mColor != other.mColor || mMode != other.mMode) {
-            return false;
-        }
-        return true;
+        return (mColor == other.mColor && mMode.nativeInt == other.mMode.nativeInt);
     }
 
     @Override
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index b584e0d..8410ab2 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -106,8 +106,10 @@
     }
 
     void discardNativeInstance() {
-        nativeSafeUnref(mNativeInstance);
-        mNativeInstance = 0;
+        if (mNativeInstance != 0) {
+            nativeSafeUnref(mNativeInstance);
+            mNativeInstance = 0;
+        }
     }
 
     /**
@@ -120,7 +122,9 @@
     @Override
     protected void finalize() throws Throwable {
         try {
-            nativeSafeUnref(mNativeInstance);
+            if (mNativeInstance != 0) {
+                nativeSafeUnref(mNativeInstance);
+            }
             mNativeInstance = -1;
         } finally {
             super.finalize();
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/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index 0722c18..c6c9271 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -218,6 +218,8 @@
     }
 
     /**
+     * Only call this method after bound is set on this drawable.
+     *
      * @return the mask path object used to clip the drawable
      */
     public Path getIconMask() {
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 3a12419..a1539b8 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -333,7 +333,7 @@
         // Color filters always override tint filters.
         final ColorFilter colorFilter = (mColorFilter == null ? mTintFilter : mColorFilter);
         final long colorFilterNativeInstance = colorFilter == null ? 0 :
-                colorFilter.native_instance;
+                colorFilter.getNativeInstance();
         boolean canReuseCache = mVectorState.canReuseCache();
         int pixelCount = nDraw(mVectorState.getNativeRenderer(), canvas.getNativeCanvasWrapper(),
                 colorFilterNativeInstance, mTmpBounds, needMirroring(),
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index b8d95e4..359cfac 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -4765,7 +4765,6 @@
                     && (targetTypeLen = attrPrivate.size())
             );
         }
-        break;
     }
     return 0;
 }
diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h
index 8e9741e..94a2a14 100644
--- a/libs/androidfw/tests/data/basic/R.h
+++ b/libs/androidfw/tests/data/basic/R.h
@@ -44,8 +44,8 @@
       density = 0x7f030002,
 
       // From feature
-      test3 = 0x7f080000,
-      test4 = 0x7f080001,
+      test3 = 0x80020000,
+      test4 = 0x80020001,
     };
   };
 
@@ -57,7 +57,7 @@
       ref2 = 0x7f040003,
 
       // From feature
-      number3 = 0x7f090000,
+      number3 = 0x80030000,
     };
   };
 
diff --git a/libs/androidfw/tests/data/feature/AndroidManifest.xml b/libs/androidfw/tests/data/feature/AndroidManifest.xml
index c972372..12ca5b6 100644
--- a/libs/androidfw/tests/data/feature/AndroidManifest.xml
+++ b/libs/androidfw/tests/data/feature/AndroidManifest.xml
@@ -15,5 +15,6 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.basic">
+    package="com.android.basic"
+    featureName="feature">
 </manifest>
diff --git a/libs/androidfw/tests/data/feature/build b/libs/androidfw/tests/data/feature/build
index 6ed3e41..aa2f716 100755
--- a/libs/androidfw/tests/data/feature/build
+++ b/libs/androidfw/tests/data/feature/build
@@ -19,4 +19,10 @@
 
 PATH_TO_FRAMEWORK_RES=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/android.jar
 
-aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES --feature-of ../basic/basic.apk -F feature.apk -f
+aapt2 compile --dir res -o compiled.flata
+aapt2 link -o feature.apk \
+    --manifest AndroidManifest.xml \
+    -I $PATH_TO_FRAMEWORK_RES \
+    -I ../basic/basic.apk \
+    --package-id 0x80 \
+    compiled.flata
diff --git a/libs/androidfw/tests/data/feature/feature.apk b/libs/androidfw/tests/data/feature/feature.apk
index 767fed6..a0dae38 100644
--- a/libs/androidfw/tests/data/feature/feature.apk
+++ b/libs/androidfw/tests/data/feature/feature.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/feature/res/values/values.xml b/libs/androidfw/tests/data/feature/res/values/values.xml
index 59f7d93..43e1517 100644
--- a/libs/androidfw/tests/data/feature/res/values/values.xml
+++ b/libs/androidfw/tests/data/feature/res/values/values.xml
@@ -15,13 +15,12 @@
 -->
 
 <resources>
-    <!-- Features are offset, so 7f020000 will become 7f080000 at runtime. -->
-    <public type="string" name="test3" id="0x7f020000" />
+    <public type="string" name="test3" id="0x80020000" />
     <string name="test3">test3</string>
 
-    <public type="string" name="test4" id="0x7f020001" />
+    <public type="string" name="test4" id="0x80020001" />
     <string name="test4">test4</string>
 
-    <public type="integer" name="number3" id="0x7f030000" />
+    <public type="integer" name="number3" id="0x80030000" />
     <integer name="number3">200</integer>
 </resources>
diff --git a/libs/common_time/Android.mk b/libs/common_time/Android.mk
index 1fec504..636f057 100644
--- a/libs/common_time/Android.mk
+++ b/libs/common_time/Android.mk
@@ -15,7 +15,8 @@
     clock_recovery.cpp \
     common_clock.cpp \
     main.cpp \
-    utils.cpp
+    utils.cpp \
+    LinearTransform.cpp
 
 # Uncomment to enable vesbose logging and debug service.
 #TIME_SERVICE_DEBUG=true
diff --git a/libs/common_time/LinearTransform.cpp b/libs/common_time/LinearTransform.cpp
new file mode 100644
index 0000000..6730855
--- /dev/null
+++ b/libs/common_time/LinearTransform.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      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.
+ */
+
+#define __STDC_LIMIT_MACROS
+
+#include "LinearTransform.h"
+#include <assert.h>
+
+
+// disable sanitize as these functions may intentionally overflow (see comments below).
+// the ifdef can be removed when host builds use clang.
+#if defined(__clang__)
+#define ATTRIBUTE_NO_SANITIZE_INTEGER __attribute__((no_sanitize("integer")))
+#else
+#define ATTRIBUTE_NO_SANITIZE_INTEGER
+#endif
+
+namespace android {
+
+// sanitize failure with T = int32_t and x = 0x80000000
+template<class T>
+ATTRIBUTE_NO_SANITIZE_INTEGER
+static inline T ABS(T x) { return (x < 0) ? -x : x; }
+
+// Static math methods involving linear transformations
+// remote sanitize failure on overflow case.
+ATTRIBUTE_NO_SANITIZE_INTEGER
+static bool scale_u64_to_u64(
+        uint64_t val,
+        uint32_t N,
+        uint32_t D,
+        uint64_t* res,
+        bool round_up_not_down) {
+    uint64_t tmp1, tmp2;
+    uint32_t r;
+
+    assert(res);
+    assert(D);
+
+    // Let U32(X) denote a uint32_t containing the upper 32 bits of a 64 bit
+    // integer X.
+    // Let L32(X) denote a uint32_t containing the lower 32 bits of a 64 bit
+    // integer X.
+    // Let X[A, B] with A <= B denote bits A through B of the integer X.
+    // Let (A | B) denote the concatination of two 32 bit ints, A and B.
+    // IOW X = (A | B) => U32(X) == A && L32(X) == B
+    //
+    // compute M = val * N (a 96 bit int)
+    // ---------------------------------
+    // tmp2 = U32(val) * N (a 64 bit int)
+    // tmp1 = L32(val) * N (a 64 bit int)
+    // which means
+    // M = val * N = (tmp2 << 32) + tmp1
+    tmp2 = (val >> 32) * N;
+    tmp1 = (val & UINT32_MAX) * N;
+
+    // compute M[32, 95]
+    // tmp2 = tmp2 + U32(tmp1)
+    //      = (U32(val) * N) + U32(L32(val) * N)
+    //      = M[32, 95]
+    tmp2 += tmp1 >> 32;
+
+    // if M[64, 95] >= D, then M/D has bits > 63 set and we have
+    // an overflow.
+    if ((tmp2 >> 32) >= D) {
+        *res = UINT64_MAX;
+        return false;
+    }
+
+    // Divide.  Going in we know
+    // tmp2 = M[32, 95]
+    // U32(tmp2) < D
+    r = tmp2 % D;
+    tmp2 /= D;
+
+    // At this point
+    // tmp1      = L32(val) * N
+    // tmp2      = M[32, 95] / D
+    //           = (M / D)[32, 95]
+    // r         = M[32, 95] % D
+    // U32(tmp2) = 0
+    //
+    // compute tmp1 = (r | M[0, 31])
+    tmp1 = (tmp1 & UINT32_MAX) | ((uint64_t)r << 32);
+
+    // Divide again.  Keep the remainder around in order to round properly.
+    r = tmp1 % D;
+    tmp1 /= D;
+
+    // At this point
+    // tmp2      = (M / D)[32, 95]
+    // tmp1      = (M / D)[ 0, 31]
+    // r         =  M % D
+    // U32(tmp1) = 0
+    // U32(tmp2) = 0
+
+    // Pack the result and deal with the round-up case (As well as the
+    // remote possiblility over overflow in such a case).
+    *res = (tmp2 << 32) | tmp1;
+    if (r && round_up_not_down) {
+        ++(*res);
+        if (!(*res)) {
+            *res = UINT64_MAX;
+            return false;
+        }
+    }
+
+    return true;
+}
+
+// at least one known sanitize failure (see comment below)
+ATTRIBUTE_NO_SANITIZE_INTEGER
+static bool linear_transform_s64_to_s64(
+        int64_t  val,
+        int64_t  basis1,
+        int32_t  N,
+        uint32_t D,
+        bool     invert_frac,
+        int64_t  basis2,
+        int64_t* out) {
+    uint64_t scaled, res;
+    uint64_t abs_val;
+    bool is_neg;
+
+    if (!out)
+        return false;
+
+    // Compute abs(val - basis_64). Keep track of whether or not this delta
+    // will be negative after the scale opertaion.
+    if (val < basis1) {
+        is_neg = true;
+        abs_val = basis1 - val;
+    } else {
+        is_neg = false;
+        abs_val = val - basis1;
+    }
+
+    if (N < 0)
+        is_neg = !is_neg;
+
+    if (!scale_u64_to_u64(abs_val,
+                          invert_frac ? D : ABS(N),
+                          invert_frac ? ABS(N) : D,
+                          &scaled,
+                          is_neg))
+        return false; // overflow/undeflow
+
+    // if scaled is >= 0x8000<etc>, then we are going to overflow or
+    // underflow unless ABS(basis2) is large enough to pull us back into the
+    // non-overflow/underflow region.
+    if (scaled & INT64_MIN) {
+        if (is_neg && (basis2 < 0))
+            return false; // certain underflow
+
+        if (!is_neg && (basis2 >= 0))
+            return false; // certain overflow
+
+        if (ABS(basis2) <= static_cast<int64_t>(scaled & INT64_MAX))
+            return false; // not enough
+
+        // Looks like we are OK
+        *out = (is_neg ? (-scaled) : scaled) + basis2;
+    } else {
+        // Scaled fits within signed bounds, so we just need to check for
+        // over/underflow for two signed integers.  Basically, if both scaled
+        // and basis2 have the same sign bit, and the result has a different
+        // sign bit, then we have under/overflow.  An easy way to compute this
+        // is
+        // (scaled_signbit XNOR basis_signbit) &&
+        // (scaled_signbit XOR res_signbit)
+        // ==
+        // (scaled_signbit XOR basis_signbit XOR 1) &&
+        // (scaled_signbit XOR res_signbit)
+
+        if (is_neg)
+            scaled = -scaled; // known sanitize failure
+        res = scaled + basis2;
+
+        if ((scaled ^ basis2 ^ INT64_MIN) & (scaled ^ res) & INT64_MIN)
+            return false;
+
+        *out = res;
+    }
+
+    return true;
+}
+
+bool LinearTransform::doForwardTransform(int64_t a_in, int64_t* b_out) const {
+    if (0 == a_to_b_denom)
+        return false;
+
+    return linear_transform_s64_to_s64(a_in,
+                                       a_zero,
+                                       a_to_b_numer,
+                                       a_to_b_denom,
+                                       false,
+                                       b_zero,
+                                       b_out);
+}
+
+bool LinearTransform::doReverseTransform(int64_t b_in, int64_t* a_out) const {
+    if (0 == a_to_b_numer)
+        return false;
+
+    return linear_transform_s64_to_s64(b_in,
+                                       b_zero,
+                                       a_to_b_numer,
+                                       a_to_b_denom,
+                                       true,
+                                       a_zero,
+                                       a_out);
+}
+
+template <class T> void LinearTransform::reduce(T* N, T* D) {
+    T a, b;
+    if (!N || !D || !(*D)) {
+        assert(false);
+        return;
+    }
+
+    a = *N;
+    b = *D;
+
+    if (a == 0) {
+        *D = 1;
+        return;
+    }
+
+    // This implements Euclid's method to find GCD.
+    if (a < b) {
+        T tmp = a;
+        a = b;
+        b = tmp;
+    }
+
+    while (1) {
+        // a is now the greater of the two.
+        const T remainder = a % b;
+        if (remainder == 0) {
+            *N /= b;
+            *D /= b;
+            return;
+        }
+        // by swapping remainder and b, we are guaranteeing that a is
+        // still the greater of the two upon entrance to the loop.
+        a = b;
+        b = remainder;
+    }
+};
+
+template void LinearTransform::reduce<uint64_t>(uint64_t* N, uint64_t* D);
+template void LinearTransform::reduce<uint32_t>(uint32_t* N, uint32_t* D);
+
+// sanitize failure if *N = 0x80000000
+ATTRIBUTE_NO_SANITIZE_INTEGER
+void LinearTransform::reduce(int32_t* N, uint32_t* D) {
+    if (N && D && *D) {
+        if (*N < 0) {
+            *N = -(*N);
+            reduce(reinterpret_cast<uint32_t*>(N), D);
+            *N = -(*N);
+        } else {
+            reduce(reinterpret_cast<uint32_t*>(N), D);
+        }
+    }
+}
+
+}  // namespace android
diff --git a/libs/common_time/LinearTransform.h b/libs/common_time/LinearTransform.h
new file mode 100644
index 0000000..bf6ab8e
--- /dev/null
+++ b/libs/common_time/LinearTransform.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LINEAR_TRANSFORM_H
+#define _LINEAR_TRANSFORM_H
+
+#include <stdint.h>
+
+namespace android {
+
+// LinearTransform defines a structure which hold the definition of a
+// transformation from single dimensional coordinate system A into coordinate
+// system B (and back again).  Values in A and in B are 64 bit, the linear
+// scale factor is expressed as a rational number using two 32 bit values.
+//
+// Specifically, let
+// f(a) = b
+// F(b) = f^-1(b) = a
+// then
+//
+// f(a) = (((a - a_zero) * a_to_b_numer) / a_to_b_denom) + b_zero;
+//
+// and
+//
+// F(b) = (((b - b_zero) * a_to_b_denom) / a_to_b_numer) + a_zero;
+//
+struct LinearTransform {
+  int64_t  a_zero;
+  int64_t  b_zero;
+  int32_t  a_to_b_numer;
+  uint32_t a_to_b_denom;
+
+  // Transform from A->B
+  // Returns true on success, or false in the case of a singularity or an
+  // overflow.
+  bool doForwardTransform(int64_t a_in, int64_t* b_out) const;
+
+  // Transform from B->A
+  // Returns true on success, or false in the case of a singularity or an
+  // overflow.
+  bool doReverseTransform(int64_t b_in, int64_t* a_out) const;
+
+  // Helpers which will reduce the fraction N/D using Euclid's method.
+  template <class T> static void reduce(T* N, T* D);
+  static void reduce(int32_t* N, uint32_t* D);
+};
+
+
+}
+
+#endif  // _LINEAR_TRANSFORM_H
diff --git a/libs/common_time/clock_recovery.h b/libs/common_time/clock_recovery.h
index 278a75e..8066a39 100644
--- a/libs/common_time/clock_recovery.h
+++ b/libs/common_time/clock_recovery.h
@@ -19,9 +19,10 @@
 
 #include <stdint.h>
 #include <common_time/ICommonClock.h>
-#include <utils/LinearTransform.h>
 #include <utils/threads.h>
 
+#include "LinearTransform.h"
+
 #ifdef TIME_SERVICE_DEBUG
 #include "diag_thread.h"
 #endif
diff --git a/libs/common_time/common_clock.cpp b/libs/common_time/common_clock.cpp
index ee326e1..aed52f1 100644
--- a/libs/common_time/common_clock.cpp
+++ b/libs/common_time/common_clock.cpp
@@ -23,7 +23,6 @@
 #include <stdint.h>
 
 #include <utils/Errors.h>
-#include <utils/LinearTransform.h>
 
 #include "common_clock.h"
 
diff --git a/libs/common_time/common_clock.h b/libs/common_time/common_clock.h
index b786fdc..5e4e5f5 100644
--- a/libs/common_time/common_clock.h
+++ b/libs/common_time/common_clock.h
@@ -20,9 +20,10 @@
 #include <stdint.h>
 
 #include <utils/Errors.h>
-#include <utils/LinearTransform.h>
 #include <utils/threads.h>
 
+#include "LinearTransform.h"
+
 namespace android {
 
 class CommonClock {
diff --git a/libs/hwui/FloatColor.h b/libs/hwui/FloatColor.h
index d8afa35..a738ba4 100644
--- a/libs/hwui/FloatColor.h
+++ b/libs/hwui/FloatColor.h
@@ -38,13 +38,14 @@
     }
 
     // "color" is a gamma-encoded sRGB color
-    // After calling this method, the color is stored as a linear color. The color
-    // is not pre-multiplied.
-    void setUnPreMultipliedSRGB(uint32_t color) {
+    // After calling this method, the color is stored as a un-premultiplied linear color
+    // if linear blending is enabled. Otherwise, the color is stored as a un-premultiplied
+    // gamma-encoded sRGB color
+    void setUnPreMultiplied(uint32_t color) {
         a = ((color >> 24) & 0xff) / 255.0f;
-        r = EOCF_sRGB(((color >> 16) & 0xff) / 255.0f);
-        g = EOCF_sRGB(((color >>  8) & 0xff) / 255.0f);
-        b = EOCF_sRGB(((color      ) & 0xff) / 255.0f);
+        r = EOCF(((color >> 16) & 0xff) / 255.0f);
+        g = EOCF(((color >>  8) & 0xff) / 255.0f);
+        b = EOCF(((color      ) & 0xff) / 255.0f);
     }
 
     bool isNotBlack() {
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 18bfcc2..dceb285 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -189,9 +189,9 @@
         float amount, uint8_t*& dst) const {
     float oppAmount = 1.0f - amount;
     float a = start.a * oppAmount + end.a * amount;
-    *dst++ = uint8_t(a * OECF_sRGB((start.r * oppAmount + end.r * amount)) * 255.0f);
-    *dst++ = uint8_t(a * OECF_sRGB((start.g * oppAmount + end.g * amount)) * 255.0f);
-    *dst++ = uint8_t(a * OECF_sRGB((start.b * oppAmount + end.b * amount)) * 255.0f);
+    *dst++ = uint8_t(a * OECF(start.r * oppAmount + end.r * amount) * 255.0f);
+    *dst++ = uint8_t(a * OECF(start.g * oppAmount + end.g * amount) * 255.0f);
+    *dst++ = uint8_t(a * OECF(start.b * oppAmount + end.b * amount) * 255.0f);
     *dst++ = uint8_t(a * 255.0f);
 }
 
@@ -201,17 +201,14 @@
     float a = start.a * oppAmount + end.a * amount;
     float* d = (float*) dst;
 #ifdef ANDROID_ENABLE_LINEAR_BLENDING
+    // We want to stay linear
     *d++ = a * (start.r * oppAmount + end.r * amount);
     *d++ = a * (start.g * oppAmount + end.g * amount);
     *d++ = a * (start.b * oppAmount + end.b * amount);
 #else
-    // What we're doing to the alpha channel here is technically incorrect
-    // but reproduces Android's old behavior when the alpha was pre-multiplied
-    // with gamma-encoded colors
-    a = EOCF_sRGB(a);
-    *d++ = a * OECF_sRGB(start.r * oppAmount + end.r * amount);
-    *d++ = a * OECF_sRGB(start.g * oppAmount + end.g * amount);
-    *d++ = a * OECF_sRGB(start.b * oppAmount + end.b * amount);
+    *d++ = a * OECF(start.r * oppAmount + end.r * amount);
+    *d++ = a * OECF(start.g * oppAmount + end.g * amount);
+    *d++ = a * OECF(start.b * oppAmount + end.b * amount);
 #endif
     *d++ = a;
     dst += 4 * sizeof(float);
@@ -232,10 +229,10 @@
     ChannelMixer mix = gMixers[mUseFloatTexture];
 
     FloatColor start;
-    start.setUnPreMultipliedSRGB(colors[0]);
+    start.setUnPreMultiplied(colors[0]);
 
     FloatColor end;
-    end.setUnPreMultipliedSRGB(colors[1]);
+    end.setUnPreMultiplied(colors[1]);
 
     int currentPos = 1;
     float startPos = positions[0];
@@ -250,7 +247,7 @@
 
             currentPos++;
 
-            end.setUnPreMultipliedSRGB(colors[currentPos]);
+            end.setUnPreMultiplied(colors[currentPos]);
             distance = positions[currentPos] - startPos;
         }
 
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 40ab778..38c23e4 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) {
@@ -202,23 +202,26 @@
 )__SHADER__";
 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"
-        "}\n"
-        "\nvec4 gammaMix(const vec4 a, const vec4 b, float v) {\n"
-        "    vec4 c = mix(a, b, v);\n"
-        "    c.a = EOTF_sRGB(c.a);\n" // This is technically incorrect but preserves compatibility
-        "    return vec4(OETF_sRGB(c.rgb) * c.a, c.a);\n"
-        "}\n",
+        R"__SHADER__(
+        vec4 dither(const vec4 color) {
+            return color + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0);
+        }
+        vec4 gradientMix(const vec4 a, const vec4 b, float v) {
+            vec4 c = mix(a, b, v);
+            return vec4(c.rgb * c.a, c.a);
+        }
+        )__SHADER__",
         // sRGB framebuffer
-        "\nvec4 dither(const vec4 color) {\n"
-        "    vec3 dithered = sqrt(color.rgb) + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0);\n"
-        "    return vec4(dithered * dithered, color.a);\n"
-        "}\n"
-        "\nvec4 gammaMix(const vec4 a, const vec4 b, float v) {\n"
-        "    vec4 c = mix(a, b, v);\n"
-        "    return vec4(c.rgb * c.a, c.a);\n"
-        "}\n"
+        R"__SHADER__(
+        vec4 dither(const vec4 color) {
+            vec3 dithered = sqrt(color.rgb) + (triangleNoise(gl_FragCoord.xy * screenSize.xy) / 255.0);
+            return vec4(dithered * dithered, color.a);
+        }
+        vec4 gradientMixMix(const vec4 a, const vec4 b, float v) {
+            vec4 c = mix(a, b, v);
+            return vec4(c.rgb * c.a, c.a);
+        }
+        )__SHADER__",
 };
 
 // Uses luminance coefficients from Rec.709 to choose the appropriate gamma
@@ -272,19 +275,19 @@
         // Linear
         "    vec4 gradientColor = texture2D(gradientSampler, linear);\n",
 
-        "    vec4 gradientColor = gammaMix(startColor, endColor, clamp(linear, 0.0, 1.0));\n",
+        "    vec4 gradientColor = gradientMix(startColor, endColor, clamp(linear, 0.0, 1.0));\n",
 
         // Circular
         "    vec4 gradientColor = texture2D(gradientSampler, vec2(length(circular), 0.5));\n",
 
-        "    vec4 gradientColor = gammaMix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n",
+        "    vec4 gradientColor = gradientMix(startColor, endColor, clamp(length(circular), 0.0, 1.0));\n",
 
         // Sweep
         "    highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
         "    vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n",
 
         "    highp float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n"
-        "    vec4 gradientColor = gammaMix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n"
+        "    vec4 gradientColor = gradientMix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n"
 };
 const char* gFS_Main_FetchBitmap =
         "    vec4 bitmapColor = OETF(texture2D(bitmapSampler, outBitmapTexCoords));\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/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index 760c10c..4f7f9d7 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -173,8 +173,8 @@
         outData->gradientSampler = 0;
         outData->gradientTexture = nullptr;
 
-        outData->startColor.setUnPreMultipliedSRGB(gradInfo.fColors[0]);
-        outData->endColor.setUnPreMultipliedSRGB(gradInfo.fColors[1]);
+        outData->startColor.setUnPreMultiplied(gradInfo.fColors[0]);
+        outData->endColor.setUnPreMultiplied(gradInfo.fColors[1]);
     }
 
     return true;
diff --git a/libs/hwui/VkLayer.cpp b/libs/hwui/VkLayer.cpp
index 537b3ea..ef4784b 100644
--- a/libs/hwui/VkLayer.cpp
+++ b/libs/hwui/VkLayer.cpp
@@ -29,7 +29,7 @@
     SkImageInfo info = SkImageInfo::MakeS32(mWidth, mHeight, kPremul_SkAlphaType);
     surface = SkSurface::MakeRenderTarget(mRenderState.getGrContext(), SkBudgeted::kNo, info);
     surface->getCanvas()->clear(SK_ColorBLUE);
-    mImage = surface->makeImageSnapshot(SkBudgeted::kNo);
+    mImage = surface->makeImageSnapshot();
 }
 
 void VkLayer::onVkContextDestroyed() {
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..c28aa5e 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -1807,6 +1807,9 @@
          * {@link #TYPE_S_DMB}, and
          * {@link #TYPE_T_DMB}.
          *
+         * <p>This value cannot be changed once it's set. Trying to modify it will make the update
+         * fail.
+         *
          * <p>This is a required field.
          *
          * <p>Type: TEXT
@@ -2679,6 +2682,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 f904600..f9342b7 100644
--- a/packages/CarrierDefaultApp/res/values/strings.xml
+++ b/packages/CarrierDefaultApp/res/values/strings.xml
@@ -6,9 +6,8 @@
     <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/drawer/DashboardCategory.java b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
index 3fc999f..f6f8168 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
@@ -48,7 +48,7 @@
     /**
      * List of the category's children
      */
-    public List<Tile> tiles = new ArrayList<Tile>();
+    public List<Tile> tiles = new ArrayList<>();
 
 
     public DashboardCategory() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 457ce76..af247bd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -30,7 +30,6 @@
 import android.provider.Settings;
 import android.util.ArraySet;
 import android.util.Log;
-import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -40,10 +39,8 @@
 import android.widget.Toolbar;
 
 import com.android.settingslib.R;
-import com.android.settingslib.applications.InterestingConfigChanges;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 
 public class SettingsDrawerActivity extends Activity {
@@ -63,15 +60,6 @@
 
     private FrameLayout mContentHeaderContainer;
 
-    // Remove below after new IA
-    @Deprecated
-    private static List<DashboardCategory> sDashboardCategories;
-    @Deprecated
-    private static HashMap<Pair<String, String>, Tile> sTileCache;
-    @Deprecated
-    private static InterestingConfigChanges sConfigTracker;
-    // Remove above after new IA
-
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -175,17 +163,6 @@
         getActionBar().setDisplayHomeAsUpEnabled(true);
     }
 
-    public List<DashboardCategory> getDashboardCategories() {
-        if (sDashboardCategories == null) {
-            sTileCache = new HashMap<>();
-            sConfigTracker = new InterestingConfigChanges();
-            // Apply initial current config.
-            sConfigTracker.applyNewConfig(getResources());
-            sDashboardCategories = TileUtils.getCategories(this, sTileCache);
-        }
-        return sDashboardCategories;
-    }
-
     protected void onCategoriesChanged() {
         final int N = mCategoryListeners.size();
         for (int i = 0; i < N; 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..fed48b4 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,62 @@
         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 averaged with the previous
+     * value. 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;
+        if (rssi != UNREACHABLE_RSSI && mRssi != UNREACHABLE_RSSI) {
+            mRssi = (mRssi + rssi) / 2; // half-life previous value
+        } else {
+            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 +438,7 @@
             }
         }
 
-        return seen;
+        mSeen = seen;
     }
 
     public NetworkInfo getNetworkInfo() {
@@ -822,13 +863,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 +888,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 +917,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 +936,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..a9aaa05 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -43,10 +43,6 @@
     private static final int[] STATE_SECURED = {
             R.attr.state_encrypted
     };
-    private static final int[] STATE_SAVED = {
-            R.attr.state_encrypted,
-            R.attr.state_saved
-    };
 
     private static final int[] wifi_friction_attributes = { R.attr.wifi_friction };
 
@@ -152,10 +148,8 @@
         }
         view.itemView.setContentDescription(mContentDescription);
 
-        if (!mForSavedNetworks) {
-            ImageView frictionImageView = (ImageView) view.findViewById(R.id.friction_icon);
-            bindFrictionImage(frictionImageView);
-        }
+        ImageView frictionImageView = (ImageView) view.findViewById(R.id.friction_icon);
+        bindFrictionImage(frictionImageView);
     }
 
     protected void updateIcon(int level, Context context) {
@@ -184,11 +178,7 @@
             return;
         }
         if (mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) {
-            if (mAccessPoint.isSaved()) {
-                mFrictionSld.setState(STATE_SAVED);
-            } else {
-                mFrictionSld.setState(STATE_SECURED);
-            }
+            mFrictionSld.setState(STATE_SECURED);
         }
         Drawable drawable = mFrictionSld.getCurrent();
         frictionImageView.setImageDrawable(drawable);
@@ -223,8 +213,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..b9b4ef3 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,58 @@
         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);
+    }
+
+    @Test
+    public void testUpdateWithScanResultShouldAverageRssi() {
+        String ssid = "ssid";
+        int originalRssi = -65;
+        int newRssi = -80;
+        int expectedRssi = (originalRssi + newRssi) / 2;
+        AccessPoint ap =
+                new TestAccessPointBuilder(mContext).setSsid(ssid).setRssi(originalRssi).build();
+
+        ScanResult scanResult = new ScanResult();
+        scanResult.SSID = ssid;
+        scanResult.level = newRssi;
+        scanResult.BSSID = "bssid";
+        scanResult.timestamp = SystemClock.elapsedRealtime() * 1000;
+        scanResult.capabilities = "";
+        assertThat(ap.update(scanResult)).isTrue();
+
+        assertThat(ap.getRssi()).isEqualTo(expectedRssi);
+    }
+
+    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;
+            scanResult.capabilities = "";
+            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/integ/src/com/android/settingslib/wifi/TestAccessPointBuilder.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
index 665c439..81bd723 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
@@ -73,25 +73,28 @@
         return this;
     }
 
+    public TestAccessPointBuilder setRssi(int rssi) {
+        mRssi = rssi;
+        return this;
+    }
+
     /**
-    * Set the signal level.
-    * Side effect: if this AccessPoint was previously unreachable,
+    * Set the rssi based upon the desired signal level.
+     *
+    * <p>Side effect: if this AccessPoint was previously unreachable,
     * setting the level will also make it reachable.
     */
     public TestAccessPointBuilder setLevel(int level) {
-        int outputRange = AccessPoint.SIGNAL_LEVELS - 1;
-
-        if (level > outputRange) {
-            level = outputRange;
-        } else if (level < 0) {
-            level = 0;
+        // Reversal of WifiManager.calculateSignalLevels
+        if (level == 0) {
+            mRssi = MIN_RSSI;
+        } else if (level >= AccessPoint.SIGNAL_LEVELS) {
+            mRssi = MAX_RSSI;
+        } else {
+            float inputRange = MAX_RSSI - MIN_RSSI;
+            float outputRange = AccessPoint.SIGNAL_LEVELS - 1;
+            mRssi = (int) (level * inputRange / outputRange + MIN_RSSI);
         }
-
-        int inputRange = MAX_RSSI - MIN_RSSI;
-
-        // calculate the rssi required to get the level we want.
-        // this is a rearrangement of the formula from WifiManager.calculateSignalLevel()
-        mRssi = (int)((float)(level * inputRange) / (float)outputRange) + MIN_RSSI;
         return this;
     }
 
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/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 91a4e79..1f1c189 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -248,6 +248,9 @@
     @GuardedBy("mLock")
     private HandlerThread mHandlerThread;
 
+    @GuardedBy("mLock")
+    private Handler mHandler;
+
     // We have to call in the user manager with no lock held,
     private volatile UserManager mUserManager;
 
@@ -300,10 +303,13 @@
             mHandlerThread = new HandlerThread(LOG_TAG,
                     Process.THREAD_PRIORITY_BACKGROUND);
             mHandlerThread.start();
+            mHandler = new Handler(mHandlerThread.getLooper());
             mSettingsRegistry = new SettingsRegistry();
         }
-        registerBroadcastReceivers();
-        startWatchingUserRestrictionChanges();
+        mHandler.post(() -> {
+            registerBroadcastReceivers();
+            startWatchingUserRestrictionChanges();
+        });
         ServiceManager.addService("settings", new SettingsService(this));
         return true;
     }
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/color/qs_user_detail_avatar_tint.xml b/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml
index 2b75c36..696e9b1 100644
--- a/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml
+++ b/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml
@@ -17,6 +17,6 @@
   -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false" android:color="@color/qs_tile_disabled_color" />
+    <item android:state_enabled="false" android:color="?android:attr/textColorPrimary" />
     <item android:color="@android:color/transparent" />
-</selector>
\ No newline at end of file
+</selector>
diff --git a/packages/SystemUI/res/drawable/ic_add_circle_qs.xml b/packages/SystemUI/res/drawable/ic_add_circle_qs.xml
index f296076..6415ecb4 100644
--- a/packages/SystemUI/res/drawable/ic_add_circle_qs.xml
+++ b/packages/SystemUI/res/drawable/ic_add_circle_qs.xml
@@ -17,14 +17,15 @@
         android:width="48.0dp"
         android:height="48.0dp"
         android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
     <group
             android:scaleX="1.2"
             android:scaleY="1.2"
             android:pivotX="12.0"
             android:pivotY="12.0">
         <path
-            android:fillColor="@color/qs_user_detail_icon_muted"
+            android:fillColor="#FFFFFFFF"
             android:pathData="M12.000000,2.000000C6.500000,2.000000 2.000000,6.500000 2.000000,12.000000s4.500000,10.000000 10.000000,10.000000c5.500000,0.000000 10.000000,-4.500000 10.000000,-10.000000S17.500000,2.000000 12.000000,2.000000zM17.000000,13.000000l-4.000000,0.000000l0.000000,4.000000l-2.000000,0.000000l0.000000,-4.000000L7.000000,13.000000l0.000000,-2.000000l4.000000,0.000000L11.000000,7.000000l2.000000,0.000000l0.000000,4.000000l4.000000,0.000000L17.000000,13.000000z"/>
     </group>
 </vector>
diff --git a/packages/SystemUI/res/layout/pip_menu_action.xml b/packages/SystemUI/res/layout/pip_menu_action.xml
index 77efc9b..9150a00 100644
--- a/packages/SystemUI/res/layout/pip_menu_action.xml
+++ b/packages/SystemUI/res/layout/pip_menu_action.xml
@@ -18,4 +18,5 @@
     android:layout_width="@dimen/pip_action_size"
     android:layout_height="@dimen/pip_action_size"
     android:padding="@dimen/pip_action_padding"
-    android:background="?android:selectableItemBackgroundBorderless" />
\ No newline at end of file
+    android:background="?android:selectableItemBackgroundBorderless"
+    android:forceHasOverlappingRendering="false" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/pip_menu_activity.xml b/packages/SystemUI/res/layout/pip_menu_activity.xml
index 5e49d05..44ced17 100644
--- a/packages/SystemUI/res/layout/pip_menu_activity.xml
+++ b/packages/SystemUI/res/layout/pip_menu_activity.xml
@@ -15,50 +15,56 @@
 -->
 <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"
+      android:forceHasOverlappingRendering="false">
 
-    <!-- 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/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index 8d1f9e4..9d1fb8f 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -14,24 +14,39 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="match_parent"
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="48dp"
+    android:paddingTop="8dp">
+    <LinearLayout
+        android:id="@+id/label_group"
+        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        android:gravity="center_horizontal"
-        android:paddingTop="8dp"
-        android:paddingBottom="8dp">
-     <TextView android:id="@+id/tile_label"
+        android:layout_alignParentTop="true"
+        android:layout_centerHorizontal="true"
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@+id/tile_label"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textColor="?android:attr/textColorPrimary"
-            android:gravity="center_horizontal"
-            android:minLines="2"
+            android:clickable="false"
+            android:maxLines="2"
             android:padding="0dp"
             android:textAppearance="@style/TextAppearance.QS.TileLabel"
-            android:clickable="false" />
-     <ImageView android:id="@+id/restricted_padlock"
+            android:textColor="?android:attr/textColorPrimary"/>
+
+        <ImageView
+            android:id="@+id/expand_indicator"
+            android:layout_marginStart="4dp"
+            android:layout_width="12dp"
+            android:layout_height="match_parent"
+            android:src="@drawable/qs_dual_tile_caret"
+            android:tint="?android:attr/textColorPrimary" />
+
+        <ImageView android:id="@+id/restricted_padlock"
             android:layout_width="@dimen/qs_tile_text_size"
             android:layout_height="match_parent"
             android:paddingBottom="@dimen/qs_tile_text_size"
@@ -39,4 +54,32 @@
             android:layout_marginLeft="@dimen/restricted_padlock_pading"
             android:scaleType="centerInside"
             android:visibility="gone" />
-</LinearLayout>
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/app_label"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignStart="@id/label_group"
+        android:layout_alignEnd="@id/label_group"
+        android:layout_below="@id/label_group"
+        android:clickable="false"
+        android:maxLines="1"
+        android:padding="0dp"
+        android:visibility="gone"
+        android:gravity="center"
+        android:textAppearance="@style/TextAppearance.QS.TileLabel"
+        android:textColor="?android:attr/textColorPrimary"/>
+
+    <View
+        android:id="@+id/underline"
+        android:layout_width="30dp"
+        android:layout_height="1dp"
+        android:layout_marginTop="2dp"
+        android:layout_alignStart="@id/label_group"
+        android:layout_alignEnd="@id/label_group"
+        android:layout_below="@id/label_group"
+        android:alpha="?android:attr/disabledAlpha"
+        android:background="?android:attr/colorForeground"/>
+
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index 8c6c7cf..c7bfaef 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -53,7 +53,7 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:textSize="@dimen/qs_detail_item_secondary_text_size"
-                android:textColor="@color/qs_user_detail_name"
+                android:textColor="?android:attr/textColorSecondary"
                 android:gravity="center_horizontal" />
         <ImageView
                 android:id="@+id/restricted_padlock"
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..9cb518c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -19,11 +19,13 @@
 import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ACTIONS;
 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_SHOW_MENU;
 import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_STACK_BOUNDS;
 
 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;
@@ -31,11 +33,11 @@
 import android.app.RemoteAction;
 import android.content.Intent;
 import android.content.pm.ParceledListSlice;
-import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.PointF;
 import android.graphics.Rect;
-import android.graphics.drawable.Icon;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -71,13 +73,21 @@
     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 static final float DISABLED_ACTION_ALPHA = 0.54f;
+
     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) {
@@ -337,13 +371,18 @@
                         actionView.setImageDrawable(d);
                     }, mHandler);
                     actionView.setContentDescription(action.getContentDescription());
-                    actionView.setOnClickListener(v -> {
-                        try {
-                            action.getActionIntent().send();
-                        } catch (CanceledException e) {
-                            Log.w(TAG, "Failed to send action", e);
-                        }
-                    });
+                    if (action.isEnabled()) {
+                        actionView.setOnClickListener(v -> {
+                            try {
+                                action.getActionIntent().send();
+                            } catch (CanceledException e) {
+                                Log.w(TAG, "Failed to send action", e);
+                            }
+                        });
+                    } else {
+                        actionView.setAlpha(DISABLED_ACTION_ALPHA);
+                        actionView.setEnabled(false);
+                    }
                     if (isLandscapePip && i > 0) {
                         LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
                                 actionView.getLayoutParams();
@@ -365,12 +404,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/CustomizeTileView.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
index 0629d66..b080642 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.java
@@ -35,19 +35,13 @@
     protected void createLabel() {
         super.createLabel();
         mLabelMinLines = mLabel.getMinLines();
-        View view = LayoutInflater.from(mContext).inflate(R.layout.qs_tile_label, null);
-        mAppLabel = (TextView) view.findViewById(R.id.tile_label);
+        mAppLabel = findViewById(R.id.app_label);
         mAppLabel.setAlpha(.6f);
-        mAppLabel.setSingleLine(true);
-        addView(view);
     }
 
     public void setShowAppLabel(boolean showAppLabel) {
         mAppLabel.setVisibility(showAppLabel ? View.VISIBLE : View.GONE);
         mLabel.setSingleLine(showAppLabel);
-        if (!showAppLabel) {
-            mLabel.setMinLines(mLabelMinLines);
-        }
     }
 
     public void setAppLabel(CharSequence label) {
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..d8e5542 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,8 @@
     }
 
     private final Callback mKeyguardCallback = () -> {
-        if (Dependency.get(KeyguardMonitor.class).isShowing()) {
+        if (!isAttachedToWindow()) return;
+        if (Dependency.get(KeyguardMonitor.class).isShowing() && !mOpening) {
             hide(0, 0);
         }
     };
@@ -237,11 +240,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/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 547bef7..2ac592f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -165,6 +165,7 @@
         }
         TileInfo info = new TileInfo();
         info.state = state;
+        info.state.dualTarget = false; // No dual targets in edit.
         info.state.expandedAccessibilityClassName =
                 Button.class.getName();
         info.spec = spec;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 5ac7891..948954c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -369,7 +369,6 @@
                         Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(
                                 intent, 0);
                     } else {
-                        mAnnounceNextStateChange = true;
                         handleClick();
                     }
                 } else if (msg.what == SECONDARY_CLICK) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index 2c04e82..d2ae6e9f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -36,11 +36,12 @@
 /** View that represents a standard quick settings tile. **/
 public class QSTileView extends QSTileBaseView {
 
-    private final View mDivider;
+    private View mDivider;
     protected TextView mLabel;
     private ImageView mPadLock;
     private int mState;
     private ViewGroup mLabelContainer;
+    private View mExpandIndicator;
 
     public QSTileView(Context context, QSIconView icon) {
         this(context, icon, false);
@@ -54,8 +55,6 @@
 
         setClickable(true);
         setId(View.generateViewId());
-        mDivider = LayoutInflater.from(context).inflate(R.layout.divider, this, false);
-        addView(mDivider);
         createLabel();
         setOrientation(VERTICAL);
         setGravity(Gravity.CENTER);
@@ -81,8 +80,10 @@
                 .inflate(R.layout.qs_tile_label, this, false);
         mLabelContainer.setClipChildren(false);
         mLabelContainer.setClipToPadding(false);
-        mLabel = (TextView) mLabelContainer.findViewById(R.id.tile_label);
-        mPadLock = (ImageView) mLabelContainer.findViewById(R.id.restricted_padlock);
+        mLabel = mLabelContainer.findViewById(R.id.tile_label);
+        mPadLock = mLabelContainer.findViewById(R.id.restricted_padlock);
+        mDivider = mLabelContainer.findViewById(R.id.underline);
+        mExpandIndicator = mLabelContainer.findViewById(R.id.expand_indicator);
 
         addView(mLabelContainer);
     }
@@ -101,6 +102,7 @@
             mLabel.setText(state.label);
         }
         mDivider.setVisibility(state.dualTarget ? View.VISIBLE : View.INVISIBLE);
+        mExpandIndicator.setVisibility(state.dualTarget ? View.VISIBLE : View.GONE);
         if (state.dualTarget != mLabelContainer.isClickable()) {
             mLabelContainer.setClickable(state.dualTarget);
             mLabelContainer.setLongClickable(state.dualTarget);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 47468ae..ac24e2e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -67,6 +67,7 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.util.ArraySet;
+import android.util.LauncherIcons;
 import android.util.Log;
 import android.util.MutableBoolean;
 import android.view.Display;
@@ -142,6 +143,7 @@
     int mDummyThumbnailHeight;
     Paint mBgProtectionPaint;
     Canvas mBgProtectionCanvas;
+    LauncherIcons mLauncherIcons;
 
     private final Handler mHandler = new H();
 
@@ -299,6 +301,7 @@
             Collections.addAll(sRecentsBlacklist,
                     res.getStringArray(R.array.recents_blacklist_array));
         }
+        mLauncherIcons = new LauncherIcons(context);
     }
 
     /**
@@ -834,7 +837,7 @@
             return new ColorDrawable(0xFF666666);
         }
 
-        Drawable icon = info.loadIcon(mPm);
+        Drawable icon = mLauncherIcons.wrapIconDrawableWithShadow(info.loadIcon(mPm));
         return getBadgedIcon(icon, userId);
     }
 
@@ -850,7 +853,7 @@
             return new ColorDrawable(0xFF666666);
         }
 
-        Drawable icon = appInfo.loadIcon(mPm);
+        Drawable icon = mLauncherIcons.wrapIconDrawableWithShadow(appInfo.loadIcon(mPm));
         return getBadgedIcon(icon, userId);
     }
 
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/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index 820638c..c30bb9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -74,7 +74,7 @@
         if (mUserListener == null) {
             return false;
         }
-        return mUserListener.getUserCount() > 1;
+        return mUserListener.getUserCount() != 0;
     }
 
     public void setUserSwitcherController(UserSwitcherController userSwitcherController) {
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 8a3c4e3..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);
         }
@@ -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..ddd8d7b 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) \
@@ -46,10 +47,11 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     metrics-helper-lib \
     android-support-test \
-    mockito-updated-target-minus-junit4 \
+    mockito-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 4250962..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,19 +32,17 @@
 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;
 import com.android.systemui.util.wakelock.WakeLockFake;
 
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -125,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);
@@ -176,7 +172,6 @@
         assertFalse(mWakeLock.isHeld());
     }
 
-    @Ignore("Flaky")
     @Test
     public void transientIndication_releasesWakeLock_afterHidingDelayed() throws Throwable {
         mInstrumentation.runOnMainSync(() -> {
@@ -188,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 25481ce..8a3b9af 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -56,6 +56,12 @@
     // Type for APP_TRANSITION event: The transition brought an already existing activity to the
     // front.
     TYPE_TRANSITION_HOT_LAUNCH = 9;
+
+    // The action was successful
+    TYPE_SUCCESS = 10;
+
+    // The action failed
+    TYPE_FAILURE = 11;
   }
 
   // Known visual elements: views or controls.
@@ -1180,7 +1186,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;
@@ -3227,7 +3233,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
@@ -3601,6 +3607,176 @@
     // 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;
+
+    // ACTION: Phase 1 of instant application resolution occurred
+    // OS: O
+    ACTION_INSTANT_APP_RESOLUTION_PHASE_ONE = 899;
+
+    // ACTION: Phase 2 of instant application resolution occurred
+    // OS: O
+    ACTION_INSTANT_APP_RESOLUTION_PHASE_TWO = 900;
+
+    // FIELD: The amount of time for an ephemeral resolution phase; in milliseconds
+    // OS: O
+    FIELD_INSTANT_APP_RESOLUTION_DELAY_MS = 901;
+
+    // FIELD: The status of an ephemeral resolution phase
+    // Value 0: success
+    // Value 1: no full hash match
+    // OS: O
+    FIELD_INSTANT_APP_RESOLUTION_STATUS = 902;
+
+    // FIELD - A token to identify all events that are part of the same instant application launch
+    // OS: O
+    FIELD_INSTANT_APP_LAUNCH_TOKEN = 903;
+
+    // FIELD - The name of the package responsible for launching the activity
+    // OS: O
+    APP_TRANSITION_CALLING_PACKAGE_NAME = 904;
+
+    // FIELD - Whether or not the launched activity is part of an instant application
+    // OS: O
+    APP_TRANSITION_IS_EPHEMERAL = 905;
+
+    // An autofill session was started
+    // Package: Package of app that is autofilled
+    AUTOFILL_SESSION_STARTED = 906;
+
+    // An autofill request was processed by a service
+    // Type TYPE_SUCCESS: The request succeeded
+    // Type TYPE_FAILURE: The request failed
+    // Package: Package of app that is autofilled
+    // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+    // Tag FIELD_AUTOFILL_NUM_DATASET: The number of datasets returned (only in success case)
+    AUTOFILL_REQUEST = 907;
+
+    // Tag of a field for a package of an autofill service
+    FIELD_AUTOFILL_SERVICE = 908;
+
+    // Tag of a field for the number of datasets
+    FIELD_AUTOFILL_NUM_DATASETS = 909;
+
+    // An autofill dataset selection UI was shown
+    // Type TYPE_DISMISS: UI was explicityly canceled by the user
+    // Type TYPE_CLOSE: UI was destroyed without influence of the user
+    // Type TYPE_ACTION: dataset was selected
+    // Type TYPE_DETAIL: authentication was selected
+    // Package: Package of app that was autofilled
+    // Tag FIELD_AUTOFILL_FILTERTEXT_LEN: The length of the filter text
+    // Tag FIELD_AUTOFILL_NUM_DATASETS: The number of datasets shown
+    AUTOFILL_FILL_UI = 910;
+
+    // Tag of a field for the length of the filter text
+    FIELD_AUTOFILL_FILTERTEXT_LEN = 911;
+
+    // An autofill authentification succeeded
+    // Package: Package of app that was autofilled
+    AUTOFILL_AUTHENTICATED = 912;
+
+    // An activity was autofilled and all values could be applied
+    // Package: Package of app that is autofilled
+    // Tag FIELD_AUTOFILL_NUM_VALUES: Number of values that were suggested to be autofilled
+    // Tag FIELD_AUTOFILL_NUM_VIEWS_FILLED: Number of views that could be filled
+    AUTOFILL_DATASET_APPLIED = 913;
+
+    // Tag of a field for the number values to be filled in
+    FIELD_AUTOFILL_NUM_VALUES = 914;
+
+    // Tag of a field for the number of views that were filled
+    FIELD_AUTOFILL_NUM_VIEWS_FILLED = 915;
+
+    // An autofill save UI was shown
+    // Type TYPE_DISMISS: UI was explicityly canceled by the user
+    // Type TYPE_CLOSE: UI was destroyed without influence of the user
+    // Type TYPE_ACTION: data was saved
+    // Package: Package of app that was autofilled
+    // Tag FIELD_AUTOFILL_NUM_ID: The number of ids that are saved
+    AUTOFILL_SAVE_UI = 916;
+
+    // Tag of a field for the number of saveable ids
+    FIELD_AUTOFILL_NUM_IDS = 917;
+
+    // ACTION: An autofill service was reqiested to save data
+    // Type TYPE_SUCCESS: The request succeeded
+    // Type TYPE_FAILURE: The request failed
+    // Package: Package of app that was autofilled
+    // Tag FIELD_AUTOFILL_SERVICE: Package of service that processed the request
+    AUTOFILL_DATA_SAVE_REQUEST = 918;
+
+    // An auto-fill session was finished
+    // Package: Package of app that was autofilled
+    AUTOFILL_SESSION_FINISHED = 919;
+
     // ---- 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 b56035f..397938a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -416,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)) {
@@ -779,6 +781,7 @@
                 userState.mIsTouchExplorationEnabled = false;
                 userState.mIsEnhancedWebAccessibilityEnabled = false;
                 userState.mIsDisplayMagnificationEnabled = false;
+                userState.mIsNavBarMagnificationEnabled = false;
                 userState.mIsAutoclickEnabled = false;
                 userState.mEnabledServices.clear();
             }
@@ -829,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);
@@ -1150,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();
+                }
             }
         }
     }
@@ -1546,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;
             }
@@ -1779,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;
@@ -1808,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;
@@ -2016,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();
@@ -2239,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(", ");
@@ -2318,6 +2338,7 @@
         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);
@@ -2404,6 +2425,14 @@
                         }
                     });
                 } break;
+
+               case MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER: {
+                    synchronized (mLock) {
+                        if (mHasInputFilter && mInputFilter != null) {
+                            mInputFilter.notifyAccessibilityButtonClicked();
+                        }
+                    }
+                }
             }
         }
 
@@ -4786,6 +4815,7 @@
         public boolean mIsTextHighContrastEnabled;
         public boolean mIsEnhancedWebAccessibilityEnabled;
         public boolean mIsDisplayMagnificationEnabled;
+        public boolean mIsNavBarMagnificationEnabled;
         public boolean mIsAutoclickEnabled;
         public boolean mIsPerformGesturesEnabled;
         public boolean mIsFilterKeyEventsEnabled;
@@ -4854,6 +4884,7 @@
             mIsTouchExplorationEnabled = false;
             mIsEnhancedWebAccessibilityEnabled = false;
             mIsDisplayMagnificationEnabled = false;
+            mIsNavBarMagnificationEnabled = false;
             mIsAutoclickEnabled = false;
             mSoftKeyboardShowMode = 0;
 
@@ -4886,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);
 
@@ -4925,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,
@@ -4964,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
index a372f95..c7ba1ff 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -28,6 +28,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
 import android.graphics.Rect;
@@ -53,6 +54,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.Preconditions;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -316,13 +318,27 @@
         @Override
         public void startSession(IBinder activityToken, IBinder windowToken, IBinder appCallback,
                 AutofillId autofillId, Rect bounds, AutofillValue value, int userId,
-                boolean hasCallback) {
+                boolean hasCallback, int flags, String packageName) {
             // TODO(b/33197203): make sure it's called by resumed / focused activity
 
+            activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
+            appCallback = Preconditions.checkNotNull(appCallback, "appCallback");
+            autofillId = Preconditions.checkNotNull(autofillId, "autoFillId");
+            bounds = Preconditions.checkNotNull(bounds, "bounds");
+            packageName = Preconditions.checkNotNull(packageName, "packageName");
+
+            Preconditions.checkArgument(userId == UserHandle.getUserId(getCallingUid()), "userId");
+
+            try {
+                mContext.getPackageManager().getPackageInfoAsUser(packageName, 0, userId);
+            } catch (PackageManager.NameNotFoundException e) {
+                throw new IllegalArgumentException(packageName + " is not a valid package", e);
+            }
+
             synchronized (mLock) {
                 final AutofillManagerServiceImpl service = getServiceForUserLocked(userId);
                 service.startSessionLocked(activityToken, windowToken, appCallback,
-                        autofillId, bounds, value, hasCallback);
+                        autofillId, bounds, value, hasCallback, flags, packageName);
             }
         }
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index b6c60d0..df76009 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -28,6 +28,7 @@
 import static com.android.server.autofill.Helper.VERBOSE;
 import static com.android.server.autofill.Helper.findValue;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -43,6 +44,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
 import android.graphics.Rect;
+import android.metrics.LogMaker;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -60,13 +62,18 @@
 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;
 import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
 import android.view.autofill.IAutoFillManagerClient;
+import android.view.autofill.AutofillManager.AutofillCallback;
+
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.IResultReceiver;
 import com.android.server.autofill.ui.AutoFillUI;
@@ -91,6 +98,7 @@
     private final Context mContext;
     private final Object mLock;
     private final AutoFillUI mUi;
+    private final MetricsLogger mMetricsLogger = new MetricsLogger();
 
     private RemoteCallbackList<IAutoFillManagerClient> mClients;
     private AutofillServiceInfo mInfo;
@@ -169,7 +177,7 @@
             structure.sanitizeForParceling(true);
 
             // TODO(b/33197203): Need to pipe the bundle
-            session.mRemoteFillService.onFillRequest(structure, null);
+            session.mRemoteFillService.onFillRequest(structure, null, session.mFlags);
         }
     };
 
@@ -227,9 +235,8 @@
                 if (!hasService()) {
                     final int sessionCount = mSessions.size();
                     for (int i = sessionCount - 1; i >= 0; i--) {
-                        Session session = mSessions.valueAt(i);
-                        session.destroyLocked();
-                        mSessions.removeAt(i);
+                        final Session session = mSessions.valueAt(i);
+                        session.removeSelfLocked();
                     }
                 }
                 sendStateToClients();
@@ -283,8 +290,10 @@
         }
     }
 
-    void startSessionLocked(IBinder activityToken, IBinder windowToken, IBinder appCallbackToken,
-            AutofillId autofillId,  Rect bounds, AutofillValue value, boolean hasCallback) {
+    void startSessionLocked(@NonNull IBinder activityToken, @Nullable IBinder windowToken,
+            @NonNull IBinder appCallbackToken, @NonNull AutofillId autofillId, @NonNull Rect bounds,
+            @Nullable AutofillValue value, boolean hasCallback, int flags,
+            @NonNull String packageName) {
         if (!hasService()) {
             return;
         }
@@ -292,7 +301,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 +312,7 @@
         }
 
         final Session newSession = createSessionByTokenLocked(activityToken,
-                windowToken, appCallbackToken, hasCallback);
+                windowToken, appCallbackToken, hasCallback, flags, packageName);
         newSession.updateLocked(autofillId, bounds, value, FLAG_START_SESSION);
     }
 
@@ -318,7 +327,13 @@
             return;
         }
 
-        session.showSaveLocked();
+        final boolean finished = session.showSaveLocked();
+        if (DEBUG) {
+            Log.d(TAG, "finishSessionLocked(): session finished on save? " + finished);
+        }
+        if (finished) {
+            session.removeSelf();
+        }
     }
 
     void cancelSessionLocked(IBinder activityToken) {
@@ -331,14 +346,14 @@
             Slog.w(TAG, "cancelSessionLocked(): no session for " + activityToken);
             return;
         }
-
-        session.destroyLocked();
+        session.removeSelfLocked();
     }
 
-    private Session createSessionByTokenLocked(IBinder activityToken, IBinder windowToken,
-            IBinder appCallbackToken, boolean hasCallback) {
+    private Session createSessionByTokenLocked(@NonNull IBinder activityToken,
+            @Nullable IBinder windowToken, @NonNull IBinder appCallbackToken, boolean hasCallback,
+            int flags, @NonNull String packageName) {
         final Session newSession = new Session(mContext, activityToken,
-                windowToken, appCallbackToken, hasCallback);
+                windowToken, appCallbackToken, hasCallback, flags, packageName);
         mSessions.put(activityToken, newSession);
 
         /*
@@ -349,7 +364,6 @@
          * - display disclosure if needed
          */
         try {
-            // TODO(b/33197203): add MetricsLogger call
             final Bundle receiverExtras = new Bundle();
             receiverExtras.putBinder(EXTRA_ACTIVITY_TOKEN, activityToken);
             final long identity = Binder.clearCallingIdentity();
@@ -369,7 +383,6 @@
 
     void updateSessionLocked(IBinder activityToken, AutofillId autofillId, Rect bounds,
             AutofillValue value, int flags) {
-        // TODO(b/33197203): add MetricsLogger call
         final Session session = mSessions.get(activityToken);
         if (session == null) {
             if (VERBOSE) {
@@ -593,6 +606,9 @@
         private final IBinder mActivityToken;
         private final IBinder mWindowToken;
 
+        /** Package name of the app that is auto-filled */
+        @NonNull private final String mPackageName;
+
         @GuardedBy("mLock")
         private final Map<AutofillId, ViewState> mViewStates = new ArrayMap<>();
 
@@ -628,13 +644,20 @@
          */
         private boolean mHasCallback;
 
-        private Session(Context context, IBinder activityToken, IBinder windowToken,
-                IBinder client, boolean hasCallback) {
+        /**
+         * Flags used to start the session.
+         */
+        private int mFlags;
+        private Session(@NonNull Context context, @NonNull IBinder activityToken,
+                @Nullable IBinder windowToken, @NonNull IBinder client, boolean hasCallback,
+                int flags, @NonNull String packageName) {
             mRemoteFillService = new RemoteFillService(context,
                     mInfo.getServiceInfo().getComponentName(), mUserId, this);
             mActivityToken = activityToken;
             mWindowToken = windowToken;
             mHasCallback = hasCallback;
+            mFlags = flags;
+            mPackageName = packageName;
 
             mClient = IAutoFillManagerClient.Stub.asInterface(client);
             try {
@@ -648,41 +671,82 @@
             } catch (RemoteException e) {
                 Slog.w(TAG, "linkToDeath() on mClient failed: " + e);
             }
+
+            mMetricsLogger.action(MetricsProto.MetricsEvent.AUTOFILL_SESSION_STARTED, mPackageName);
         }
 
         // FillServiceCallbacks
         @Override
-        public void onFillRequestSuccess(FillResponse response) {
-            // TODO(b/33197203): add MetricsLogger call
+        public void onFillRequestSuccess(@Nullable FillResponse response,
+                @NonNull String servicePackageName) {
             if (response == null) {
+                // Nothing to be done, but need to notify client.
+                notifyUnavailableToClient();
                 removeSelf();
                 return;
             }
+
+            if ((response.getDatasets() == null || response.getDatasets().isEmpty())
+                            && response.getAuthentication() == null) {
+                // Response is "empty" from an UI point of view, need to notify client.
+                notifyUnavailableToClient();
+            }
             synchronized (mLock) {
                 processResponseLocked(response);
             }
+
+            LogMaker log = (new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_REQUEST))
+                    .setType(MetricsProto.MetricsEvent.TYPE_SUCCESS)
+                    .setPackageName(mPackageName)
+                    .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS,
+                            response.getDatasets() == null ? 0 : response.getDatasets().size())
+                    .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_SERVICE,
+                            servicePackageName);
+            mMetricsLogger.write(log);
         }
 
         // FillServiceCallbacks
         @Override
-        public void onFillRequestFailure(CharSequence message) {
-            // TODO(b/33197203): add MetricsLogger call
+        public void onFillRequestFailure(@Nullable CharSequence message,
+                @NonNull String servicePackageName) {
+            LogMaker log = (new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_REQUEST))
+                    .setType(MetricsProto.MetricsEvent.TYPE_FAILURE)
+                    .setPackageName(mPackageName)
+                    .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_SERVICE,
+                            servicePackageName);
+            mMetricsLogger.write(log);
+
             getUiForShowing().showError(message);
             removeSelf();
         }
 
         // FillServiceCallbacks
         @Override
-        public void onSaveRequestSuccess() {
-            // TODO(b/33197203): add MetricsLogger call
+        public void onSaveRequestSuccess(@NonNull String servicePackageName) {
+            LogMaker log = (new LogMaker(
+                    MetricsProto.MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST))
+                    .setType(MetricsProto.MetricsEvent.TYPE_SUCCESS)
+                    .setPackageName(mPackageName)
+                    .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_SERVICE,
+                            servicePackageName);
+            mMetricsLogger.write(log);
+
             // Nothing left to do...
             removeSelf();
         }
 
         // FillServiceCallbacks
         @Override
-        public void onSaveRequestFailure(CharSequence message) {
-            // TODO(b/33197203): add MetricsLogger call
+        public void onSaveRequestFailure(@Nullable CharSequence message,
+                @NonNull String servicePackageName) {
+            LogMaker log = (new LogMaker(
+                    MetricsProto.MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST))
+                    .setType(MetricsProto.MetricsEvent.TYPE_FAILURE)
+                    .setPackageName(mPackageName)
+                    .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_SERVICE,
+                            servicePackageName);
+            mMetricsLogger.write(log);
+
             getUiForShowing().showError(message);
             removeSelf();
         }
@@ -694,9 +758,7 @@
             synchronized (mLock) {
                 fillInIntent = createAuthFillInIntent(mStructure);
             }
-            mHandlerCaller.getHandler().post(() -> {
-                startAuthentication(intent, fillInIntent);
-            });
+            mHandlerCaller.getHandler().post(() -> startAuthentication(intent, fillInIntent));
         }
 
         // FillServiceCallbacks
@@ -716,8 +778,7 @@
                 Binder.restoreCallingIdentity(identity);
             }
             synchronized (mLock) {
-                destroyLocked();
-                mSessions.remove(this);
+                removeSelfLocked();
             }
         }
 
@@ -730,9 +791,7 @@
         // AutoFillUiCallback
         @Override
         public void fill(Dataset dataset) {
-            mHandlerCaller.getHandler().post(() -> {
-                autoFill(dataset);
-            });
+            mHandlerCaller.getHandler().post(() -> autoFill(dataset));
         }
 
         // AutoFillUiCallback
@@ -745,17 +804,13 @@
         // AutoFillUiCallback
         @Override
         public void cancelSave() {
-            mHandlerCaller.getHandler().post(() -> {
-                removeSelf();
-            });
+            mHandlerCaller.getHandler().post(() -> removeSelf());
         }
 
         // AutoFillUiCallback
         @Override
         public void onEvent(AutofillId id, int event) {
-            mHandlerCaller.getHandler().post(() -> {
-                notifyChangeToClient(id, event);
-            });
+            mHandlerCaller.getHandler().post(() -> notifyChangeToClient(id, event));
         }
 
         public void setAuthenticationResultLocked(Bundle data) {
@@ -765,6 +820,9 @@
                 Parcelable result = data.getParcelable(
                         AutofillManager.EXTRA_AUTHENTICATION_RESULT);
                 if (result instanceof FillResponse) {
+                    mMetricsLogger.action(MetricsProto.MetricsEvent.AUTOFILL_AUTHENTICATED,
+                            mPackageName);
+
                     mCurrentResponse = (FillResponse) result;
                     processResponseLocked(mCurrentResponse);
                 } else if (result instanceof Dataset) {
@@ -782,12 +840,14 @@
         }
 
         /**
-         * Show the save UI, when session can be saved.
+         * Shows the save UI, when session can be saved.
+         *
+         * @return {@code true} if session is done, or {@code false} if it's pending user action.
          */
-        public void showSaveLocked() {
+        public boolean showSaveLocked() {
             if (mStructure == null) {
                 Slog.wtf(TAG, "showSaveLocked(): no mStructure");
-                return;
+                return true;
             }
             if (mCurrentResponse == null) {
                 // Happens when the activity / session was finished before the service replied, or
@@ -795,42 +855,108 @@
                 if (DEBUG) {
                     Slog.d(TAG, "showSaveLocked(): no mCurrentResponse");
                 }
-                return;
+                return true;
             }
             final SaveInfo saveInfo = mCurrentResponse.getSaveInfo();
             if (DEBUG) {
                 Slog.d(TAG, "showSaveLocked(): saveInfo=" + saveInfo);
             }
 
-            if (saveInfo == null || saveInfo.getSavableIds() == null
-                    || saveInfo.getSavableIds().isEmpty()) {
-                return;
+            /*
+             * 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 true;
             }
 
-            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 true;
+            }
+
+            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;
+
                     }
-                    getUiForShowing().showSaveUi(
-                            mInfo.getServiceInfo().loadLabel(mContext.getPackageManager()),
-                            saveInfo);
-                    return;
                 }
             }
 
+            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, mPackageName);
+                    return false;
+                }
+            }
             // 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);
             }
+            return true;
         }
 
         /**
@@ -872,7 +998,7 @@
                 mStructure.dump();
             }
 
-            mRemoteFillService.onSaveRequest(mStructure, extras);
+            mRemoteFillService.onSaveRequest(mStructure, extras, () -> removeSelf());
         }
 
         void updateLocked(AutofillId id, Rect bounds, AutofillValue value, int flags) {
@@ -913,7 +1039,11 @@
                     viewState.mAutofillValue = value;
 
                     // Update the chooser UI
-                    getUiForShowing().filterFillUi(value.coerceToString());
+                    if (value.isText()) {
+                        getUiForShowing().filterFillUi(value.getTextValue().toString());
+                    } else {
+                        getUiForShowing().filterFillUi(null);
+                    }
                 }
 
                 return;
@@ -951,16 +1081,12 @@
         @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);
+            getUiForShowing().showFillUi(filledId, response, bounds, filterText, mPackageName);
         }
 
         private void notifyChangeToClient(AutofillId id, int event) {
@@ -972,17 +1098,24 @@
             }
         }
 
+        private void notifyUnavailableToClient() {
+            if (mCurrentViewState == null) {
+                // TODO(b/33197203): temporary sanity check; should never happen
+                Slog.w(TAG, "notifyUnavailable(): mCurrentViewState is null");
+                return;
+            }
+            notifyChangeToClient(mCurrentViewState.mId, AutofillCallback.EVENT_INPUT_UNAVAILABLE);
+        }
+
         private void processResponseLocked(FillResponse response) {
             if (DEBUG) {
                 Slog.d(TAG, "processResponseLocked(auth=" + response.getAuthentication()
                     + "):" + response);
             }
 
-            // TODO(b/33197203): add MetricsLogger calls
-
             if (mCurrentViewState == null) {
                 // TODO(b/33197203): temporary sanity check; should never happen
-                Slog.w(TAG, "processResponseLocked(): mCurrentResponse is null");
+                Slog.w(TAG, "processResponseLocked(): mCurrentViewState is null");
                 return;
             }
 
@@ -1034,6 +1167,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);
@@ -1113,17 +1247,23 @@
         private void destroyLocked() {
             mRemoteFillService.destroy();
             mUi.setCallback(null, null);
+
+            mMetricsLogger.action(MetricsProto.MetricsEvent.AUTOFILL_SESSION_FINISHED,
+                    mPackageName);
         }
 
-        private void removeSelf() {
-            if (VERBOSE) {
-                Slog.v(TAG, "removeSelf()");
-            }
-
+        void removeSelf() {
             synchronized (mLock) {
-                destroyLocked();
-                mSessions.remove(mActivityToken);
+                removeSelfLocked();
             }
         }
+
+        private void removeSelfLocked() {
+            if (VERBOSE) {
+                Slog.v(TAG, "removeSelfLocked()");
+            }
+            destroyLocked();
+            mSessions.remove(mActivityToken);
+        }
     }
 }
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index eeff37c..d1c8b4f8 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.autofill;
 
+import static com.android.server.autofill.Helper.DEBUG;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.assist.AssistStructure;
@@ -38,8 +40,10 @@
 import android.service.autofill.ISaveCallback;
 import android.text.format.DateUtils;
 import android.util.Slog;
+
 import com.android.internal.os.HandlerCaller;
 import com.android.server.FgThread;
+import com.android.server.autofill.AutofillManagerServiceImpl.Session;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -55,8 +59,6 @@
 final class RemoteFillService implements DeathRecipient {
     private static final String LOG_TAG = "RemoteFillService";
 
-    private static final boolean DEBUG = Helper.DEBUG;
-
     // How long after the last interaction with the service we would unbind
     private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
 
@@ -87,10 +89,10 @@
     private PendingRequest mPendingRequest;
 
     public interface FillServiceCallbacks {
-        void onFillRequestSuccess(FillResponse response);
-        void onFillRequestFailure(CharSequence message);
-        void onSaveRequestSuccess();
-        void onSaveRequestFailure(CharSequence message);
+        void onFillRequestSuccess(@Nullable FillResponse response, @NonNull String servicePackageName);
+        void onFillRequestFailure(@Nullable CharSequence message, @NonNull String servicePackageName);
+        void onSaveRequestSuccess(@NonNull String servicePackageName);
+        void onSaveRequestFailure(@Nullable CharSequence message, @NonNull String servicePackageName);
         void onServiceDied(RemoteFillService service);
         void onDisableSelf();
     }
@@ -132,15 +134,17 @@
         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();
     }
 
-    public void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle extras) {
+    public void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle extras,
+            @Nullable Runnable finalizer) {
         cancelScheduledUnbind();
-        final PendingSaveRequest request = new PendingSaveRequest(structure, extras, this);
+        final PendingSaveRequest request = new PendingSaveRequest(structure, extras, this, finalizer);
         mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, request).sendToTarget();
     }
 
@@ -240,7 +244,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 {
@@ -261,7 +265,7 @@
             FillResponse response) {
         mHandler.getHandler().post(() -> {
             if (handleResponseCallbackCommon(pendingRequest)) {
-                mCallbacks.onFillRequestSuccess(response);
+                mCallbacks.onFillRequestSuccess(response, mComponentName.getPackageName());
             }
         });
     }
@@ -270,7 +274,7 @@
             CharSequence message) {
         mHandler.getHandler().post(() -> {
             if (handleResponseCallbackCommon(pendingRequest)) {
-                mCallbacks.onFillRequestFailure(message);
+                mCallbacks.onFillRequestFailure(message, mComponentName.getPackageName());
             }
         });
     }
@@ -278,7 +282,7 @@
     private void dispatchOnSaveRequestSuccess(PendingRequest pendingRequest) {
         mHandler.getHandler().post(() -> {
             if (handleResponseCallbackCommon(pendingRequest)) {
-                mCallbacks.onSaveRequestSuccess();
+                mCallbacks.onSaveRequestSuccess(mComponentName.getPackageName());
             }
         });
     }
@@ -287,7 +291,7 @@
             CharSequence message) {
         mHandler.getHandler().post(() -> {
             if (handleResponseCallbackCommon(pendingRequest)) {
-                mCallbacks.onSaveRequestFailure(message);
+                mCallbacks.onSaveRequestFailure(message, mComponentName.getPackageName());
             }
         });
     }
@@ -322,7 +326,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) {
@@ -413,16 +417,18 @@
     private static final class PendingFillRequest extends PendingRequest {
         private final Object mLock = new Object();
         private final WeakReference<RemoteFillService> mWeakService;
-        private AssistStructure mStructure;
-        private Bundle mExtras;
+        private final AssistStructure mStructure;
+        private final Bundle mExtras;
         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
@@ -469,11 +475,7 @@
             if (remoteService != null) {
                 try {
                     remoteService.mAutoFillService.onFillRequest(mStructure,
-                            mExtras, mCallback);
-                    synchronized (mLock) {
-                        mStructure = null;
-                        mExtras = null;
-                    }
+                            mExtras, mCallback, mFlags);
                 } catch (RemoteException e) {
                     Slog.e(LOG_TAG, "Error calling on fill request", e);
                     cancel();
@@ -503,17 +505,18 @@
     }
 
     private static final class PendingSaveRequest extends PendingRequest {
-        private final Object mLock = new Object();
         private final WeakReference<RemoteFillService> mWeakService;
-        private AssistStructure mStructure;
-        private Bundle mExtras;
+        private final AssistStructure mStructure;
+        private final Bundle mExtras;
         private final ISaveCallback mCallback;
+        private final Runnable mFinalizer;
 
-        public PendingSaveRequest(@NonNull AssistStructure structure,
-                @Nullable Bundle extras, @NonNull RemoteFillService service) {
+        public PendingSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle extras,
+                @NonNull RemoteFillService service, @Nullable Runnable finalizer) {
             mStructure = structure;
             mExtras = extras;
             mWeakService = new WeakReference<>(service);
+            mFinalizer = finalizer;
             mCallback = new ISaveCallback.Stub() {
                 @Override
                 public void onSuccess() {
@@ -537,19 +540,17 @@
 
         @Override
         public void run() {
-            RemoteFillService service = mWeakService.get();
+            final RemoteFillService service = mWeakService.get();
             if (service != null) {
                 try {
-                    service.mAutoFillService.onSaveRequest(mStructure,
-                            mExtras, mCallback);
-                    synchronized (mLock) {
-                        mStructure = null;
-                        mExtras = null;
-                    }
+                    service.mAutoFillService.onSaveRequest(mStructure, mExtras, mCallback);
                 } catch (RemoteException e) {
                     Slog.e(LOG_TAG, "Error calling on save request", e);
                 }
             }
+            if (mFinalizer != null) {
+              mFinalizer.run();
+            }
         }
 
         @Override
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..776fa1e 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.IntentSender;
 import android.graphics.Rect;
+import android.metrics.LogMaker;
 import android.os.Handler;
 import android.os.IBinder;
 import android.service.autofill.Dataset;
@@ -34,6 +35,8 @@
 import android.view.autofill.AutofillId;
 import android.widget.Toast;
 
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto;
 import com.android.server.UiThread;
 
 import java.io.PrintWriter;
@@ -60,6 +63,7 @@
     private @Nullable IBinder mWindowToken;
 
     private int mSaveTimeoutMs = (int) (5 * DateUtils.SECOND_IN_MILLIS);
+    private final MetricsLogger mMetricsLogger = new MetricsLogger();
 
     public interface AutoFillUiCallback {
         void authenticate(@NonNull IntentSender intent);
@@ -123,7 +127,7 @@
             }
             hideSaveUiUiThread();
             if (mFillUi != null) {
-                mFillUi.filter(filterText);
+                mFillUi.setFilterText(filterText);
             }
         });
     }
@@ -152,9 +156,17 @@
      * @param response the current fill response
      * @param anchorBounds bounds of the focused view
      * @param filterText text of the view to be filled
+     * @param packageName package name of the activity that is filled
      */
     public void showFillUi(@NonNull AutofillId focusedId, @NonNull FillResponse response,
-            @NonNull Rect anchorBounds, @Nullable String filterText) {
+            @NonNull Rect anchorBounds, @Nullable String filterText, @NonNull String packageName) {
+        LogMaker log = (new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_FILL_UI))
+                .setPackageName(packageName)
+                .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_FILTERTEXT_LEN,
+                        filterText == null ? 0 : filterText.length())
+                .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS,
+                        response.getDatasets() == null ? 0 : response.getDatasets().size());
+
         mHandler.post(() -> {
             if (!hasCallback()) {
                 return;
@@ -164,6 +176,7 @@
                     mWindowToken, anchorBounds, filterText, new FillUi.Callback() {
                 @Override
                 public void onResponsePicked(FillResponse response) {
+                    log.setType(MetricsProto.MetricsEvent.TYPE_DETAIL);
                     hideFillUiUiThread();
                     if (mCallback != null) {
                         mCallback.authenticate(response.getAuthentication());
@@ -172,17 +185,25 @@
 
                 @Override
                 public void onDatasetPicked(Dataset dataset) {
+                    log.setType(MetricsProto.MetricsEvent.TYPE_ACTION);
                     hideFillUiUiThread();
                     if (mCallback != null) {
                         mCallback.fill(dataset);
                     }
-                    // TODO(b/33197203): add MetricsLogger call
                 }
 
                 @Override
                 public void onCanceled() {
+                    log.setType(MetricsProto.MetricsEvent.TYPE_DISMISS);
                     hideFillUiUiThread();
-                    // TODO(b/33197203): add MetricsLogger call
+                }
+
+                @Override
+                public void onDestroy() {
+                    if (log.getType() == MetricsProto.MetricsEvent.TYPE_UNKNOWN) {
+                        log.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);
+                    }
+                    mMetricsLogger.write(log);
                 }
             });
             mCallback.onEvent(focusedId, EVENT_INPUT_SHOWN);
@@ -192,7 +213,16 @@
     /**
      * Shows the UI asking the user to save for autofill.
      */
-    public void showSaveUi(@NonNull CharSequence providerLabel, @NonNull SaveInfo info) {
+    public void showSaveUi(@NonNull CharSequence providerLabel, @NonNull SaveInfo info,
+            @NonNull String packageName) {
+        int numIds = 0;
+        numIds += info.getRequiredIds() == null ? 0 : info.getRequiredIds().length;
+        numIds += info.getOptionalIds() == null ? 0 : info.getOptionalIds().length;
+
+        LogMaker log = (new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_SAVE_UI))
+                .setPackageName(packageName).addTaggedData(
+                        MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_IDS, numIds);
+
         mHandler.post(() -> {
             if (!hasCallback()) {
                 return;
@@ -202,16 +232,16 @@
                     new SaveUi.OnSaveListener() {
                 @Override
                 public void onSave() {
+                    log.setType(MetricsProto.MetricsEvent.TYPE_ACTION);
                     hideSaveUiUiThread();
                     if (mCallback != null) {
                         mCallback.save();
                     }
-                    // TODO(b/33197203): add MetricsLogger call
                 }
 
                 @Override
                 public void onCancel(IntentSender listener) {
-                    // TODO(b/33197203): add MetricsLogger call
+                    log.setType(MetricsProto.MetricsEvent.TYPE_DISMISS);
                     hideSaveUiUiThread();
                     if (listener != null) {
                         try {
@@ -225,6 +255,14 @@
                         mCallback.cancelSave();
                     }
                 }
+
+                @Override
+                public void onDestroy() {
+                    if (log.getType() == MetricsProto.MetricsEvent.TYPE_UNKNOWN) {
+                        log.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);
+                    }
+                    mMetricsLogger.write(log);
+                }
             }, mSaveTimeoutMs);
         });
     }
@@ -247,11 +285,17 @@
     }
 
     public void dump(PrintWriter pw) {
-        pw.println("AufoFill UI");
+        pw.println("Autofill UI");
         final String prefix = "  ";
-        pw.print(prefix); pw.print("showsFillUi: "); pw.println(mFillUi != null);
+        final String prefix2 = "    ";
         pw.print(prefix); pw.print("showsSaveUi: "); pw.println(mSaveUi != null);
         pw.print(prefix); pw.print("save timeout: "); pw.println(mSaveTimeoutMs);
+        if (mFillUi != null) {
+            pw.print(prefix); pw.println("showsFillUi: true");
+            mFillUi.dump(pw, prefix2);
+        } else {
+            pw.print(prefix); pw.println("showsFillUi: false");
+        }
     }
 
     @android.annotation.UiThread
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 3fdcd9e..a8c8752 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -39,6 +39,7 @@
 import com.android.internal.R;
 import libcore.util.Objects;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 
 final class FillUi {
@@ -50,6 +51,7 @@
         void onResponsePicked(@NonNull FillResponse response);
         void onDatasetPicked(@NonNull Dataset dataset);
         void onCanceled();
+        void onDestroy();
     }
 
     private final Rect mAnchorBounds = new Rect();
@@ -63,6 +65,7 @@
     private final @Nullable ArrayAdapter<ViewItem> mAdapter;
 
     private @Nullable String mFilterText;
+    private final String mAccessibilityTitle;
 
     private int mContentWidth;
     private int mContentHeight;
@@ -76,6 +79,8 @@
         mAnchorBounds.set(anchorBounds);
         mCallback = callback;
 
+        mAccessibilityTitle = context.getString(R.string.autofill_picker_accessibility_title);
+
         if (response.getAuthentication() != null) {
             mListView = null;
             mAdapter = null;
@@ -113,8 +118,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));
                 }
             }
 
@@ -134,7 +144,13 @@
                 mCallback.onDatasetPicked(vi.getDataset());
             });
 
-            filter(filterText);
+            if (filterText == null) {
+                mFilterText = null;
+            } else {
+                mFilterText = filterText.toLowerCase();
+            }
+
+            applyNewFilterText();
             mWindow = new AnchoredWindow(windowToken, mListView);
         }
     }
@@ -147,16 +163,8 @@
         }
     }
 
-    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;
             }
@@ -176,8 +184,29 @@
         });
     }
 
+    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();
+        mCallback.onDestroy();
         mWindow.hide();
         mDestroyed = true;
     }
@@ -235,7 +264,7 @@
 
         ViewItem(Dataset dataset, String value, View view) {
             mDataset = dataset;
-            mValue = value.toLowerCase();
+            mValue = value;
             mView = view;
         }
 
@@ -296,6 +325,7 @@
 
         public void show(int desiredWidth, int desiredHeight, Rect anchorBounds) {
             final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+
             params.setTitle("FillUi");
             params.token = mActivityToken;
             params.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -304,6 +334,7 @@
                     | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                     | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                     | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+            params.accessibilityTitle = mAccessibilityTitle;
 
             mWm.getDefaultDisplay().getRealSize(mTempPoint);
             final int screenWidth = mTempPoint.x;
@@ -354,4 +385,16 @@
             }
         }
     }
+
+    public void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix); pw.print("mAnchorBounds: "); pw.println(mAnchorBounds);
+        pw.print(prefix); pw.print("mCallback: "); pw.println(mCallback != null);
+        pw.print(prefix); pw.print("mListView: "); pw.println(mListView);
+        pw.print(prefix); pw.print("mAdapter: "); pw.println(mAdapter != null);
+        pw.print(prefix); pw.print("mFilterText: "); pw.println(mFilterText);
+        pw.print(prefix); pw.print("mAccessibilityTitle: "); pw.println(mAccessibilityTitle);
+        pw.print(prefix); pw.print("mContentWidth: "); pw.println(mContentWidth);
+        pw.print(prefix); pw.print("mContentHeight: "); pw.println(mContentHeight);
+        pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
+    }
 }
diff --git a/services/autofill/java/com/android/server/autofill/ui/Helper.java b/services/autofill/java/com/android/server/autofill/ui/Helper.java
new file mode 100644
index 0000000..996e421
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/Helper.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.autofill.ui;
+
+final class Helper {
+
+    static final boolean DEBUG = true; // TODO(b/33197203): set to false when stable
+    static final boolean VERBOSE = false;
+    private Helper() {
+        throw new UnsupportedOperationException("contains static members only");
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 3f409ad..509351b 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -16,6 +16,8 @@
 
 package com.android.server.autofill.ui;
 
+import static com.android.server.autofill.ui.Helper.DEBUG;
+
 import android.annotation.NonNull;
 import android.app.Dialog;
 import android.content.Context;
@@ -23,6 +25,7 @@
 import android.os.Handler;
 import android.service.autofill.SaveInfo;
 import android.text.format.DateUtils;
+import android.util.Slog;
 import android.view.Gravity;
 import android.view.Window;
 import android.view.WindowManager;
@@ -37,22 +40,66 @@
  * Autofill Save Prompt
  */
 final class SaveUi {
+
+    private static final String TAG = "SaveUi";
+
     public interface OnSaveListener {
         void onSave();
         void onCancel(IntentSender listener);
+        void onDestroy();
+    }
+
+    private class OneTimeListener implements OnSaveListener {
+
+        private final OnSaveListener mRealListener;
+        private boolean mDone;
+
+        OneTimeListener(OnSaveListener realListener) {
+            mRealListener = realListener;
+        }
+
+        @Override
+        public void onSave() {
+            if (DEBUG) Slog.d(TAG, "onSave(): " + mDone);
+            if (mDone) {
+                return;
+            }
+            mDone = true;
+            mRealListener.onSave();
+        }
+
+        @Override
+        public void onCancel(IntentSender listener) {
+            if (DEBUG) Slog.d(TAG, "onCancel(): " + mDone);
+            if (mDone) {
+                return;
+            }
+            mDone = true;
+            mRealListener.onCancel(listener);
+        }
+
+        @Override
+        public void onDestroy() {
+            if (DEBUG) Slog.d(TAG, "onDestroy(): " + mDone);
+            if (mDone) {
+                return;
+            }
+            mDone = true;
+            mRealListener.onDestroy();
+        }
     }
 
     private final Handler mHandler = UiThread.getHandler();
 
     private final @NonNull Dialog mDialog;
 
-    private final @NonNull OnSaveListener mListener;
+    private final @NonNull OneTimeListener mListener;
 
     private boolean mDestroyed;
 
     SaveUi(@NonNull Context context, @NonNull CharSequence providerLabel, @NonNull SaveInfo info,
             @NonNull OnSaveListener listener, int lifeTimeMs) {
-        mListener = listener;
+        mListener = new OneTimeListener(listener);
 
         final LayoutInflater inflater = LayoutInflater.from(context);
         final View view = inflater.inflate(R.layout.autofill_save, null);
@@ -117,11 +164,17 @@
 
         mDialog.show();
 
-        mHandler.postDelayed(() -> mListener.onCancel(null), lifeTimeMs);
+        mHandler.postDelayed(() -> {
+            if (!mListener.mDone) {
+                mListener.onCancel(null);
+                Slog.d(TAG, "Save snackbar timed out after " + lifeTimeMs + "ms");
+            }
+        }, lifeTimeMs);
     }
 
     void destroy() {
         throwIfDestroyed();
+        mListener.onDestroy();
         mHandler.removeCallbacksAndMessages(mListener);
         mDialog.dismiss();
         mDestroyed = true;
diff --git a/services/core/Android.mk b/services/core/Android.mk
index 1864d34..794ece6 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -23,7 +23,9 @@
     android.hardware.power@1.0-java \
     android.hardware.tv.cec@1.0-java
 
-LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update2 \
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    tzdata_shared2 \
+    tzdata_update2 \
     android.hidl.base@1.0-java-static \
     android.hardware.biometrics.fingerprint@2.1-java-static \
 
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/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index bcee2c1b..c946d09 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);
 
@@ -1482,7 +1488,7 @@
             return VerifyCredentialResponse.OK;
         }
 
-        if (TextUtils.isEmpty(credential)) {
+        if (storedHash == null || TextUtils.isEmpty(credential)) {
             return VerifyCredentialResponse.ERROR;
         }
 
@@ -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 3667ecd..8e6310f 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2721,7 +2721,7 @@
      */
     @Override
     public int getPasswordType() {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
             "no permission to access the crypt keeper");
 
         waitForReady();
@@ -2747,7 +2747,7 @@
      */
     @Override
     public void setField(String field, String contents) throws RemoteException {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
             "no permission to access the crypt keeper");
 
         waitForReady();
@@ -2767,7 +2767,7 @@
      */
     @Override
     public String getField(String field) throws RemoteException {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
             "no permission to access the crypt keeper");
 
         waitForReady();
@@ -2793,7 +2793,7 @@
      */
     @Override
     public boolean isConvertibleToFBE() throws RemoteException {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
             "no permission to access the crypt keeper");
 
         waitForReady();
@@ -2809,7 +2809,7 @@
 
     @Override
     public String getPassword() throws RemoteException {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
                 "only keyguard can retrieve password");
 
         if (!isReady()) {
@@ -2834,7 +2834,7 @@
 
     @Override
     public void clearPassword() throws RemoteException {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL,
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
                 "only keyguard can clear password");
 
         if (!isReady()) {
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..f954f75 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -35,7 +35,6 @@
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
-import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.INotificationManager;
 import android.app.Notification;
@@ -64,7 +63,6 @@
 import android.content.pm.UserInfo;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteStatement;
-import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -80,7 +78,6 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.os.storage.StorageManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
@@ -113,7 +110,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -158,6 +154,11 @@
         public void onUnlockUser(int userHandle) {
             mService.onUnlockUser(userHandle);
         }
+
+        @Override
+        public void onCleanupUser(int userHandle) {
+            mService.onCleanupUser(userHandle);
+        }
     }
 
     final Context mContext;
@@ -303,18 +304,6 @@
             }
         }, intentFilter);
 
-        IntentFilter userFilter = new IntentFilter();
-        userFilter.addAction(Intent.ACTION_USER_REMOVED);
-        mContext.registerReceiverAsUser(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
-                if (Intent.ACTION_USER_REMOVED.equals(action)) {
-                    onUserRemoved(intent);
-                }
-            }
-        }, UserHandle.ALL, userFilter, null, null);
-
         injector.addLocalService(new AccountManagerInternalImpl());
 
         // Need to cancel account request notifications if the update/install can access the account
@@ -1133,16 +1122,12 @@
         }
     }
 
-    private void onUserRemoved(Intent intent) {
-        int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
-        if (userId < 1) return;
-
+    private void onCleanupUser(int userId) {
+        Log.i(TAG, "onCleanupUser " + userId);
         UserAccounts accounts;
-        boolean userUnlocked;
         synchronized (mUsers) {
             accounts = mUsers.get(userId);
             mUsers.remove(userId);
-            userUnlocked = mLocalUnlockedUsers.get(userId);
             mLocalUnlockedUsers.delete(userId);
         }
         if (accounts != null) {
@@ -1150,18 +1135,6 @@
                 accounts.accountsDb.close();
             }
         }
-        Log.i(TAG, "Removing database files for user " + userId);
-        File dbFile = new File(mInjector.getDeDatabaseName(userId));
-
-        AccountsDb.deleteDbFileWarnIfFailed(dbFile);
-        // Remove CE file if user is unlocked, or FBE is not enabled
-        boolean fbeEnabled = StorageManager.isFileEncryptedNativeOrEmulated();
-        if (!fbeEnabled || userUnlocked) {
-            File ceDb = new File(mInjector.getCeDatabaseName(userId));
-            if (ceDb.exists()) {
-                AccountsDb.deleteDbFileWarnIfFailed(ceDb);
-            }
-        }
     }
 
     @VisibleForTesting
@@ -4003,12 +3976,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 +4034,7 @@
             return getAccountsInternal(
                     accounts,
                     callingUid,
-                    callingPackage,
+                    opPackageName,
                     visibleAccountTypes,
                     includeUserManagedNotVisible);
         } finally {
@@ -4178,7 +4151,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 +4170,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 +4335,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 +4347,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 +4362,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 +5347,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 +5373,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 +5453,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/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 100d821..df250b1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -105,16 +105,16 @@
     }
 
     private void updateConstants() {
+        final String setting = Settings.Global.getString(mResolver,
+                Settings.Global.ACTIVITY_MANAGER_CONSTANTS);
         synchronized (mService) {
             try {
-                mParser.setString(Settings.Global.getString(mResolver,
-                        Settings.Global.ACTIVITY_MANAGER_CONSTANTS));
+                mParser.setString(setting);
             } catch (IllegalArgumentException e) {
                 // Failed to parse the settings string, log this and move on
                 // with defaults.
                 Slog.e("ActivityManagerConstants", "Bad activity manager config settings", e);
             }
-
             ENFORCE_BG_CHECK = mParser.getBoolean(KEY_ENFORCE_BG_CHECK, DEFAULT_ENFORCE_BG_CHECK);
             MAX_CACHED_PROCESSES = mParser.getInt(KEY_MAX_CACHED_PROCESSES,
                     DEFAULT_MAX_CACHED_PROCESSES);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6bf77ae..f722a8e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6691,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
@@ -21494,6 +21493,15 @@
         return success;
     }
 
+    private boolean isEphemeralLocked(int uid) {
+        String packages[] = mContext.getPackageManager().getPackagesForUid(uid);
+        if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid
+            return false;
+        }
+        return getPackageManagerInternalLocked().isPackageEphemeral(UserHandle.getUserId(uid),
+                packages[0]);
+    }
+
     private final void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) {
         final UidRecord.ChangeItem pendingChange;
         if (uidRec == null || uidRec.pendingChange == null) {
@@ -21534,7 +21542,7 @@
         pendingChange.change = change;
         pendingChange.processState = uidRec != null
                 ? uidRec.setProcState : ActivityManager.PROCESS_STATE_NONEXISTENT;
-        pendingChange.ephemeral = uidRec.ephemeral;
+        pendingChange.ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid);
         pendingChange.procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0;
 
         // Directly update the power manager, since we sit on top of it and it is critical
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index ebbce02..918747b 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -251,7 +251,8 @@
      *                       ActivityManagerInternal.APP_TRANSITION_* reasons.
      */
     void notifyTransitionStarting(SparseIntArray stackIdReasons) {
-        if (!isAnyTransitionActive() || mLoggedTransitionStarting) {
+        // TODO (b/36339388): Figure out why stackIdReasons can be null
+        if (stackIdReasons == null || !isAnyTransitionActive() || mLoggedTransitionStarting) {
             return;
         }
         mCurrentTransitionDelayMs = calculateCurrentDelay();
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 2e26bed..2b2471b 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1953,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..217515b 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
 import static android.Manifest.permission.START_ANY_ACTIVITY;
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
 import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
@@ -1612,8 +1613,8 @@
             return true;
         }
 
-        // Check if the caller can launch anything.
-        final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
+        // Check if the caller can manage activity stacks.
+        final int startAnyPerm = mService.checkPermission(MANAGE_ACTIVITY_STACKS, callingPid,
                 callingUid);
         if (startAnyPerm == PERMISSION_GRANTED) {
             if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
@@ -4654,14 +4655,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/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 36a913f..f927cce 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -857,17 +857,26 @@
 
         ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
 
-        String[] nativeProcs = NATIVE_STACKS_OF_INTEREST;
-        // don't dump native PIDs for background ANRs
-        File tracesFile = null;
+        // don't dump native PIDs for background ANRs unless it is the process of interest
+        String[] nativeProcs = null;
         if (isSilentANR) {
-            tracesFile = mService.dumpStackTraces(true, firstPids, null, lastPids,
-                null);
+            for (int i = 0; i < NATIVE_STACKS_OF_INTEREST.length; i++) {
+                if (NATIVE_STACKS_OF_INTEREST[i].equals(app.processName)) {
+                    nativeProcs = new String[] { app.processName };
+                    break;
+                }
+            }
         } else {
-            tracesFile = mService.dumpStackTraces(true, firstPids, processCpuTracker, lastPids,
-                nativeProcs);
+            nativeProcs = NATIVE_STACKS_OF_INTEREST;
         }
 
+        // For background ANRs, don't pass the ProcessCpuTracker to
+        // avoid spending 1/2 second collecting stats to rank lastPids.
+        File tracesFile = mService.dumpStackTraces(true, firstPids,
+                                                   (isSilentANR) ? null : processCpuTracker,
+                                                   (isSilentANR) ? null : lastPids,
+                                                   nativeProcs);
+
         String cpuInfo = null;
         if (ActivityManagerService.MONITOR_CPU_USAGE) {
             mService.updateCpuStatsNow();
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 2787895..dd3d4e0 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1152,17 +1152,14 @@
                     skip = true;
                 }
             }
-            final boolean visibleToInstantApps =
-                    (r.intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
             if (!skip && info.activityInfo.applicationInfo.isInstantApp()
-                    && !visibleToInstantApps
                     && r.callingUid != info.activityInfo.applicationInfo.uid) {
                 Slog.w(TAG, "Instant App Denial: receiving "
                         + r.intent
                         + " to " + component.flattenToShortString()
                         + " due to sender " + r.callerPackage
                         + " (uid " + r.callingUid + ")"
-                        + " not specifying FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS");
+                        + " Instant Apps do not support manifest receivers");
                 skip = true;
             }
             if (!skip && r.callerInstantApp
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/connectivity/tethering/IPv6TetheringInterfaceServices.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
index dec2f77..8c6430c 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
@@ -16,6 +16,8 @@
 
 package com.android.server.connectivity.tethering;
 
+import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
+
 import android.net.INetd;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
@@ -48,7 +50,6 @@
 public class IPv6TetheringInterfaceServices {
     private static final String TAG = IPv6TetheringInterfaceServices.class.getSimpleName();
     private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
-    private static final int RFC7421_IP_PREFIX_LENGTH = 64;
 
     private final String mIfName;
     private final INetworkManagementService mNMService;
@@ -124,7 +125,7 @@
             params.hasDefaultRoute = v6only.hasIPv6DefaultRoute();
 
             for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
-                if (linkAddr.getPrefixLength() != RFC7421_IP_PREFIX_LENGTH) continue;
+                if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue;
 
                 final IpPrefix prefix = new IpPrefix(
                         linkAddr.getAddress(), linkAddr.getPrefixLength());
@@ -206,7 +207,7 @@
             for (Inet6Address dns : deprecatedDnses) {
                 final String dnsString = dns.getHostAddress();
                 try {
-                    netd.interfaceDelAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH);
+                    netd.interfaceDelAddress(mIfName, dnsString, RFC7421_PREFIX_LENGTH);
                 } catch (ServiceSpecificException | RemoteException e) {
                     Log.e(TAG, "Failed to remove local dns IP: " + dnsString, e);
                 }
@@ -223,7 +224,7 @@
             for (Inet6Address dns : addedDnses) {
                 final String dnsString = dns.getHostAddress();
                 try {
-                    netd.interfaceAddAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH);
+                    netd.interfaceAddAddress(mIfName, dnsString, RFC7421_PREFIX_LENGTH);
                 } catch (ServiceSpecificException | RemoteException e) {
                     Log.e(TAG, "Failed to add local dns IP: " + dnsString, e);
                     newDnses.remove(dns);
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..bdba64f 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -97,7 +97,6 @@
     static final String TAG = "FingerprintService";
     static final boolean DEBUG = true;
     private static final String FP_DATA_DIR = "fpdata";
-    private static final String FINGERPRINT_HIDL = "fingerprint_hal";
     private static final int MSG_USER_SWITCHING = 10;
     private static final String ACTION_LOCKOUT_RESET =
             "com.android.server.fingerprint.ACTION_LOCKOUT_RESET";
@@ -206,7 +205,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;
@@ -219,7 +218,7 @@
     public synchronized IBiometricsFingerprint getFingerprintDaemon() {
         if (mDaemon == null) {
             try {
-                mDaemon = IBiometricsFingerprint.getService(FINGERPRINT_HIDL);
+                mDaemon = IBiometricsFingerprint.getService();
             } catch (java.util.NoSuchElementException e) {
                 // Service doesn't exist or cannot be opened. Logged below.
             } catch (RemoteException e) {
@@ -235,7 +234,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 +390,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 +404,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 +416,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 +446,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 +468,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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index be8aaf0..3dcc5d9 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -22,10 +22,10 @@
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL_ALL;
 import static android.service.notification.NotificationListenerService.REASON_CHANNEL_BANNED;
-import static android.service.notification.NotificationListenerService.REASON_DELEGATE_CANCEL;
-import static android.service.notification.NotificationListenerService.REASON_DELEGATE_CANCEL_ALL;
-import static android.service.notification.NotificationListenerService.REASON_DELEGATE_CLICK;
-import static android.service.notification.NotificationListenerService.REASON_DELEGATE_ERROR;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
+import static android.service.notification.NotificationListenerService.REASON_CLICK;
+import static android.service.notification.NotificationListenerService.REASON_ERROR;
 import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
 import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL;
 import static android.service.notification.NotificationListenerService.REASON_LISTENER_CANCEL_ALL;
@@ -88,7 +88,6 @@
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
 import android.media.IRingtonePlayer;
-import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -179,7 +178,6 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
-import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -543,7 +541,7 @@
         @Override
         public void onClearAll(int callingUid, int callingPid, int userId) {
             synchronized (mNotificationLock) {
-                cancelAllLocked(callingUid, callingPid, userId, REASON_DELEGATE_CANCEL_ALL, null,
+                cancelAllLocked(callingUid, callingPid, userId, REASON_CANCEL_ALL, null,
                         /*includeCurrentProfiles*/ true);
             }
         }
@@ -567,7 +565,7 @@
                 cancelNotification(callingUid, callingPid, sbn.getPackageName(), sbn.getTag(),
                         sbn.getId(), Notification.FLAG_AUTO_CANCEL,
                         Notification.FLAG_FOREGROUND_SERVICE, false, r.getUserId(),
-                        REASON_DELEGATE_CLICK, null);
+                        REASON_CLICK, null);
             }
         }
 
@@ -596,7 +594,7 @@
                 String pkg, String tag, int id, int userId) {
             cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
                     Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
-                    true, userId, REASON_DELEGATE_CANCEL, null);
+                    true, userId, REASON_CANCEL, null);
         }
 
         @Override
@@ -631,7 +629,7 @@
             Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id
                     + "; will crashApplication(uid=" + uid + ", pid=" + initialPid + ")");
             cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
-                    REASON_DELEGATE_ERROR, null);
+                    REASON_ERROR, null);
             long ident = Binder.clearCallingIdentity();
             try {
                 ActivityManager.getService().crashApplication(uid, initialPid, pkg, -1,
@@ -1658,8 +1656,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 +1672,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 +1716,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();
@@ -3349,7 +3373,7 @@
                         Slog.e(TAG, "Not posting notification without small icon: " + notification);
                         if (old != null && !old.isCanceled) {
                             mListeners.notifyRemovedLocked(n,
-                                    NotificationListenerService.REASON_DELEGATE_ERROR);
+                                    NotificationListenerService.REASON_ERROR);
                             mHandler.post(new Runnable() {
                                 @Override
                                 public void run() {
@@ -3996,8 +4020,8 @@
         // Record usage stats
         // TODO: add unbundling stats?
         switch (reason) {
-            case REASON_DELEGATE_CANCEL:
-            case REASON_DELEGATE_CANCEL_ALL:
+            case REASON_CANCEL:
+            case REASON_CANCEL_ALL:
             case REASON_LISTENER_CANCEL:
             case REASON_LISTENER_CANCEL_ALL:
                 mUsageStats.registerDismissedByUser(r);
@@ -4057,7 +4081,7 @@
 
                         // Ideally we'd do this in the caller of this method. However, that would
                         // require the caller to also find the notification.
-                        if (reason == REASON_DELEGATE_CLICK) {
+                        if (reason == REASON_CLICK) {
                             mUsageStats.registerClickedByUser(r);
                         }
 
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..db133f8 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -130,7 +130,7 @@
         // installed and should be removed
         final int storedOverlayInfosSize = storedOverlayInfos.size();
         for (int i = 0; i < storedOverlayInfosSize; i++) {
-            final OverlayInfo oi = storedOverlayInfos.get(i);
+            final OverlayInfo oi = storedOverlayInfos.valueAt(i);
             mSettings.remove(oi.packageName, oi.userId);
             removeIdmapIfPossible(oi);
             packagesToUpdateAssets.add(oi.targetPackageName);
@@ -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..d364d17 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;
 
@@ -292,8 +290,8 @@
                             PackageManagerService.REASON_BACKGROUND_DEXOPT,
                             /* force */ false)
                     : pm.performDexOptSecondary(pkg,
-                            PackageManagerServiceCompilerMapping.getFullCompilerFilter(),
-                            /* force */ true);
+                            PackageManagerService.REASON_BACKGROUND_DEXOPT,
+                            /* force */ false);
             if (success) {
                 // Dexopt succeeded, remove package from the list of failing ones.
                 synchronized (failedPackageNames) {
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/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index aac04da..126f8c4 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -214,6 +214,7 @@
         if (getAvailableSpace() > 0) {
             dexoptCommandCountExecuted++;
 
+            Log.d(TAG, "Next command: " + next);
             return next;
         } else {
             if (DEBUG_DEXOPT) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 0ec85aa..2e4a3a3 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -915,7 +915,7 @@
 
         // This is kind of hacky; we're creating a half-parsed package that is
         // straddled between the inherited and staged APKs.
-        final PackageLite pkg = new PackageLite(null, baseApk, null, null,
+        final PackageLite pkg = new PackageLite(null, baseApk, null, null, null, null,
                 splitPaths.toArray(new String[splitPaths.size()]), null);
         final boolean isForwardLocked =
                 (params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c27806d..2882d20 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -260,7 +260,6 @@
 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;
@@ -272,6 +271,7 @@
 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;
@@ -399,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;
@@ -2038,12 +2038,15 @@
         final boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
                 >= Build.VERSION_CODES.M;
 
+        final boolean instantApp = isInstantApp(pkg.packageName, userId);
+
         for (String permission : pkg.requestedPermissions) {
             final BasePermission bp;
             synchronized (mPackages) {
                 bp = mSettings.mPermissions.get(permission);
             }
             if (bp != null && (bp.isRuntime() || bp.isDevelopment())
+                    && (!instantApp || bp.isInstant())
                     && (grantedPermissions == null
                            || ArrayUtils.contains(grantedPermissions, permission))) {
                 final int flags = permissionsState.getPermissionFlags(permission, userId);
@@ -3348,7 +3351,7 @@
         //   * The system/shell/root can see metadata for any app
         //   * An installed app can see metadata for 1) other installed apps
         //     and 2) ephemeral apps that have explicitly interacted with it
-        //   * Ephemeral apps can only see their own metadata
+        //   * Ephemeral apps can only see their own data and exposed installed apps
         //   * Holding a signature permission allows seeing instant apps
         final int callingAppId = UserHandle.getAppId(Binder.getCallingUid());
         if (callingAppId != Process.SYSTEM_UID
@@ -3358,8 +3361,10 @@
                         Binder.getCallingUid()) != PackageManager.PERMISSION_GRANTED) {
             final String instantAppPackageName = getInstantAppPackageName(Binder.getCallingUid());
             if (instantAppPackageName != null) {
-                // ephemeral apps can only get information on themselves
-                if (!instantAppPackageName.equals(p.packageName)) {
+                // ephemeral apps can only get information on themselves or
+                // installed apps that are exposed.
+                if (!instantAppPackageName.equals(p.packageName)
+                        && (ps.getInstantApp(userId) || !p.visibleToInstantApps)) {
                     return null;
                 }
             } else {
@@ -8453,6 +8458,11 @@
         return mDexManager.dexoptSecondaryDex(packageName, compilerFilter, force);
     }
 
+    public boolean performDexOptSecondary(String packageName, int compileReason,
+            boolean force) {
+        return mDexManager.dexoptSecondaryDex(packageName, compileReason, force);
+    }
+
     /**
      * Reconcile the information we have about the secondary dex files belonging to
      * {@code packagName} and the actual dex files. For all dex files that were
@@ -10565,9 +10575,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,
@@ -12190,6 +12200,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) {
@@ -16845,11 +16860,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)) {
@@ -16885,6 +16900,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;
@@ -19674,6 +19690,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/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index a7349fc..25a596a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -147,6 +147,8 @@
                     return runSetHomeActivity();
                 case "get-privapp-permissions":
                     return runGetPrivappPermissions();
+                case "has-feature":
+                    return runHasFeature();
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -169,7 +171,8 @@
             if (file.isFile()) {
                 try {
                     ApkLite baseApk = PackageParser.parseApkLite(file, 0);
-                    PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null);
+                    PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
+                            null, null);
                     params.sessionParams.setSize(PackageHelper.calculateInstalledSize(
                             pkgLite, false, params.sessionParams.abiOverride));
                 } catch (PackageParserException | IOException e) {
@@ -1268,6 +1271,28 @@
         return 0;
     }
 
+    private int runHasFeature() {
+        final PrintWriter err = getErrPrintWriter();
+        final String featureName = getNextArg();
+        if (featureName == null) {
+            err.println("Error: expected FEATURE name");
+            return 1;
+        }
+        final String versionString = getNextArg();
+        try {
+            final int version = (versionString == null) ? 0 : Integer.parseInt(versionString);
+            final boolean hasFeature = mInterface.hasSystemFeature(featureName, version);
+            getOutPrintWriter().println(hasFeature);
+            return hasFeature ? 0 : 1;
+        } catch (NumberFormatException e) {
+            err.println("Error: illegal version number " + versionString);
+            return 1;
+        } catch (RemoteException e) {
+            err.println(e.toString());
+            return 1;
+        }
+    }
+
     private static String checkAbiArgument(String abi) {
         if (TextUtils.isEmpty(abi)) {
             throw new IllegalArgumentException("Missing ABI argument");
@@ -1649,6 +1674,9 @@
         pw.println("    Unsuspends the specified package (as user).");
         pw.println("  set-home-activity [--user USER_ID] TARGET-COMPONENT");
         pw.println("    set the default home activity (aka launcher).");
+        pw.println("  has-feature FEATURE_NAME [version]");
+        pw.println("   prints true and returns exit status 0 when system has a FEATURE_NAME,");
+        pw.println("   otherwise prints false and returns exit status 1");
         pw.println();
         Intent.printIntentArgsHelp(pw , "");
     }
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 b9fcf4e..a31258c3 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1493,6 +1493,10 @@
                     listeners[i].onUserRestrictionsChanged(userId,
                             newRestrictionsFinal, prevRestrictionsFinal);
                 }
+
+                final Intent broadcast = new Intent(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)
+                        .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                mContext.sendBroadcastAsUser(broadcast, UserHandle.of(userId));
             }
         });
     }
@@ -1810,8 +1814,8 @@
                             if (type == XmlPullParser.START_TAG) {
                                 if (parser.getName().equals(TAG_RESTRICTIONS)) {
                                     synchronized (mGuestRestrictions) {
-                                        mGuestRestrictions.putAll(
-                                                UserRestrictionsUtils.readRestrictions(parser));
+                                        UserRestrictionsUtils
+                                                .readRestrictions(parser, mGuestRestrictions);
                                     }
                                 }
                                 break;
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 36eba8e..cb2ed6e 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -207,6 +207,7 @@
     }
 
     public static void readRestrictions(XmlPullParser parser, Bundle restrictions) {
+        restrictions.clear();
         for (String key : USER_RESTRICTIONS) {
             final String value = parser.getAttributeValue(null, key);
             if (value != null) {
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..a904d17 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -279,6 +279,17 @@
      * @return true if all secondary dex files were processed successfully (compiled or skipped
      *         because they don't need to be compiled)..
      */
+    public boolean dexoptSecondaryDex(String packageName, int compilerReason, boolean force) {
+        return dexoptSecondaryDex(packageName,
+                PackageManagerServiceCompilerMapping.getCompilerFilterForReason(compilerReason),
+                force);
+    }
+
+    /**
+     * Perform dexopt on the package {@code packageName} secondary dex files.
+     * @return true if all secondary dex files were processed successfully (compiled or skipped
+     *         because they don't need to be compiled)..
+     */
     public boolean dexoptSecondaryDex(String packageName, String compilerFilter, boolean force) {
         // Select the dex optimizer based on the force parameter.
         // Forced compilation is done through ForcedUpdatePackageDexOptimizer which will adjust
@@ -432,7 +443,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/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/vr/CompatibilityDisplay.java b/services/core/java/com/android/server/vr/CompatibilityDisplay.java
index 5e17daa..8f95cc7 100644
--- a/services/core/java/com/android/server/vr/CompatibilityDisplay.java
+++ b/services/core/java/com/android/server/vr/CompatibilityDisplay.java
@@ -1,6 +1,8 @@
 
 package com.android.server.vr;
 
+import static android.view.Display.INVALID_DISPLAY;
+
 import android.app.Service;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -86,10 +88,8 @@
                 startVirtualDisplay();
             }
         } else {
-            // TODO: Remove conditional when launching apps 2D doesn't force VrMode to stop.
-            if (!DEBUG) {
-                stopVirtualDisplay();
-            }
+            // Stop virtual display to test exit condition
+            stopVirtualDisplay();
         }
     }
 
@@ -138,6 +138,19 @@
         }
     }
 
+    public int getVirtualDisplayId() {
+        synchronized(vdLock) {
+            if (mVirtualDisplay != null) {
+                int virtualDisplayId = mVirtualDisplay.getDisplay().getDisplayId();
+                if (DEBUG) {
+                    Log.e(TAG, "VD id: " + virtualDisplayId);
+                }
+                return virtualDisplayId;
+            }
+        }
+        return INVALID_DISPLAY;
+    }
+
     private void startVirtualDisplay() {
         if (DEBUG) {
             Log.d(TAG, "Request to start VD, DM:" + mDisplayManager);
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index 58e4bdc..210aa44 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -90,6 +90,15 @@
     public abstract void setPersistentVrModeEnabled(boolean enabled);
 
     /**
+     * Return {@link android.view.Display.INVALID_DISPLAY} if there exists no virtual display
+     * currently or the display id of the current virtual display.
+     *
+     * @return {@link android.view.Display.INVALID_DISPLAY} if there is no virtual display
+     * currently, else return the display id of the virtual display
+     */
+    public abstract int getCompatibilityDisplayId();
+
+    /**
      * Adds listener that reports state changes to persistent VR mode.
      */
     public abstract void addPersistentVrModeStateListener(PersistentVrStateListener listener);
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 8a23173..a00115c 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -15,6 +15,8 @@
  */
 package com.android.server.vr;
 
+import static android.view.Display.INVALID_DISPLAY;
+
 import android.Manifest;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
@@ -392,6 +394,11 @@
         }
 
         @Override
+        public int getCompatibilityDisplayId() {
+            return VrManagerService.this.getCompatibilityDisplayId();
+        }
+
+        @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                     != PackageManager.PERMISSION_GRANTED) {
@@ -495,6 +502,11 @@
         }
 
         @Override
+        public int getCompatibilityDisplayId() {
+            return VrManagerService.this.getCompatibilityDisplayId();
+        }
+
+        @Override
         public void addPersistentVrModeStateListener(PersistentVrStateListener listener) {
             VrManagerService.this.addPersistentVrModeStateListener(listener);
         }
@@ -1054,6 +1066,14 @@
         }
     }
 
+    private int getCompatibilityDisplayId() {
+        if (mCompatibilityDisplay != null) {
+            return mCompatibilityDisplay.getVirtualDisplayId();
+        }
+        Slog.w(TAG, "CompatibilityDisplay is null!");
+        return INVALID_DISPLAY;
+    }
+
     private void setPersistentModeAndNotifyListenersLocked(boolean enabled) {
         if (mPersistentVrModeEnabled == enabled) {
             return;
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/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 2f221df..c20ee97 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -163,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<>();
 
@@ -970,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();
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..30e0ded 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -28,7 +28,6 @@
 
 import android.content.ClipData;
 import android.content.Context;
-import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Binder;
@@ -39,6 +38,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.util.MergedConfiguration;
 import android.util.Slog;
 import android.view.Display;
 import android.view.IWindow;
@@ -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);
     }
@@ -223,22 +216,18 @@
             int requestedWidth, int requestedHeight, int viewFlags,
             int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
             Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
-            Configuration outConfig, Surface outSurface) {
+            MergedConfiguration mergedConfiguration, Surface outSurface) {
         if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
         int res = mService.relayoutWindow(this, window, seq, attrs,
                 requestedWidth, requestedHeight, viewFlags, flags,
                 outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
-                outStableInsets, outsets, outBackdropFrame, outConfig, outSurface);
+                outStableInsets, outsets, outBackdropFrame, mergedConfiguration, outSurface);
         if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
                 + Binder.getCallingPid());
         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/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index c0598ca..04403e2 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -27,7 +27,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 import android.app.ActivityManager.TaskDescription;
-import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -38,6 +37,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.util.MergedConfiguration;
 import android.util.Slog;
 import android.view.IWindowSession;
 import android.view.Surface;
@@ -78,7 +78,7 @@
         final Surface surface = new Surface();
         final Rect tmpRect = new Rect();
         final Rect tmpFrame = new Rect();
-        final Configuration tmpConfiguration = new Configuration();
+        final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration();
         int fillBackgroundColor = Color.WHITE;
         synchronized (service.mWindowMap) {
             layoutParams.type = TYPE_APPLICATION_STARTING;
@@ -122,7 +122,7 @@
         window.setOuter(snapshotSurface);
         try {
             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame,
-                    tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpConfiguration,
+                    tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpMergedConfiguration,
                     surface);
         } catch (RemoteException e) {
             // Local call.
@@ -221,9 +221,9 @@
 
         @Override
         public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
-                Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig,
-                Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar,
-                int displayId) {
+                Rect stableInsets, Rect outsets, boolean reportDraw,
+                MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
+                boolean alwaysConsumeNavBar, int displayId) {
             if (reportDraw) {
                 sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget();
             }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 64614fe..7539cd4 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;
@@ -151,6 +150,7 @@
 import android.os.WorkSource;
 import android.provider.Settings;
 import android.util.ArraySet;
+import android.util.MergedConfiguration;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
@@ -592,11 +592,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();
@@ -841,9 +836,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.
@@ -1817,70 +1809,12 @@
         }
     }
 
-    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,
             Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
             Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
-            Configuration outConfig, Surface outSurface) {
+            MergedConfiguration mergedConfiguration, Surface outSurface) {
         int result = 0;
         boolean configChanged;
         boolean hasStatusBarPermission =
@@ -1992,7 +1926,7 @@
             if (viewVisibility == View.VISIBLE &&
                     (win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                             || !win.mAppToken.clientHidden)) {
-                result = win.relayoutVisibleWindow(outConfig, result, attrChanges,
+                result = win.relayoutVisibleWindow(mergedConfiguration, result, attrChanges,
                         oldVisibility);
                 try {
                     result = createSurfaceControl(outSurface, result, win, winAnimator);
@@ -2219,23 +2153,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();
 
@@ -2461,10 +2378,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()) {
@@ -2505,7 +2422,7 @@
                 if (dc.isDefaultDisplay) {
                     mPolicy.setCurrentOrientationLw(req);
                 }
-                if (updateRotationUncheckedLocked(inTransaction, displayId)) {
+                if (dc.updateRotationUnchecked(inTransaction)) {
                     // changed
                     return true;
                 }
@@ -3843,10 +3760,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();
                 }
             }
         }
@@ -3862,9 +3781,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();
@@ -3872,234 +3792,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--) {
-            final RotationWatcher rotationWatcher = mRotationWatchers.get(i);
-            if (rotationWatcher.mDisplayId == displayId) {
-                try {
-                    rotationWatcher.mWatcher.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) {
@@ -4572,7 +4271,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;
@@ -4597,297 +4296,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() {
@@ -6099,7 +5510,7 @@
                 displayId);
         final Configuration currentDisplayConfig = displayContent.getConfiguration();
         mTempConfiguration.setTo(currentDisplayConfig);
-        computeScreenConfigurationLocked(mTempConfiguration, displayId);
+        displayContent.computeScreenConfiguration(mTempConfiguration);
         configChanged |= currentDisplayConfig.diff(mTempConfiguration) != 0;
 
         if (configChanged) {
@@ -6562,7 +5973,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) {
@@ -6605,8 +6017,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) {
@@ -7642,9 +7055,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();
             }
         }
     }
@@ -7670,6 +7084,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..ca5d551 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -114,6 +114,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.util.MergedConfiguration;
 import android.util.DisplayMetrics;
 import android.util.Slog;
 import android.util.TimeUtils;
@@ -1273,8 +1274,8 @@
 
     void prelayout() {
         if (mEnforceSizeCompat) {
-            mGlobalScale = mService.mCompatibleScreenScale;
-            mInvGlobalScale = 1/mGlobalScale;
+            mGlobalScale = getDisplayContent().mCompatibleScreenScale;
+            mInvGlobalScale = 1 / mGlobalScale;
         } else {
             mGlobalScale = mInvGlobalScale = 1;
         }
@@ -2265,7 +2266,7 @@
         }
     }
 
-    void prepareWindowToDisplayDuringRelayout(Configuration outConfig) {
+    void prepareWindowToDisplayDuringRelayout(MergedConfiguration mergedConfiguration) {
         if ((mAttrs.softInputMode & SOFT_INPUT_MASK_ADJUST)
                 == SOFT_INPUT_ADJUST_RESIZE) {
             mLayoutNeeded = true;
@@ -2278,10 +2279,13 @@
             mTurnOnScreen = true;
         }
         if (isConfigChanged()) {
-            outConfig.setTo(getConfiguration());
-            if (DEBUG_CONFIGURATION) Slog.i(TAG, "Window " + this + " visible with new config: "
-                    + outConfig);
-            mLastReportedConfiguration.setTo(outConfig);
+            final Configuration globalConfig = mService.mRoot.getConfiguration();
+            final Configuration overrideConfig = getMergedOverrideConfiguration();
+            mergedConfiguration.setConfiguration(globalConfig, overrideConfig);
+            if (DEBUG_CONFIGURATION) Slog.i(TAG, "Window " + this
+                    + " visible with new global config: " + globalConfig
+                    + " merged override config: " + overrideConfig);
+            mLastReportedConfiguration.setTo(getConfiguration());
         }
     }
 
@@ -3027,12 +3031,13 @@
         try {
             if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
                     + ": " + mCompatFrame);
-            final Configuration newConfig;
+            final MergedConfiguration mergedConfiguration;
             if (isConfigChanged()) {
-                newConfig = new Configuration(getConfiguration());
-                mLastReportedConfiguration.setTo(newConfig);
+                mergedConfiguration = new MergedConfiguration(mService.mRoot.getConfiguration(),
+                        getMergedOverrideConfiguration());
+                mLastReportedConfiguration.setTo(mergedConfiguration.getMergedConfiguration());
             } else {
-                newConfig = null;
+                mergedConfiguration = null;
             }
             if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == DRAW_PENDING)
                 Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
@@ -3054,7 +3059,7 @@
                     public void run() {
                         try {
                             dispatchResized(frame, overscanInsets, contentInsets, visibleInsets,
-                                    stableInsets, outsets, reportDraw, newConfig,
+                                    stableInsets, outsets, reportDraw, mergedConfiguration,
                                     reportOrientation, displayId);
                         } catch (RemoteException e) {
                             // Not a remote call, RemoteException won't be raised.
@@ -3063,7 +3068,7 @@
                 });
             } else {
                 dispatchResized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets,
-                        outsets, reportDraw, newConfig, reportOrientation, displayId);
+                        outsets, reportDraw, mergedConfiguration, reportOrientation, displayId);
             }
 
             //TODO (multidisplay): Accessibility supported only for the default display.
@@ -3120,14 +3125,14 @@
 
     private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
             Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
-            Configuration newConfig, boolean reportOrientation, int displayId)
+            MergedConfiguration mergedConfiguration, boolean reportOrientation, int displayId)
             throws RemoteException {
         final boolean forceRelayout = isDragResizeChanged() || mResizedWhileNotDragResizing
                 || reportOrientation;
 
         mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets,
-                reportDraw, newConfig, getBackdropFrame(frame),
-                forceRelayout, mPolicy.isNavBarForcedShownLw(this), displayId);
+                reportDraw, mergedConfiguration, getBackdropFrame(frame), forceRelayout,
+                mPolicy.isNavBarForcedShownLw(this), displayId);
         mDragResizingChangeReported = true;
     }
 
@@ -4341,8 +4346,8 @@
         return !mLastSurfaceInsets.equals(mAttrs.surfaceInsets);
     }
 
-    int relayoutVisibleWindow(Configuration outConfig, int result,
-            int attrChanges, int oldVisibility) {
+    int relayoutVisibleWindow(MergedConfiguration mergedConfiguration, int result, int attrChanges,
+            int oldVisibility) {
         result |= !isVisibleLw() ? RELAYOUT_RES_FIRST_TIME : 0;
         if (mAnimatingExit) {
             Slog.d(TAG, "relayoutVisibleWindow: " + this + " mAnimatingExit=true, mRemoveOnExit="
@@ -4363,7 +4368,7 @@
 
         mWinAnimator.mEnteringAnimation = true;
         if ((result & RELAYOUT_RES_FIRST_TIME) != 0) {
-            prepareWindowToDisplayDuringRelayout(outConfig);
+            prepareWindowToDisplayDuringRelayout(mergedConfiguration);
         }
         if ((attrChanges & FORMAT_CHANGED) != 0) {
             // If the format can't be changed in place, preserve the old surface until the app draws
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index c9863c5..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 {
@@ -834,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/jni/Android.mk b/services/core/jni/Android.mk
index ac95db5..b5ed266 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -65,6 +65,7 @@
     libinputflinger \
     libinputservice \
     libsensorservice \
+    libsensorservicehidl \
     libskia \
     libgui \
     libusbhost \
@@ -88,5 +89,6 @@
     android.hardware.tv.input@1.0 \
     android.hardware.vibrator@1.0 \
     android.hardware.vr@1.0 \
+    android.frameworks.sensorservice@1.0 \
 
-LOCAL_STATIC_LIBRARIES += libscrypt_static
\ No newline at end of file
+LOCAL_STATIC_LIBRARIES += libscrypt_static
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 3120af56..8ad88ed 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -17,7 +17,10 @@
 #include <jni.h>
 #include <JNIHelp.h>
 
+#include <hidl/HidlTransportSupport.h>
+
 #include <sensorservice/SensorService.h>
+#include <sensorservicehidl/SensorManager.h>
 
 #include <cutils/properties.h>
 #include <utils/Log.h>
@@ -32,6 +35,21 @@
     if (strcmp(propBuf, "1") == 0) {
         SensorService::instantiate();
     }
+
+}
+
+static void android_server_SystemServer_startHidlServices(JNIEnv* /* env */, jobject /* clazz */) {
+    using ::android::frameworks::sensorservice::V1_0::ISensorManager;
+    using ::android::frameworks::sensorservice::V1_0::implementation::SensorManager;
+    using ::android::hardware::configureRpcThreadpool;
+
+    configureRpcThreadpool(1, false /* callerWillJoin */);
+    sp<ISensorManager> sensorService = new SensorManager();
+    status_t err = sensorService->registerAsService();
+    if (err != OK) {
+        ALOGE("Cannot register ::android::frameworks::sensorservice::V1_0::"
+              "implementation::SensorManager: %d", err);
+    }
 }
 
 /*
@@ -40,6 +58,7 @@
 static const JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
     { "startSensorService", "()V", (void*) android_server_SystemServer_startSensorService },
+    { "startHidlServices", "()V", (void*) android_server_SystemServer_startHidlServices },
 };
 
 int register_android_server_SystemServer(JNIEnv* env)
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 a8423e2..e6e0242 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;
@@ -235,16 +236,24 @@
     private final boolean mRuntimeRestart;
 
     private static final String START_SENSOR_SERVICE = "StartSensorService";
+    private static final String START_HIDL_SERVICES = "StartHidlServices";
+
+
     private Future<?> mSensorServiceStart;
     private Future<?> mZygotePreload;
 
-
     /**
      * Start the sensor service. This is a blocking call and can take time.
      */
     private static native void startSensorService();
 
     /**
+     * Start all HIDL services that are run inside the system server. This
+     * may take some time.
+     */
+    private static native void startHidlServices();
+
+    /**
      * The main entry point from zygote.
      */
     public static void main(String[] args) {
@@ -610,6 +619,7 @@
             startSensorService();
             traceLog.traceEnd();
         }, START_SENSOR_SERVICE);
+
     }
 
     /**
@@ -637,6 +647,14 @@
         traceBeginAndSlog("StartWebViewUpdateService");
         mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
         traceEnd();
+
+        // Start receiving calls from HIDL services. Start in in a separate thread
+        // because it need to connect to SensorManager.
+        SystemServerInitThreadPool.get().submit(() -> {
+            traceBeginAndSlog(START_HIDL_SERVICES);
+            startHidlServices();
+            traceEnd();
+        }, START_HIDL_SERVICES);
     }
 
     /**
@@ -1428,11 +1446,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..c670782 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -16,6 +16,8 @@
 
 package android.net.ip;
 
+import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
+
 import com.android.internal.util.MessageUtils;
 import com.android.internal.util.WakeupMessage;
 
@@ -23,6 +25,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,11 +37,14 @@
 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.system.OsConstants;
 import android.text.TextUtils;
 import android.util.LocalLog;
 import android.util.Log;
@@ -631,6 +637,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();
@@ -1018,16 +1031,39 @@
         return true;
     }
 
-    private boolean startIPv6() {
-        // Set privacy extensions.
+    private void enableInterfaceIPv6PrivacyExtensions() {
+        final String PREFER_TEMPADDRS = "2";
+        NetdService.run((INetd netd) -> {
+                netd.setProcSysNet(
+                        INetd.IPV6, INetd.CONF, mInterfaceName, "use_tempaddr", PREFER_TEMPADDRS);
+            });
+    }
+
+    private void setInterfaceIPv6RaRtInfoMaxPlen(int plen) {
+        // Setting RIO max plen is best effort. Catch and ignore most exceptions.
         try {
-            mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
+            NetdService.run((INetd netd) -> {
+                    netd.setProcSysNet(
+                            INetd.IPV6, INetd.CONF, mInterfaceName, "accept_ra_rt_info_max_plen",
+                            Integer.toString(plen));
+                });
+        } catch (ServiceSpecificException e) {
+            // Old kernel versions without support for RIOs do not export accept_ra_rt_info_max_plen
+            // in the /proc filesystem. If the kernel supports RIOs we should never see any other
+            // type of error.
+            if (e.errorCode != OsConstants.ENOENT) {
+                logError("unexpected error setting accept_ra_rt_info_max_plen %s", e);
+            }
+        }
+    }
+
+    private boolean startIPv6() {
+        try {
+            enableInterfaceIPv6PrivacyExtensions();
+            setInterfaceIPv6RaRtInfoMaxPlen(RFC7421_PREFIX_LENGTH);
             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/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
index 362f757..26f3050 100644
--- a/services/net/java/android/net/util/NetworkConstants.java
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -97,6 +97,7 @@
     public static final int IPV6_SRC_ADDR_OFFSET = 8;
     public static final int IPV6_DST_ADDR_OFFSET = 24;
     public static final int IPV6_ADDR_LEN = 16;
+    public static final int RFC7421_PREFIX_LENGTH = 64;
 
     /**
      * ICMPv6 constants.
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/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/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 8987ac1..721d337 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -157,7 +157,6 @@
         super.setUp();
 
         mContext = getContext();
-
         when(mContext.packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN)))
                 .thenReturn(true);
 
@@ -174,6 +173,12 @@
         setUpUserManager();
     }
 
+    @Override
+    protected void tearDown() throws Exception {
+        flushTasks();
+        super.tearDown();
+    }
+
     private void initializeDpms() {
         // Need clearCallingIdentity() to pass permission checks.
         final long ident = mContext.binder.clearCallingIdentity();
@@ -1246,7 +1251,7 @@
         mContext.applicationInfo = new ApplicationInfo();
         mContext.callerPermissions.add(permission.MANAGE_USERS);
         mContext.packageName = "com.android.frameworks.servicestests";
-        mContext.userContexts.put(user, mContext);
+        mContext.addPackageContext(user, mContext);
         when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
 
         StringParceledListSlice oneCert = asSlice(new String[] {"1"});
@@ -3966,121 +3971,168 @@
     }
 
     public void testGetOwnerInstalledCaCertsForDeviceOwner() throws Exception {
+        mContext.packageName = mRealTestContext.getPackageName();
         setDeviceOwner();
 
-        mContext.packageName = admin1.getPackageName();
-        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
-        verifyCanGetOwnerInstalledCaCerts(admin1);
+        final DpmMockContext caller = new DpmMockContext(mRealTestContext, "test-caller");
+        caller.packageName = admin1.getPackageName();
+        caller.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+        verifyCanGetOwnerInstalledCaCerts(admin1, caller);
     }
 
     public void testGetOwnerInstalledCaCertsForProfileOwner() throws Exception {
+        mContext.packageName = mRealTestContext.getPackageName();
         setAsProfileOwner(admin1);
 
-        mContext.packageName = admin1.getPackageName();
-        verifyCanGetOwnerInstalledCaCerts(admin1);
-        verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(admin1);
+        final DpmMockContext caller = new DpmMockContext(mRealTestContext, "test-caller");
+        caller.packageName = admin1.getPackageName();
+        caller.binder.callingUid = DpmMockContext.CALLER_UID;
+
+        verifyCanGetOwnerInstalledCaCerts(admin1, caller);
+        verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(admin1, caller);
     }
 
     public void testGetOwnerInstalledCaCertsForDelegate() throws Exception {
+        mContext.packageName = mRealTestContext.getPackageName();
         setAsProfileOwner(admin1);
 
         final String delegate = "com.example.delegate";
         final int delegateUid = setupPackageInPackageManager(delegate, 20988);
         dpm.setCertInstallerPackage(admin1, delegate);
 
-        mContext.packageName = delegate;
-        mContext.binder.callingUid = delegateUid;
-        verifyCanGetOwnerInstalledCaCerts(null);
-        verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(null);
+        final DpmMockContext caller = new DpmMockContext(mRealTestContext, "test-caller");
+        caller.packageName = delegate;
+        caller.binder.callingUid = delegateUid;
+
+        verifyCanGetOwnerInstalledCaCerts(null, caller);
+        verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(null, caller);
     }
 
-    private void verifyCanGetOwnerInstalledCaCerts(ComponentName caller) throws Exception {
-        final UserHandle user = UserHandle.getUserHandleForUid(mContext.binder.callingUid);
-        final int ownerUid = user.equals(UserHandle.SYSTEM) ?
-                DpmMockContext.CALLER_SYSTEM_USER_UID : DpmMockContext.CALLER_UID;
-
-        mContext.applicationInfo = new ApplicationInfo();
-        mContext.userContexts.put(user, mContext);
-        when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
-
-        // Install a CA cert.
+    private void verifyCanGetOwnerInstalledCaCerts(
+            final ComponentName caller, final DpmMockContext callerContext) throws Exception {
         final String alias = "cert";
         final byte[] caCert = TEST_CA.getBytes();
-        when(mContext.keyChainConnection.getService().installCaCertificate(caCert))
-                .thenReturn(alias);
-        assertTrue(dpm.installCaCert(caller, caCert));
-        when(mContext.keyChainConnection.getService().getUserCaAliases())
-                .thenReturn(asSlice(new String[] {alias}));
-        mContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED));
+
+        // device admin (used for posting the tls notification)
+        final DpmMockContext admin1Context;
+        if (admin1.getPackageName().equals(callerContext.getPackageName())) {
+            admin1Context = callerContext;
+        } else {
+            admin1Context = new DpmMockContext(mRealTestContext, "test-admin");
+            admin1Context.packageName = admin1.getPackageName();
+            admin1Context.applicationInfo = new ApplicationInfo();
+        }
+        when(admin1Context.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
+
+        // caller: device admin or delegated certificate installer
+        callerContext.applicationInfo = new ApplicationInfo();
+        final UserHandle callerUser = callerContext.binder.getCallingUserHandle();
+
+        // system_server
+        final DpmMockContext serviceContext = mContext;
+        serviceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+        serviceContext.addPackageContext(callerUser, admin1Context);
+        serviceContext.addPackageContext(callerUser, callerContext);
+
+        // Install a CA cert.
+        runAsCaller(callerContext, dpms, (dpm) -> {
+            when(mContext.keyChainConnection.getService().installCaCertificate(caCert))
+                        .thenReturn(alias);
+            assertTrue(dpm.installCaCert(caller, caCert));
+            when(mContext.keyChainConnection.getService().getUserCaAliases())
+                    .thenReturn(asSlice(new String[] {alias}));
+
+        });
+
+        serviceContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED)
+                .putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier()));
         flushTasks();
 
+        final List<String> ownerInstalledCaCerts = new ArrayList<>();
+
         // Device Owner / Profile Owner can find out which CA certs were installed by itself.
-        final String packageName = mContext.packageName;
-        mContext.packageName = admin1.getPackageName();
-        final long callerIdentity = mContext.binder.clearCallingIdentity();
-        mContext.binder.callingUid = ownerUid;
-        List<String> ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(user);
-        assertNotNull(ownerInstalledCaCerts);
-        assertEquals(1, ownerInstalledCaCerts.size());
-        assertTrue(ownerInstalledCaCerts.contains(alias));
+        runAsCaller(admin1Context, dpms, (dpm) -> {
+            final List<String> installedCaCerts = dpm.getOwnerInstalledCaCerts(callerUser);
+            assertEquals(Arrays.asList(alias), installedCaCerts);
+            ownerInstalledCaCerts.addAll(installedCaCerts);
+        });
 
         // Restarting the DPMS should not lose information.
         initializeDpms();
-        assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(user));
+        runAsCaller(admin1Context, dpms, (dpm) -> {
+            assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(callerUser));
+        });
 
         // System can find out which CA certs were installed by the Device Owner / Profile Owner.
-        mContext.packageName = "com.android.frameworks.servicestests";
-        mContext.binder.clearCallingIdentity();
-        assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(user));
+        runAsCaller(serviceContext, dpms, (dpm) -> {
+            assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(callerUser));
 
-        // Remove the CA cert.
-        mContext.packageName = packageName;
-        mContext.binder.restoreCallingIdentity(callerIdentity);
-        reset(mContext.keyChainConnection.getService());
-        mContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED));
+            // Remove the CA cert.
+            reset(mContext.keyChainConnection.getService());
+        });
+
+        serviceContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED)
+                .putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier()));
         flushTasks();
 
         // Verify that the CA cert is no longer reported as installed by the Device Owner / Profile
         // Owner.
-        mContext.packageName = admin1.getPackageName();
-        mContext.binder.callingUid = ownerUid;
-        ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(user);
-        assertNotNull(ownerInstalledCaCerts);
-        assertTrue(ownerInstalledCaCerts.isEmpty());
-
-        mContext.packageName = packageName;
-        mContext.binder.restoreCallingIdentity(callerIdentity);
+        runAsCaller(admin1Context, dpms, (dpm) -> {
+            MoreAsserts.assertEmpty(dpm.getOwnerInstalledCaCerts(callerUser));
+        });
     }
 
-    private void verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(ComponentName caller)
-            throws Exception {
-        final UserHandle user = UserHandle.of(DpmMockContext.CALLER_USER_HANDLE);
-
-        mContext.applicationInfo = new ApplicationInfo();
-        mContext.userContexts.put(user, mContext);
-        when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
-
-        // Install a CA cert.
+    private void verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(
+            final ComponentName callerName, final DpmMockContext callerContext) throws Exception {
         final String alias = "cert";
         final byte[] caCert = TEST_CA.getBytes();
-        when(mContext.keyChainConnection.getService().installCaCertificate(caCert))
-                .thenReturn(alias);
-        assertTrue(dpm.installCaCert(caller, caCert));
-        when(mContext.keyChainConnection.getService().getUserCaAliases())
+
+        // device admin (used for posting the tls notification)
+        final DpmMockContext admin1Context;
+        if (admin1.getPackageName().equals(callerContext.getPackageName())) {
+            admin1Context = callerContext;
+        } else {
+            admin1Context = new DpmMockContext(mRealTestContext, "test-admin");
+            admin1Context.packageName = admin1.getPackageName();
+            admin1Context.applicationInfo = new ApplicationInfo();
+        }
+        when(admin1Context.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
+
+        // caller: device admin or delegated certificate installer
+        callerContext.applicationInfo = new ApplicationInfo();
+        final UserHandle callerUser = callerContext.binder.getCallingUserHandle();
+
+        // system_server
+        final DpmMockContext serviceContext = mContext;
+        serviceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+        serviceContext.addPackageContext(callerUser, admin1Context);
+        serviceContext.addPackageContext(callerUser, callerContext);
+
+        // Install a CA cert as caller
+        runAsCaller(callerContext, dpms, (dpm) -> {
+            when(mContext.keyChainConnection.getService().installCaCertificate(caCert))
+                    .thenReturn(alias);
+            assertTrue(dpm.installCaCert(callerName, caCert));
+        });
+
+        // Fake the CA cert as having been installed
+        when(serviceContext.keyChainConnection.getService().getUserCaAliases())
                 .thenReturn(asSlice(new String[] {alias}));
-        mContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED));
+        serviceContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED)
+                .putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier()));
         flushTasks();
 
-        // Removing the Profile Owner should clear the information which CA certs were installed
-        // by it.
-        mContext.packageName = admin1.getPackageName();
-        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
-        dpm.clearProfileOwner(admin1);
-        mContext.packageName = "com.android.frameworks.servicestests";
-        mContext.binder.clearCallingIdentity();
-        final List<String> ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(user);
-        assertNotNull(ownerInstalledCaCerts);
-        assertTrue(ownerInstalledCaCerts.isEmpty());
+        // Removing the Profile Owner should clear the information on which CA certs were installed
+        runAsCaller(admin1Context, dpms, (dpm) -> {
+            dpm.clearProfileOwner(admin1);
+        });
+
+        runAsCaller(serviceContext, dpms, (dpm) -> {
+            final List<String> ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(callerUser);
+            assertNotNull(ownerInstalledCaCerts);
+            assertTrue(ownerInstalledCaCerts.isEmpty());
+        });
     }
 
     private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) {
@@ -4147,29 +4199,11 @@
     }
 
     private void flushTasks() throws Exception {
-        Boolean tasksFlushed[] = new Boolean[] {false};
-        final Runnable tasksFlushedNotifier = () -> {
-            synchronized (tasksFlushed) {
-                tasksFlushed[0] = true;
-                tasksFlushed.notify();
-            }
-        };
+        dpms.mHandler.runWithScissors(() -> {}, 0 /*now*/);
+        dpms.mBackgroundHandler.runWithScissors(() -> {}, 0 /*now*/);
 
-        // Flush main thread handler.
-        dpms.mHandler.post(tasksFlushedNotifier);
-        synchronized (tasksFlushed) {
-            if (!tasksFlushed[0]) {
-                tasksFlushed.wait();
-            }
-        }
-
-        // Flush background thread handler.
-        tasksFlushed[0] = false;
-        dpms.mBackgroundHandler.post(tasksFlushedNotifier);
-        synchronized (tasksFlushed) {
-            if (!tasksFlushed[0]) {
-                tasksFlushed.wait();
-            }
-        }
+        // We can't let exceptions happen on the background thread. Throw them here if they happen
+        // so they still cause the test to fail despite being suppressed.
+        mContext.rethrowBackgroundBroadcastExceptions();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 7d017c5..1ea12d8 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -48,6 +48,7 @@
 import android.test.mock.MockContentResolver;
 import android.test.mock.MockContext;
 import android.util.ArrayMap;
+import android.util.Pair;
 import android.view.IWindowManager;
 
 import com.android.internal.widget.LockPatternUtils;
@@ -61,6 +62,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
 
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
@@ -111,6 +113,7 @@
     public static class MockBinder {
         public int callingUid = CALLER_UID;
         public int callingPid = CALLER_PID;
+        public final Map<Integer, List<String>> callingPermissions = new ArrayMap<>();
 
         public long clearCallingIdentity() {
             final long token = (((long) callingUid) << 32) | (callingPid);
@@ -303,14 +306,19 @@
     /** Note this is a partial mock, not a real mock. */
     public final PackageManager packageManager;
 
+    /** TODO: Migrate everything to use {@link #permissions} to avoid confusion. */
+    @Deprecated
     public final List<String> callerPermissions = new ArrayList<>();
 
+    /** Less confusing alias for {@link #callerPermissions}. */
+    public final List<String> permissions = callerPermissions;
+
     private final ArrayList<UserInfo> mUserInfos = new ArrayList<>();
 
     public final BuildMock buildMock = new BuildMock();
 
     /** Optional mapping of other user contexts for {@link #createPackageContextAsUser} to return */
-    public final Map<UserHandle, Context> userContexts = new ArrayMap<>();
+    public final Map<Pair<UserHandle, String>, Context> userPackageContexts = new ArrayMap<>();
 
     public String packageName = null;
 
@@ -324,6 +332,9 @@
         public final IntentFilter filter;
         public final Handler scheduler;
 
+        // Exceptions thrown in a background thread kill the whole test. Save them instead.
+        public final AtomicReference<Exception> backgroundException = new AtomicReference<>();
+
         public BroadcastReceiverRegistration(BroadcastReceiver receiver, IntentFilter filter,
                 Handler scheduler) {
             this.receiver = receiver;
@@ -337,20 +348,30 @@
                     0 /* type */, false /* ordered */, false /* sticky */, null /* token */, userId,
                     0 /* flags */);
             if (filter.match(null, intent, false, "DpmMockContext") > 0) {
-                if (scheduler != null) {
-                    scheduler.post(() -> {
-                        receiver.setPendingResult(result);
-                        receiver.onReceive(DpmMockContext.this, intent);
-                    });
-                } else {
+                final Runnable send = () -> {
                     receiver.setPendingResult(result);
                     receiver.onReceive(DpmMockContext.this, intent);
+                };
+                if (scheduler != null) {
+                    scheduler.post(() -> {
+                        try {
+                            send.run();
+                        } catch (Exception e) {
+                            backgroundException.compareAndSet(null, e);
+                        }
+                    });
+                } else {
+                    send.run();
                 }
             }
         }
     }
     private List<BroadcastReceiverRegistration> mBroadcastReceivers = new ArrayList<>();
 
+    public DpmMockContext(Context realTestContext, String name) {
+        this(realTestContext, new File(realTestContext.getCacheDir(), name));
+    }
+
     public DpmMockContext(Context context, File dataDir) {
         realTestContext = context;
 
@@ -511,13 +532,29 @@
                 .thenReturn(isRunning);
     }
 
-    public void injectBroadcast(Intent intent) {
+    public void injectBroadcast(final Intent intent) {
         final int userId = UserHandle.getUserId(binder.getCallingUid());
         for (final BroadcastReceiverRegistration receiver : mBroadcastReceivers) {
             receiver.sendBroadcastIfApplicable(userId, intent);
         }
     }
 
+    public void rethrowBackgroundBroadcastExceptions() throws Exception {
+        for (final BroadcastReceiverRegistration receiver : mBroadcastReceivers) {
+            final Exception e = receiver.backgroundException.getAndSet(null);
+            if (e != null) {
+                throw e;
+            }
+        }
+    }
+
+    public void addPackageContext(UserHandle user, Context context) {
+        if (context.getPackageName() == null) {
+            throw new NullPointerException("getPackageName() == null");
+        }
+        userPackageContexts.put(new Pair<>(user, context.getPackageName()), context);
+    }
+
     @Override
     public Resources getResources() {
         return resources;
@@ -576,7 +613,16 @@
         if (binder.getCallingUid() == SYSTEM_UID) {
             return; // Assume system has all permissions.
         }
-        if (!callerPermissions.contains(permission)) {
+
+        List<String> permissions = binder.callingPermissions.get(binder.getCallingUid());
+        if (permissions == null) {
+            // TODO: delete the following line. to do this without breaking any tests, first it's
+            //       necessary to remove all tests that set it directly.
+            permissions = callerPermissions;
+//            throw new UnsupportedOperationException(
+//                    "Caller UID " + binder.getCallingUid() + " doesn't exist");
+        }
+        if (!permissions.contains(permission)) {
             throw new SecurityException("Caller doesn't have " + permission + " : " + message);
         }
     }
@@ -751,14 +797,11 @@
     @Override
     public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
             throws PackageManager.NameNotFoundException {
-        if (!userContexts.containsKey(user)) {
-            return super.createPackageContextAsUser(packageName, flags, user);
+        final Pair<UserHandle, String> key = new Pair<>(user, packageName);
+        if (userPackageContexts.containsKey(key)) {
+            return userPackageContexts.get(key);
         }
-        if (!getPackageName().equals(packageName)) {
-            throw new UnsupportedOperationException(
-                    "Creating a context as another package is not implemented");
-        }
-        return userContexts.get(user);
+        throw new UnsupportedOperationException("No package " + packageName + " for user " + user);
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 43e2610..5d68edd 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -16,6 +16,7 @@
 
 package com.android.server.devicepolicy;
 
+import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -70,6 +71,34 @@
         return mMockContext;
     }
 
+    protected interface DpmRunnable {
+        public void run(DevicePolicyManager dpm) throws Exception;
+    }
+
+    /**
+     * Simulate an RPC from {@param caller} to the service context ({@link #mContext}).
+     *
+     * The caller sees its own context. The server also sees its own separate context, with the
+     * appropriate calling UID and calling permissions fields already set up.
+     */
+    protected void runAsCaller(DpmMockContext caller, DevicePolicyManagerServiceTestable dpms,
+            DpmRunnable action) {
+        final DpmMockContext serviceContext = mMockContext;
+
+        final long origId = serviceContext.binder.clearCallingIdentity();
+        try {
+            serviceContext.binder.callingUid = caller.binder.callingUid;
+            serviceContext.binder.callingPid = caller.binder.callingPid;
+            serviceContext.binder.callingPermissions.put(caller.binder.callingUid,
+                    caller.permissions);
+            action.run(new DevicePolicyManagerTestable(caller, dpms));
+        } catch (Exception e) {
+            throw new AssertionError(e);
+        } finally {
+            serviceContext.binder.restoreCallingIdentity(origId);
+        }
+    }
+
     protected void markPackageAsInstalled(String packageName, ApplicationInfo ai, int userId)
             throws Exception {
         final PackageInfo pi = DpmTestUtils.cloneParcelable(
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/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
index 9f50a2c..921e0e3 100644
--- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -126,8 +126,6 @@
     }
 
     @Test
-    @Ignore
-    // TODO(b/35034729): Need to fix before re-enabling
     public void testLandscapeSeascapeRotationByPolicy() throws Exception {
         // Some plumbing to get the service ready for rotation updates.
         sWm.mDisplayReady = true;
@@ -145,15 +143,20 @@
         appWindowToken.addWindow(appWindow);
 
         // Set initial orientation and update.
-        ((TestWindowManagerPolicy) sWm.mPolicy).rotationToReport = Surface.ROTATION_90;
-        sWm.updateRotation(false, false);
+        performRotation(Surface.ROTATION_90);
         appWindow.resizeReported = false;
 
         // Update the rotation to perform 180 degree rotation and check that resize was reported.
-        ((TestWindowManagerPolicy) sWm.mPolicy).rotationToReport = Surface.ROTATION_270;
-        sWm.updateRotation(false, false);
-        sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
+        performRotation(Surface.ROTATION_270);
         assertTrue(appWindow.resizeReported);
         appWindow.removeImmediately();
     }
+
+    private void performRotation(int rotationToReport) {
+        ((TestWindowManagerPolicy) sWm.mPolicy).rotationToReport = rotationToReport;
+        sWm.updateRotation(false, false);
+        // Simulate animator finishing orientation change
+        sWm.mRoot.mOrientationChangeComplete = true;
+        sWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
index b6dc9a5..0a644b60 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestIWindow.java
@@ -18,11 +18,11 @@
 
 import com.android.internal.os.IResultReceiver;
 
-import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.util.MergedConfiguration;
 import android.view.DragEvent;
 import android.view.IWindow;
 
@@ -36,7 +36,7 @@
 
     @Override
     public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
-            Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig,
+            Rect stableInsets, Rect outsets, boolean reportDraw, MergedConfiguration mergedConfig,
             Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId)
             throws RemoteException {
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 911050a..65efd9c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -71,7 +71,10 @@
     static TestWindowManagerPolicy sPolicy = null;
     private final static IWindow sIWindow = new TestIWindow();
     private final static Session sMockSession = mock(Session.class);
-    private static int sNextDisplayId = Display.DEFAULT_DISPLAY + 1;
+    // The default display is removed in {@link #setUp} and then we iterate over all displays to
+    // make sure we don't collide with any existing display. If we run into no other display, the
+    // added display should be treated as default.
+    private static int sNextDisplayId = Display.DEFAULT_DISPLAY;
     static int sNextStackId = FIRST_DYNAMIC_STACK_ID;
     private static int sNextTaskId = 0;
 
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/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 40bdaa5..03d8241 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -25,8 +25,8 @@
 import android.media.AudioSystem;
 import android.media.IAudioService;
 import android.media.midi.MidiDeviceInfo;
-import android.os.FileObserver;
 import android.os.Bundle;
+import android.os.FileObserver;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -41,10 +41,7 @@
 import libcore.io.IoUtils;
 
 import java.io.File;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
 import java.util.HashMap;
-import java.util.ArrayList;
 
 /**
  * UsbAlsaManager manages USB audio and MIDI devices.
@@ -219,23 +216,23 @@
         AlsaDevice testDevice = new AlsaDevice(type, card, device);
 
         // This value was empirically determined.
-        final int kWaitTime = 2500; // ms
+        final int kWaitTimeMs = 2500;
 
         synchronized(mAlsaDevices) {
-            long timeout = SystemClock.elapsedRealtime() + kWaitTime;
+            long timeoutMs = SystemClock.elapsedRealtime() + kWaitTimeMs;
             do {
                 if (mAlsaDevices.values().contains(testDevice)) {
                     return testDevice;
                 }
-                long waitTime = timeout - SystemClock.elapsedRealtime();
-                if (waitTime > 0) {
+                long waitTimeMs = timeoutMs - SystemClock.elapsedRealtime();
+                if (waitTimeMs > 0) {
                     try {
-                        mAlsaDevices.wait(waitTime);
+                        mAlsaDevices.wait(waitTimeMs);
                     } catch (InterruptedException e) {
                         Slog.d(TAG, "usb: InterruptedException while waiting for ALSA file.");
                     }
                 }
-            } while (timeout > SystemClock.elapsedRealtime());
+            } while (timeoutMs > SystemClock.elapsedRealtime());
         }
 
         Slog.e(TAG, "waitForAlsaDevice failed for " + testDevice);
@@ -498,6 +495,7 @@
     // Devices List
     //
 /*
+    //import java.util.ArrayList;
     public ArrayList<UsbAudioDevice> getConnectedDevices() {
         ArrayList<UsbAudioDevice> devices = new ArrayList<UsbAudioDevice>(mAudioDevices.size());
         for (HashMap.Entry<UsbDevice,UsbAudioDevice> entry : mAudioDevices.entrySet()) {
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 4178f4d..4da2853 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -169,6 +169,7 @@
         public AdbSettingsObserver() {
             super(null);
         }
+
         @Override
         public void onChange(boolean selfChange) {
             boolean enable = (Settings.Global.getInt(mContentResolver,
@@ -208,9 +209,9 @@
     private final BroadcastReceiver mChargingReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-             int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
-             boolean usbCharging = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
-             mHandler.sendMessage(MSG_UPDATE_CHARGING_STATE, usbCharging);
+            int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
+            boolean usbCharging = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
+            mHandler.sendMessage(MSG_UPDATE_CHARGING_STATE, usbCharging);
         }
     };
 
@@ -261,9 +262,10 @@
             // TV-specific notification channel
             mNotificationManager.createNotificationChannel(
                     new NotificationChannel(ADB_NOTIFICATION_CHANNEL_ID_TV,
-                        mContext.getString(
-                            com.android.internal.R.string.adb_debugging_notification_channel_tv),
-                        NotificationManager.IMPORTANCE_HIGH));
+                            mContext.getString(
+                                    com.android.internal.R.string
+                                            .adb_debugging_notification_channel_tv),
+                            NotificationManager.IMPORTANCE_HIGH));
         }
 
         // We do not show the USB notification if the primary volume supports mass storage.
@@ -309,8 +311,8 @@
         boolean enableAudio = (nativeGetAudioMode() == AUDIO_MODE_SOURCE);
         // don't start accessory mode if our mandatory strings have not been set
         boolean enableAccessory = (mAccessoryStrings != null &&
-                        mAccessoryStrings[UsbAccessory.MANUFACTURER_STRING] != null &&
-                        mAccessoryStrings[UsbAccessory.MODEL_STRING] != null);
+                mAccessoryStrings[UsbAccessory.MANUFACTURER_STRING] != null &&
+                mAccessoryStrings[UsbAccessory.MODEL_STRING] != null);
         String functions = null;
 
         if (enableAccessory && enableAudio) {
@@ -341,14 +343,14 @@
         int serialLength = serial.length();
         // XOR the USB serial across the remaining 5 bytes
         for (int i = 0; i < serialLength; i++) {
-            address[i % (ETH_ALEN - 1) + 1] ^= (int)serial.charAt(i);
+            address[i % (ETH_ALEN - 1) + 1] ^= (int) serial.charAt(i);
         }
         String addrString = String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
-            address[0], address[1], address[2], address[3], address[4], address[5]);
+                address[0], address[1], address[2], address[3], address[4], address[5]);
         try {
             FileUtils.stringToFile(RNDIS_ETH_ADDR_PATH, addrString);
         } catch (IOException e) {
-           Slog.e(TAG, "failed to write to " + RNDIS_ETH_ADDR_PATH);
+            Slog.e(TAG, "failed to write to " + RNDIS_ETH_ADDR_PATH);
         }
     }
 
@@ -400,7 +402,7 @@
                 // register observer to listen for settings changes
                 mContentResolver.registerContentObserver(
                         Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
-                                false, new AdbSettingsObserver());
+                        false, new AdbSettingsObserver());
 
                 // Watch for USB configuration changes
                 mUEventObserver.startObserving(USB_STATE_MATCH);
@@ -466,9 +468,9 @@
             }
 
             SomeArgs args = SomeArgs.obtain();
-            args.argi1 = hostConnected ? 1 :0;
-            args.argi2 = sourcePower ? 1 :0;
-            args.argi3 = sinkPower ? 1 :0;
+            args.argi1 = hostConnected ? 1 : 0;
+            args.argi2 = sourcePower ? 1 : 0;
+            args.argi3 = sinkPower ? 1 : 0;
 
             removeMessages(MSG_UPDATE_HOST_STATE);
             Message msg = obtainMessage(MSG_UPDATE_HOST_STATE, args);
@@ -506,7 +508,7 @@
 
                 // Persist the adb setting
                 String newFunction = applyAdbFunction(SystemProperties.get(
-                            USB_PERSISTENT_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_NONE));
+                        USB_PERSISTENT_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_NONE));
                 SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY, newFunction);
 
                 // Remove mtp from the config if file transfer is not enabled
@@ -529,8 +531,10 @@
          */
         private void setEnabledFunctions(String functions, boolean forceRestart,
                 boolean usbDataUnlocked) {
-            if (DEBUG) Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", "
-                    + "forceRestart=" + forceRestart);
+            if (DEBUG) {
+                Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", "
+                        + "forceRestart=" + forceRestart);
+            }
 
             if (usbDataUnlocked != mUsbDataUnlocked) {
                 mUsbDataUnlocked = usbDataUnlocked;
@@ -680,7 +684,7 @@
                 }
                 for (String key : keySet) {
                     if (intent.getBooleanExtra(key, false) !=
-                        mBroadcastedIntent.getBooleanExtra(key, false)) {
+                            mBroadcastedIntent.getBooleanExtra(key, false)) {
                         return true;
                     }
                 }
@@ -697,7 +701,8 @@
             intent.putExtra(UsbManager.USB_CONNECTED, mConnected);
             intent.putExtra(UsbManager.USB_HOST_CONNECTED, mHostConnected);
             intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured);
-            intent.putExtra(UsbManager.USB_DATA_UNLOCKED, isUsbTransferAllowed() && mUsbDataUnlocked);
+            intent.putExtra(UsbManager.USB_DATA_UNLOCKED,
+                    isUsbTransferAllowed() && mUsbDataUnlocked);
             intent.putExtra(UsbManager.USB_CONFIG_CHANGED, configChanged);
 
             if (mCurrentFunctions != null) {
@@ -825,7 +830,7 @@
                     setAdbEnabled(msg.arg1 == 1);
                     break;
                 case MSG_SET_CURRENT_FUNCTIONS:
-                    String functions = (String)msg.obj;
+                    String functions = (String) msg.obj;
                     setEnabledFunctions(functions, false, msg.arg1 == 1);
                     break;
                 case MSG_UPDATE_USER_RESTRICTIONS:
@@ -854,9 +859,9 @@
                     if (mCurrentUser != msg.arg1) {
                         // Restart the USB stack and re-apply user restrictions for MTP or PTP.
                         final boolean active = UsbManager.containsFunction(mCurrentFunctions,
-                                        UsbManager.USB_FUNCTION_MTP)
+                                UsbManager.USB_FUNCTION_MTP)
                                 || UsbManager.containsFunction(mCurrentFunctions,
-                                        UsbManager.USB_FUNCTION_PTP);
+                                UsbManager.USB_FUNCTION_PTP);
                         if (mUsbDataUnlocked && active && mCurrentUser != UserHandle.USER_NULL) {
                             Slog.v(TAG, "Current user switched to " + mCurrentUser
                                     + "; resetting USB host stack for MTP or PTP");
@@ -885,7 +890,9 @@
 
         private void updateUsbNotification() {
             if (mNotificationManager == null || !mUseUsbNotification
-                    || ("0".equals(SystemProperties.get("persist.charging.notify")))) return;
+                    || ("0".equals(SystemProperties.get("persist.charging.notify")))) {
+                return;
+            }
             int id = 0;
             Resources r = mContext.getResources();
             if (mConnected) {
@@ -937,18 +944,19 @@
 
                     Notification notification =
                             new Notification.Builder(mContext, SystemNotificationChannels.USB)
-                            .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
-                            .setWhen(0)
-                            .setOngoing(true)
-                            .setTicker(title)
-                            .setDefaults(0)  // please be quiet
-                            .setColor(mContext.getColor(
-                                    com.android.internal.R.color.system_notification_accent_color))
-                            .setContentTitle(title)
-                            .setContentText(message)
-                            .setContentIntent(pi)
-                            .setVisibility(Notification.VISIBILITY_PUBLIC)
-                            .build();
+                                    .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+                                    .setWhen(0)
+                                    .setOngoing(true)
+                                    .setTicker(title)
+                                    .setDefaults(0)  // please be quiet
+                                    .setColor(mContext.getColor(
+                                            com.android.internal.R.color
+                                                    .system_notification_accent_color))
+                                    .setContentTitle(title)
+                                    .setContentText(message)
+                                    .setContentIntent(pi)
+                                    .setVisibility(Notification.VISIBILITY_PUBLIC)
+                                    .build();
                     mNotificationManager.notifyAsUser(null, id, notification,
                             UserHandle.ALL);
                     mUsbNotificationId = id;
@@ -976,20 +984,21 @@
 
                     Notification notification =
                             new Notification.Builder(mContext, SystemNotificationChannels.DEVELOPER)
-                            .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
-                            .setWhen(0)
-                            .setOngoing(true)
-                            .setTicker(title)
-                            .setDefaults(0)  // please be quiet
-                            .setColor(mContext.getColor(
-                                    com.android.internal.R.color.system_notification_accent_color))
-                            .setContentTitle(title)
-                            .setContentText(message)
-                            .setContentIntent(pi)
-                            .setVisibility(Notification.VISIBILITY_PUBLIC)
-                            .extend(new Notification.TvExtender()
-                                    .setChannel(ADB_NOTIFICATION_CHANNEL_ID_TV))
-                            .build();
+                                    .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+                                    .setWhen(0)
+                                    .setOngoing(true)
+                                    .setTicker(title)
+                                    .setDefaults(0)  // please be quiet
+                                    .setColor(mContext.getColor(
+                                            com.android.internal.R.color
+                                                    .system_notification_accent_color))
+                                    .setContentTitle(title)
+                                    .setContentText(message)
+                                    .setContentIntent(pi)
+                                    .setVisibility(Notification.VISIBILITY_PUBLIC)
+                                    .extend(new Notification.TvExtender()
+                                            .setChannel(ADB_NOTIFICATION_CHANNEL_ID_TV))
+                                    .build();
                     mAdbNotificationShown = true;
                     mNotificationManager.notifyAsUser(null, id, notification,
                             UserHandle.ALL);
@@ -1038,7 +1047,8 @@
     }
 
     /* opens the currently attached USB accessory */
-    public ParcelFileDescriptor openAccessory(UsbAccessory accessory, UsbUserSettingsManager settings) {
+    public ParcelFileDescriptor openAccessory(UsbAccessory accessory,
+            UsbUserSettingsManager settings) {
         UsbAccessory currentAccessory = mHandler.getCurrentAccessory();
         if (currentAccessory == null) {
             throw new IllegalArgumentException("no accessory attached");
@@ -1058,17 +1068,19 @@
     }
 
     public void setCurrentFunctions(String functions, boolean usbDataUnlocked) {
-        if (DEBUG) Slog.d(TAG, "setCurrentFunctions(" + functions + ", " +
-				usbDataUnlocked + ")");
+        if (DEBUG) {
+            Slog.d(TAG, "setCurrentFunctions(" + functions + ", " +
+                    usbDataUnlocked + ")");
+        }
         mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions, usbDataUnlocked);
     }
 
     private void readOemUsbOverrideConfig() {
         String[] configList = mContext.getResources().getStringArray(
-            com.android.internal.R.array.config_oemUsbModeOverride);
+                com.android.internal.R.array.config_oemUsbModeOverride);
 
         if (configList != null) {
-            for (String config: configList) {
+            for (String config : configList) {
                 String[] items = config.split(":");
                 if (items.length == 3) {
                     if (mOemModeMap == null) {
@@ -1092,7 +1104,7 @@
 
         List<Pair<String, String>> overrides = mOemModeMap.get(bootMode);
         if (overrides != null) {
-            for (Pair<String, String> pair: overrides) {
+            for (Pair<String, String> pair : overrides) {
                 if (pair.first.equals(usbFunctions)) {
                     Slog.d(TAG, "OEM USB override: " + pair.first + " ==> " + pair.second);
                     return pair.second;
@@ -1120,7 +1132,7 @@
             mDebuggingManager.clearUsbDebuggingKeys();
         } else {
             throw new RuntimeException("Cannot clear Usb Debugging keys, "
-                        + "UsbDebuggingManager not enabled");
+                    + "UsbDebuggingManager not enabled");
         }
     }
 
@@ -1134,7 +1146,10 @@
     }
 
     private native String[] nativeGetAccessoryStrings();
+
     private native ParcelFileDescriptor nativeOpenAccessory();
+
     private native boolean nativeIsStartRequested();
+
     private native int nativeGetAudioMode();
 }
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/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
index 54ab19d..2516d51 100644
--- a/telephony/java/android/telephony/PreciseDisconnectCause.java
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -25,75 +25,480 @@
 public class PreciseDisconnectCause {
 
     /** The disconnect cause is not valid (Not received a disconnect cause)*/
-    public static final int NOT_VALID                      = -1;
+    public static final int NOT_VALID                                        = -1;
     /** No disconnect cause provided. Generally a local disconnect or an incoming missed call */
-    public static final int NO_DISCONNECT_CAUSE_AVAILABLE  = 0;
+    public static final int NO_DISCONNECT_CAUSE_AVAILABLE                    = 0;
     /**
      * The destination cannot be reached because the number, although valid,
      * is not currently assigned
      */
-    public static final int UNOBTAINABLE_NUMBER            = 1;
+    public static final int UNOBTAINABLE_NUMBER                              = 1;
+    /** The user cannot be reached because the network through which the call has been
+     *  routed does not serve the destination desired
+     */
+    public static final int NO_ROUTE_TO_DESTINATION                          = 3;
+    /** The channel most recently identified is not acceptable to the sending entity for
+     *  use in this call
+     */
+    public static final int CHANNEL_UNACCEPTABLE                             = 6;
+    /** The MS has tried to access a service that the MS's network operator or service
+     *  provider is not prepared to allow
+     */
+    public static final int OPERATOR_DETERMINED_BARRING                      = 8;
     /** One of the users involved in the call has requested that the call is cleared */
-    public static final int NORMAL                         = 16;
+    public static final int NORMAL                                           = 16;
     /** The called user is unable to accept another call */
-    public static final int BUSY                           = 17;
+    public static final int BUSY                                             = 17;
+    /** The user does not respond to a call establishment message with either an alerting
+     *  or connect indication within the prescribed period of time allocated
+     */
+    public static final int NO_USER_RESPONDING                               = 18;
+    /** The user has provided an alerting indication but has not provided a connect
+     *  indication within a prescribed period of time
+     */
+    public static final int NO_ANSWER_FROM_USER                              = 19;
+    /** The equipment sending this cause does not wish to accept this call */
+    public static final int CALL_REJECTED                                    = 21;
     /** The called number is no longer assigned */
-    public static final int NUMBER_CHANGED                 = 22;
+    public static final int NUMBER_CHANGED                                   = 22;
+    /** This cause is returned to the network when a mobile station clears an active
+     *  call which is being pre-empted by another call with higher precedence
+     */
+    public static final int PREEMPTION                                       = 25;
+    /** The destination indicated by the mobile station cannot be reached because
+     *  the interface to the destination is not functioning correctly
+     */
+    public static final int DESTINATION_OUT_OF_ORDER                         = 27;
+    /** The called party number is not a valid format or is not complete */
+    public static final int INVALID_NUMBER_FORMAT                            = 28;
+    /** The facility requested by user can not be provided by the network */
+    public static final int FACILITY_REJECTED                                = 29;
     /** Provided in response to a STATUS ENQUIRY message */
-    public static final int STATUS_ENQUIRY                 = 30;
+    public static final int STATUS_ENQUIRY                                   = 30;
     /** Reports a normal disconnect only when no other normal cause applies */
-    public static final int NORMAL_UNSPECIFIED             = 31;
+    public static final int NORMAL_UNSPECIFIED                               = 31;
     /** There is no channel presently available to handle the call */
-    public static final int NO_CIRCUIT_AVAIL               = 34;
+    public static final int NO_CIRCUIT_AVAIL                                 = 34;
+    /** The network is not functioning correctly and that the condition is likely
+     *  to last a relatively long period of time
+     */
+    public static final int NETWORK_OUT_OF_ORDER                             = 38;
     /**
      * The network is not functioning correctly and the condition is not likely to last
      * a long period of time
      */
-    public static final int TEMPORARY_FAILURE              = 41;
+    public static final int TEMPORARY_FAILURE                                = 41;
     /** The switching equipment is experiencing a period of high traffic */
-    public static final int SWITCHING_CONGESTION           = 42;
+    public static final int SWITCHING_CONGESTION                             = 42;
+    /** The network could not deliver access information to the remote user as requested */
+    public static final int ACCESS_INFORMATION_DISCARDED                     = 43;
     /** The channel cannot be provided */
-    public static final int CHANNEL_NOT_AVAIL              = 44;
+    public static final int CHANNEL_NOT_AVAIL                                = 44;
+    /** This cause is used to report a resource unavailable event only when no other
+     *  cause in the resource unavailable class applies
+     */
+    public static final int RESOURCES_UNAVAILABLE_OR_UNSPECIFIED             = 44;
     /** The requested quality of service (ITU-T X.213) cannot be provided */
-    public static final int QOS_NOT_AVAIL                  = 49;
+    public static final int QOS_NOT_AVAIL                                    = 49;
+    /** The facility could not be provided by the network because the user has no
+     *  complete subscription
+     */
+    public static final int REQUESTED_FACILITY_NOT_SUBSCRIBED                = 50;
+    /** Incoming calls are not allowed within this CUG */
+    public static final int INCOMING_CALLS_BARRED_WITHIN_CUG                 = 55;
+    /** The mobile station is not authorized to use bearer capability requested */
+    public static final int BEARER_CAPABILITY_NOT_AUTHORIZED                 = 57;
     /** The requested bearer capability is not available at this time */
-    public static final int BEARER_NOT_AVAIL               = 58;
+    public static final int BEARER_NOT_AVAIL                                 = 58;
+    /** The service option is not availble at this time */
+    public static final int SERVICE_OPTION_NOT_AVAILABLE                     = 63;
+    /** The equipment sending this cause does not support the bearer capability requested */
+    public static final int BEARER_SERVICE_NOT_IMPLEMENTED                   = 65;
     /** The call clearing is due to ACM being greater than or equal to ACMmax */
-    public static final int ACM_LIMIT_EXCEEDED             = 68;
+    public static final int ACM_LIMIT_EXCEEDED                               = 68;
+    /** The equipment sending this cause does not support the requested facility */
+    public static final int REQUESTED_FACILITY_NOT_IMPLEMENTED               = 69;
+    /** The equipment sending this cause only supports the restricted version of
+     *  the requested bearer capability
+     */
+    public static final int ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE        = 70;
+    /** The service requested is not implemented at network */
+    public static final int SERVICE_OR_OPTION_NOT_IMPLEMENTED                = 79;
+    /** The equipment sending this cause has received a message with a transaction identifier
+     *  which is not currently in use on the MS-network interface
+     */
+    public static final int INVALID_TRANSACTION_IDENTIFIER                   = 81;
+    /** The called user for the incoming CUG call is not a member of the specified CUG */
+    public static final int USER_NOT_MEMBER_OF_CUG                           = 87;
+    /** The equipment sending this cause has received a request which can't be accomodated */
+    public static final int INCOMPATIBLE_DESTINATION                         = 88;
+    /** This cause is used to report receipt of a message with semantically incorrect contents */
+    public static final int SEMANTICALLY_INCORRECT_MESSAGE                   = 95;
+    /** The equipment sending this cause has received a message with a non-semantical
+     *  mandatory IE error
+     */
+    public static final int INVALID_MANDATORY_INFORMATION                    = 96;
+    /** This is sent in response to a message which is not defined, or defined but not
+     *  implemented by the equipment sending this cause
+     */
+    public static final int MESSAGE_TYPE_NON_IMPLEMENTED                     = 97;
+    /** The equipment sending this cause has received a message not compatible with the
+     *  protocol state
+     */
+    public static final int MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE  = 98;
+    /** The equipment sending this cause has received a message which includes information
+     *  elements not recognized because its identifier is not defined or it is defined but not
+     *  implemented by the equipment sending the cause
+     */
+    public static final int INFORMATION_ELEMENT_NON_EXISTENT                 = 99;
+    /** The equipment sending this cause has received a message with conditional IE errors */
+    public static final int CONDITIONAL_IE_ERROR                             = 100;
+    /** The message has been received which is incompatible with the protocol state */
+    public static final int MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE       = 101;
+    /** The  procedure has been initiated by the expiry of a timer in association with
+     *  3GPP TS 24.008 error handling procedures
+     */
+    public static final int RECOVERY_ON_TIMER_EXPIRED                        = 102;
+    /** This protocol error event is reported only when no other cause in the protocol
+     *  error class applies
+     */
+    public static final int PROTOCOL_ERROR_UNSPECIFIED                       = 111;
+    /** interworking with a network which does not provide causes for actions it takes
+     *  thus, the precise cause for a message which is being sent cannot be ascertained
+     */
+    public static final int INTERWORKING_UNSPECIFIED                         = 127;
     /** The call is restricted */
-    public static final int CALL_BARRED                    = 240;
+    public static final int CALL_BARRED                                      = 240;
     /** The call is blocked by the Fixed Dialing Number list */
-    public static final int FDN_BLOCKED                    = 241;
+    public static final int FDN_BLOCKED                                      = 241;
     /** The given IMSI is not known at the VLR */
     /** TS 24.008 cause 4 */
-    public static final int IMSI_UNKNOWN_IN_VLR            = 242;
+    public static final int IMSI_UNKNOWN_IN_VLR                              = 242;
     /**
      * The network does not accept emergency call establishment using an IMEI or not accept attach
      * procedure for emergency services using an IMEI
      */
-    public static final int IMEI_NOT_ACCEPTED              = 243;
+    public static final int IMEI_NOT_ACCEPTED                                = 243;
+    /** The call cannot be established because RADIO is OFF */
+    public static final int RADIO_OFF                                        = 247;
+    /** The call cannot be established because of no cell coverage */
+    public static final int OUT_OF_SRV                                       = 248;
+    /** The call cannot be established because of no valid SIM */
+    public static final int NO_VALID_SIM                                     = 249;
+    /** The call is dropped or failed internally by modem */
+    public static final int RADIO_INTERNAL_ERROR                             = 250;
+    /** Call failed because of UE timer expired while waiting for a response from network */
+    public static final int NETWORK_RESP_TIMEOUT                             = 251;
+    /** Call failed because of a network reject */
+    public static final int NETWORK_REJECT                                   = 252;
+    /** Call failed because of radio access failure. ex. RACH failure */
+    public static final int RADIO_ACCESS_FAILURE                             = 253;
+    /** Call failed/dropped because of a RLF */
+    public static final int RADIO_LINK_FAILURE                               = 254;
+    /** Call failed/dropped because of radio link lost */
+    public static final int RADIO_LINK_LOST                                  = 255;
+    /** Call failed because of a radio uplink issue */
+    public static final int RADIO_UPLINK_FAILURE                             = 256;
+    /** Call failed because of a RRC connection setup failure */
+    public static final int RADIO_SETUP_FAILURE                              = 257;
+    /** Call failed/dropped because of RRC connection release from NW */
+    public static final int RADIO_RELEASE_NORMAL                             = 258;
+    /** Call failed/dropped because of RRC abnormally released by modem/network */
+    public static final int RADIO_RELEASE_ABNORMAL                           = 259;
+    /** Call setup failed because of access class barring */
+    public static final int ACCESS_CLASS_BLOCKED                             = 260;
+    /** Call failed/dropped because of a network detach */
+    public static final int NETWORK_DETACH                                   = 261;
+
     /** MS is locked until next power cycle */
-    public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE  = 1000;
+    public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE                    = 1000;
     /** Drop call*/
-    public static final int CDMA_DROP                      = 1001;
+    public static final int CDMA_DROP                                        = 1001;
     /** INTERCEPT order received, MS state idle entered */
-    public static final int CDMA_INTERCEPT                 = 1002;
+    public static final int CDMA_INTERCEPT                                   = 1002;
     /** MS has been redirected, call is cancelled */
-    public static final int CDMA_REORDER                   = 1003;
+    public static final int CDMA_REORDER                                     = 1003;
     /** Service option rejection */
-    public static final int CDMA_SO_REJECT                 = 1004;
+    public static final int CDMA_SO_REJECT                                   = 1004;
     /** Requested service is rejected, retry delay is set */
-    public static final int CDMA_RETRY_ORDER               = 1005;
+    public static final int CDMA_RETRY_ORDER                                 = 1005;
     /** Unable to obtain access to the CDMA system */
-    public static final int CDMA_ACCESS_FAILURE            = 1006;
+    public static final int CDMA_ACCESS_FAILURE                              = 1006;
     /** Not a preempted call */
-    public static final int CDMA_PREEMPTED                 = 1007;
+    public static final int CDMA_PREEMPTED                                   = 1007;
     /** Not an emergency call */
-    public static final int CDMA_NOT_EMERGENCY             = 1008;
+    public static final int CDMA_NOT_EMERGENCY                               = 1008;
     /** Access Blocked by CDMA network */
-    public static final int CDMA_ACCESS_BLOCKED            = 1009;
+    public static final int CDMA_ACCESS_BLOCKED                              = 1009;
+
+    /** Mapped from ImsReasonInfo */
+    /* The passed argument is an invalid */
+    public static final int LOCAL_ILLEGAL_ARGUMENT                           = 1200;
+    // The operation is invoked in invalid call state
+    public static final int LOCAL_ILLEGAL_STATE                              = 1201;
+    // IMS service internal error
+    public static final int LOCAL_INTERNAL_ERROR                             = 1202;
+    // IMS service goes down (service connection is lost)
+    public static final int LOCAL_IMS_SERVICE_DOWN                           = 1203;
+    // No pending incoming call exists
+    public static final int LOCAL_NO_PENDING_CALL                            = 1204;
+    // Service unavailable; by power off
+    public static final int LOCAL_POWER_OFF                                  = 1205;
+    // Service unavailable; by low battery
+    public static final int LOCAL_LOW_BATTERY                                = 1206;
+    // Service unavailable; by out of service (data service state)
+    public static final int LOCAL_NETWORK_NO_SERVICE                         = 1207;
+    /* Service unavailable; by no LTE coverage
+     * (VoLTE is not supported even though IMS is registered)
+     */
+    public static final int LOCAL_NETWORK_NO_LTE_COVERAGE                    = 1208;
+    /** Service unavailable; by located in roaming area */
+    public static final int LOCAL_NETWORK_ROAMING                            = 1209;
+    /** Service unavailable; by IP changed */
+    public static final int LOCAL_NETWORK_IP_CHANGED                         = 1210;
+    /** Service unavailable; other */
+    public static final int LOCAL_SERVICE_UNAVAILABLE                        = 1211;
+    /* Service unavailable; IMS connection is lost (IMS is not registered) */
+    public static final int LOCAL_NOT_REGISTERED                             = 1212;
+    /** Max call exceeded */
+    public static final int LOCAL_MAX_CALL_EXCEEDED                          = 1213;
+    /** Call decline */
+    public static final int LOCAL_CALL_DECLINE                               = 1214;
+    /** SRVCC is in progress */
+    public static final int LOCAL_CALL_VCC_ON_PROGRESSING                    = 1215;
+    /** Resource reservation is failed (QoS precondition) */
+    public static final int LOCAL_CALL_RESOURCE_RESERVATION_FAILED           = 1216;
+    /** Retry CS call; VoLTE service can't be provided by the network or remote end
+     *  Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set
+     */
+    public static final int LOCAL_CALL_CS_RETRY_REQUIRED                     = 1217;
+    /** Retry VoLTE call; VoLTE service can't be provided by the network temporarily */
+    public static final int LOCAL_CALL_VOLTE_RETRY_REQUIRED                  = 1218;
+    /** IMS call is already terminated (in TERMINATED state) */
+    public static final int LOCAL_CALL_TERMINATED                            = 1219;
+    /** Handover not feasible */
+    public static final int LOCAL_HO_NOT_FEASIBLE                            = 1220;
+
+    /** 1xx waiting timer is expired after sending INVITE request (MO only) */
+    public static final int TIMEOUT_1XX_WAITING                              = 1221;
+    /** User no answer during call setup operation (MO/MT)
+     *  MO : 200 OK to INVITE request is not received,
+     *  MT : No action from user after alerting the call
+     */
+    public static final int TIMEOUT_NO_ANSWER                                = 1222;
+    /** User no answer during call update operation (MO/MT)
+     *  MO : 200 OK to re-INVITE request is not received,
+     *  MT : No action from user after alerting the call
+     */
+    public static final int TIMEOUT_NO_ANSWER_CALL_UPDATE                    = 1223;
+
+    /**
+     * STATUSCODE (SIP response code) (IMS -> Telephony)
+     */
+    /** SIP request is redirected */
+    public static final int SIP_REDIRECTED                                   = 1300;
+    /** 4xx responses */
+    /** 400 : Bad Request */
+    public static final int SIP_BAD_REQUEST                                  = 1310;
+    /** 403 : Forbidden */
+    public static final int SIP_FORBIDDEN                                    = 1311;
+    /** 404 : Not Found */
+    public static final int SIP_NOT_FOUND                                    = 1312;
+    /** 415 : Unsupported Media Type
+     *  416 : Unsupported URI Scheme
+     *  420 : Bad Extension
+     */
+    public static final int SIP_NOT_SUPPORTED                                = 1313;
+    /** 408 : Request Timeout */
+    public static final int SIP_REQUEST_TIMEOUT                              = 1314;
+    /** 480 : Temporarily Unavailable */
+    public static final int SIP_TEMPRARILY_UNAVAILABLE                       = 1315;
+    /** 484 : Address Incomplete */
+    public static final int SIP_BAD_ADDRESS                                  = 1316;
+    /** 486 : Busy Here
+     *  600 : Busy Everywhere
+     */
+    public static final int SIP_BUSY                                         = 1317;
+    /** 487 : Request Terminated */
+    public static final int SIP_REQUEST_CANCELLED                            = 1318;
+    /** 406 : Not Acceptable
+     *  488 : Not Acceptable Here
+     *  606 : Not Acceptable
+     */
+    public static final int SIP_NOT_ACCEPTABLE                               = 1319;
+    /** 410 : Gone
+     *  604 : Does Not Exist Anywhere
+     */
+    public static final int SIP_NOT_REACHABLE                                = 1320;
+    /** Others */
+    public static final int SIP_CLIENT_ERROR                                 = 1321;
+    /** 5xx responses
+     *  501 : Server Internal Error
+     */
+    public static final int SIP_SERVER_INTERNAL_ERROR                        = 1330;
+    /** 503 : Service Unavailable */
+    public static final int SIP_SERVICE_UNAVAILABLE                          = 1331;
+    /** 504 : Server Time-out */
+    public static final int SIP_SERVER_TIMEOUT                               = 1332;
+    /** Others */
+    public static final int SIP_SERVER_ERROR                                 = 1333;
+    /** 6xx responses
+     *  603 : Decline
+     */
+    public static final int SIP_USER_REJECTED                                = 1340;
+    /** Others */
+    public static final int SIP_GLOBAL_ERROR                                 = 1341;
+    /** Emergency failure */
+    public static final int EMERGENCY_TEMP_FAILURE                           = 1342;
+    public static final int EMERGENCY_PERM_FAILURE                           = 1343;
+    /** Media resource initialization failed */
+    public static final int MEDIA_INIT_FAILED                                = 1400;
+    /** RTP timeout (no audio / video traffic in the session) */
+    public static final int MEDIA_NO_DATA                                    = 1401;
+    /** Media is not supported; so dropped the call */
+    public static final int MEDIA_NOT_ACCEPTABLE                             = 1402;
+    /** Unknown media related errors */
+    public static final int MEDIA_UNSPECIFIED                                = 1403;
+    /** User triggers the call end */
+    public static final int USER_TERMINATED                                  = 1500;
+    /** No action while an incoming call is ringing */
+    public static final int USER_NOANSWER                                    = 1501;
+    /** User ignores an incoming call */
+    public static final int USER_IGNORE                                      = 1502;
+    /** User declines an incoming call */
+    public static final int USER_DECLINE                                     = 1503;
+    /** Device declines/ends a call due to low battery */
+    public static final int LOW_BATTERY                                      = 1504;
+    /** Device declines call due to blacklisted call ID */
+    public static final int BLACKLISTED_CALL_ID                              = 1505;
+    /** The call is terminated by the network or remote user */
+    public static final int USER_TERMINATED_BY_REMOTE                        = 1510;
+
+    /**
+     * UT
+     */
+    public static final int UT_NOT_SUPPORTED                                 = 1800;
+    public static final int UT_SERVICE_UNAVAILABLE                           = 1801;
+    public static final int UT_OPERATION_NOT_ALLOWED                         = 1802;
+    public static final int UT_NETWORK_ERROR                                 = 1803;
+    public static final int UT_CB_PASSWORD_MISMATCH                          = 1804;
+
+    /**
+     * ECBM
+     */
+    public static final int ECBM_NOT_SUPPORTED                               = 1900;
+
+    /**
+     * Fail code used to indicate that Multi-endpoint is not supported by the Ims framework.
+     */
+    public static final int MULTIENDPOINT_NOT_SUPPORTED                      = 1901;
+
+    /**
+     * CALL DROP error codes (Call could drop because of many reasons like Network not available,
+     *  handover, failed, etc)
+     */
+
+    /**
+     * CALL DROP error code for the case when a device is ePDG capable and when the user is on an
+     * active wifi call and at the edge of coverage and there is no qualified LTE network available
+     * to handover the call to. We get a handover NOT_TRIGERRED message from the modem. This error
+     * code is received as part of the handover message.
+     */
+    public static final int CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE               = 2000;
+
+    /**
+     * MT call has ended due to a release from the network
+     * because the call was answered elsewhere
+     */
+    public static final int ANSWERED_ELSEWHERE                               = 2100;
+
+    /**
+     * For MultiEndpoint - Call Pull request has failed
+     */
+    public static final int CALL_PULL_OUT_OF_SYNC                            = 2101;
+
+    /**
+     * For MultiEndpoint - Call has been pulled from primary to secondary
+     */
+    public static final int CALL_PULLED                                      = 2102;
+
+    /**
+     * Supplementary services (HOLD/RESUME) failure error codes.
+     * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
+     */
+    public static final int SUPP_SVC_FAILED                                  = 2300;
+    public static final int SUPP_SVC_CANCELLED                               = 2301;
+    public static final int SUPP_SVC_REINVITE_COLLISION                      = 2302;
+
+    /**
+     * DPD Procedure received no response or send failed
+     */
+    public static final int IWLAN_DPD_FAILURE                                = 2400;
+
+    /**
+     * Establishment of the ePDG Tunnel Failed
+     */
+    public static final int EPDG_TUNNEL_ESTABLISH_FAILURE                    = 2500;
+
+    /**
+     * Re-keying of the ePDG Tunnel Failed; may not always result in teardown
+     */
+    public static final int EPDG_TUNNEL_REKEY_FAILURE                        = 2501;
+
+    /**
+     * Connection to the packet gateway is lost
+     */
+    public static final int EPDG_TUNNEL_LOST_CONNECTION                      = 2502;
+
+    /**
+     * The maximum number of calls allowed has been reached.  Used in a multi-endpoint scenario
+     * where the number of calls across all connected devices has reached the maximum.
+     */
+    public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED                  = 2503;
+
+    /**
+     * Similar to {@link #CODE_LOCAL_CALL_DECLINE}, except indicates that a remote device has
+     * declined the call.  Used in a multi-endpoint scenario where a remote device declined an
+     * incoming call.
+     */
+    public static final int REMOTE_CALL_DECLINE                              = 2504;
+
+    /**
+     * Indicates the call was disconnected due to the user reaching their data limit.
+     */
+    public static final int DATA_LIMIT_REACHED                               = 2505;
+
+    /**
+     * Indicates the call was disconnected due to the user disabling cellular data.
+     */
+    public static final int DATA_DISABLED                                    = 2506;
+
+    /**
+     * Indicates a call was disconnected due to loss of wifi signal.
+     */
+    public static final int WIFI_LOST                                        = 2507;
+
+
+    /* OEM specific error codes. To be used by OEMs when they don't want to
+       reveal error code which would be replaced by ERROR_UNSPECIFIED */
+    public static final int OEM_CAUSE_1                                      = 0xf001;
+    public static final int OEM_CAUSE_2                                      = 0xf002;
+    public static final int OEM_CAUSE_3                                      = 0xf003;
+    public static final int OEM_CAUSE_4                                      = 0xf004;
+    public static final int OEM_CAUSE_5                                      = 0xf005;
+    public static final int OEM_CAUSE_6                                      = 0xf006;
+    public static final int OEM_CAUSE_7                                      = 0xf007;
+    public static final int OEM_CAUSE_8                                      = 0xf008;
+    public static final int OEM_CAUSE_9                                      = 0xf009;
+    public static final int OEM_CAUSE_10                                     = 0xf00a;
+    public static final int OEM_CAUSE_11                                     = 0xf00b;
+    public static final int OEM_CAUSE_12                                     = 0xf00c;
+    public static final int OEM_CAUSE_13                                     = 0xf00d;
+    public static final int OEM_CAUSE_14                                     = 0xf00e;
+    public static final int OEM_CAUSE_15                                     = 0xf00f;
+
     /** Disconnected due to unspecified reasons */
-    public static final int ERROR_UNSPECIFIED              = 0xffff;
+    public static final int ERROR_UNSPECIFIED                                = 0xffff;
 
     /** Private constructor to avoid class instantiation. */
     private PreciseDisconnectCause() {
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/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java
index e4f380f..bd8492b3 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/com/android/ims/ImsReasonInfo.java
@@ -318,6 +318,66 @@
      */
     public static final int CODE_IKEV2_AUTH_FAILURE = 1408;
 
+    /** The call cannot be established because RADIO is OFF */
+    public static final int CODE_RADIO_OFF = 1500;
+
+    /** The call cannot be established because of no valid SIM */
+    public static final int CODE_NO_VALID_SIM = 1501;
+
+    /** The failure is due internal error at modem */
+    public static final int CODE_RADIO_INTERNAL_ERROR = 1502;
+
+    /** The failure is due to UE timer expired while waiting for a response from network */
+    public static final int CODE_NETWORK_RESP_TIMEOUT = 1503;
+
+    /** The failure is due to explicit reject from network */
+    public static final int CODE_NETWORK_REJECT = 1504;
+
+    /** The failure is due to radio access failure. ex. RACH failure */
+    public static final int CODE_RADIO_ACCESS_FAILURE = 1505;
+
+    /** Call/IMS registration failed/dropped because of a RLF */
+    public static final int CODE_RADIO_LINK_FAILURE = 1506;
+
+    /** Call/IMS registration failed/dropped because of radio link lost */
+    public static final int CODE_RADIO_LINK_LOST = 1507;
+
+    /** The call Call/IMS registration failed because of a radio uplink issue */
+    public static final int CODE_RADIO_UPLINK_FAILURE = 1508;
+
+    /** Call failed because of a RRC connection setup failure */
+    public static final int CODE_RADIO_SETUP_FAILURE = 1509;
+
+    /** Call failed/dropped because of RRC connection release from NW */
+    public static final int CODE_RADIO_RELEASE_NORMAL = 1510;
+
+    /** Call failed/dropped because of RRC abnormally released by modem/network */
+    public static final int CODE_RADIO_RELEASE_ABNORMAL = 1511;
+
+    /** Call failed because of access class barring */
+    public static final int CODE_ACCESS_CLASS_BLOCKED = 1512;
+
+    /** Call/IMS registration is failed/dropped because of a network detach */
+    public static final int CODE_NETWORK_DETACH = 1513;
+
+    /* OEM specific error codes. To be used by OEMs when they don't want to
+   reveal error code which would be replaced by ERROR_UNSPECIFIED */
+    public static final int CODE_OEM_CAUSE_1 = 0xf001;
+    public static final int CODE_OEM_CAUSE_2 = 0xf002;
+    public static final int CODE_OEM_CAUSE_3 = 0xf003;
+    public static final int CODE_OEM_CAUSE_4 = 0xf004;
+    public static final int CODE_OEM_CAUSE_5 = 0xf005;
+    public static final int CODE_OEM_CAUSE_6 = 0xf006;
+    public static final int CODE_OEM_CAUSE_7 = 0xf007;
+    public static final int CODE_OEM_CAUSE_8 = 0xf008;
+    public static final int CODE_OEM_CAUSE_9 = 0xf009;
+    public static final int CODE_OEM_CAUSE_10 = 0xf00a;
+    public static final int CODE_OEM_CAUSE_11 = 0xf00b;
+    public static final int CODE_OEM_CAUSE_12 = 0xf00c;
+    public static final int CODE_OEM_CAUSE_13 = 0xf00d;
+    public static final int CODE_OEM_CAUSE_14 = 0xf00e;
+    public static final int CODE_OEM_CAUSE_15 = 0xf00f;
+
     /**
      * Network string error messages.
      * mExtraMessage may have these values.
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/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
index 808b5d3..a7bdabd 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
@@ -138,7 +138,7 @@
             mSaturation = saturation;
             final ColorMatrixColorFilter filter =
                     (ColorMatrixColorFilter) mColorMatrixPaint.getColorFilter();
-            final ColorMatrix m = filter.getColorMatrix();
+            final ColorMatrix m = new ColorMatrix();
             m.setSaturation(saturation);
             filter.setColorMatrix(m);
             invalidate();
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/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..759bc35
--- /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-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..a123d80
--- /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-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/AppInfo.h b/tools/aapt2/AppInfo.h
index 1e488f7..9db21aa 100644
--- a/tools/aapt2/AppInfo.h
+++ b/tools/aapt2/AppInfo.h
@@ -23,30 +23,22 @@
 
 namespace aapt {
 
-/**
- * Holds basic information about the app being built. Most of this information
- * will come from the app's AndroidManifest.
- */
+// Information relevant to building an app, parsed from the app's AndroidManifest.xml.
 struct AppInfo {
-  /**
-   * App's package name.
-   */
+  // The app's package name.
   std::string package;
 
-  /**
-   * The App's minimum SDK version.
-   */
+  // The app's minimum SDK version, if it is defined.
   Maybe<std::string> min_sdk_version;
 
-  /**
-   * The Version code of the app.
-   */
+  // The app's version code, if it is defined.
   Maybe<uint32_t> version_code;
 
-  /**
-   * The revision code of the app.
-   */
+  // The app's revision code, if it is defined.
   Maybe<uint32_t> revision_code;
+
+  // The app's split name, if it is a split.
+  Maybe<std::string> split_name;
 };
 
 }  // namespace aapt
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..1b4d5bb 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;
 
@@ -750,70 +751,67 @@
     return true;
   }
 
-  Maybe<AppInfo> ExtractAppInfoFromManifest(xml::XmlResource* xml_res,
-                                            IDiagnostics* diag) {
+  Maybe<AppInfo> ExtractAppInfoFromManifest(xml::XmlResource* xml_res, IDiagnostics* diag) {
     // Make sure the first element is <manifest> with package attribute.
-    if (xml::Element* manifest_el = xml::FindRootElement(xml_res->root.get())) {
-      AppInfo app_info;
-
-      if (!manifest_el->namespace_uri.empty() ||
-          manifest_el->name != "manifest") {
-        diag->Error(DiagMessage(xml_res->file.source)
-                    << "root tag must be <manifest>");
-        return {};
-      }
-
-      xml::Attribute* package_attr = manifest_el->FindAttribute({}, "package");
-      if (!package_attr) {
-        diag->Error(DiagMessage(xml_res->file.source)
-                    << "<manifest> must have a 'package' attribute");
-        return {};
-      }
-
-      app_info.package = package_attr->value;
-
-      if (xml::Attribute* version_code_attr =
-              manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) {
-        Maybe<uint32_t> maybe_code =
-            ResourceUtils::ParseInt(version_code_attr->value);
-        if (!maybe_code) {
-          diag->Error(DiagMessage(xml_res->file.source.WithLine(
-                          manifest_el->line_number))
-                      << "invalid android:versionCode '"
-                      << version_code_attr->value << "'");
-          return {};
-        }
-        app_info.version_code = maybe_code.value();
-      }
-
-      if (xml::Attribute* revision_code_attr =
-              manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
-        Maybe<uint32_t> maybe_code =
-            ResourceUtils::ParseInt(revision_code_attr->value);
-        if (!maybe_code) {
-          diag->Error(DiagMessage(xml_res->file.source.WithLine(
-                          manifest_el->line_number))
-                      << "invalid android:revisionCode '"
-                      << revision_code_attr->value << "'");
-          return {};
-        }
-        app_info.revision_code = maybe_code.value();
-      }
-
-      if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) {
-        if (xml::Attribute* min_sdk = uses_sdk_el->FindAttribute(
-                xml::kSchemaAndroid, "minSdkVersion")) {
-          app_info.min_sdk_version = min_sdk->value;
-        }
-      }
-      return app_info;
+    xml::Element* manifest_el = xml::FindRootElement(xml_res->root.get());
+    if (manifest_el == nullptr) {
+      return {};
     }
-    return {};
+
+    AppInfo app_info;
+
+    if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") {
+      diag->Error(DiagMessage(xml_res->file.source) << "root tag must be <manifest>");
+      return {};
+    }
+
+    xml::Attribute* package_attr = manifest_el->FindAttribute({}, "package");
+    if (!package_attr) {
+      diag->Error(DiagMessage(xml_res->file.source)
+                  << "<manifest> must have a 'package' attribute");
+      return {};
+    }
+    app_info.package = package_attr->value;
+
+    if (xml::Attribute* version_code_attr =
+            manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) {
+      Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_attr->value);
+      if (!maybe_code) {
+        diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
+                    << "invalid android:versionCode '" << version_code_attr->value << "'");
+        return {};
+      }
+      app_info.version_code = maybe_code.value();
+    }
+
+    if (xml::Attribute* revision_code_attr =
+            manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
+      Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(revision_code_attr->value);
+      if (!maybe_code) {
+        diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
+                    << "invalid android:revisionCode '" << revision_code_attr->value << "'");
+        return {};
+      }
+      app_info.revision_code = maybe_code.value();
+    }
+
+    if (xml::Attribute* split_name_attr = manifest_el->FindAttribute({}, "split")) {
+      if (!split_name_attr->value.empty()) {
+        app_info.split_name = split_name_attr->value;
+      }
+    }
+
+    if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) {
+      if (xml::Attribute* min_sdk =
+              uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) {
+        app_info.min_sdk_version = min_sdk->value;
+      }
+    }
+    return app_info;
   }
 
   /**
-   * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it
-   * linked.
+   * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
    * Postcondition: ResourceTable has only one package left. All others are
    * stripped, or there is an error and false is returned.
    */
@@ -1366,45 +1364,44 @@
     return true;
   }
 
-  std::unique_ptr<xml::XmlResource> GenerateSplitManifest(
-      const AppInfo& app_info, const SplitConstraints& constraints) {
-    std::unique_ptr<xml::XmlResource> doc =
-        util::make_unique<xml::XmlResource>();
+  std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info,
+                                                          const SplitConstraints& constraints) {
+    std::unique_ptr<xml::XmlResource> doc = util::make_unique<xml::XmlResource>();
 
-    std::unique_ptr<xml::Namespace> namespace_android =
-        util::make_unique<xml::Namespace>();
+    std::unique_ptr<xml::Namespace> namespace_android = util::make_unique<xml::Namespace>();
     namespace_android->namespace_uri = xml::kSchemaAndroid;
     namespace_android->namespace_prefix = "android";
 
-    std::unique_ptr<xml::Element> manifest_el =
-        util::make_unique<xml::Element>();
+    std::unique_ptr<xml::Element> manifest_el = util::make_unique<xml::Element>();
     manifest_el->name = "manifest";
-    manifest_el->attributes.push_back(
-        xml::Attribute{"", "package", app_info.package});
+    manifest_el->attributes.push_back(xml::Attribute{"", "package", app_info.package});
 
     if (app_info.version_code) {
-      manifest_el->attributes.push_back(
-          xml::Attribute{xml::kSchemaAndroid, "versionCode",
-                         std::to_string(app_info.version_code.value())});
+      manifest_el->attributes.push_back(xml::Attribute{
+          xml::kSchemaAndroid, "versionCode", std::to_string(app_info.version_code.value())});
     }
 
     if (app_info.revision_code) {
-      manifest_el->attributes.push_back(
-          xml::Attribute{xml::kSchemaAndroid, "revisionCode",
-                         std::to_string(app_info.revision_code.value())});
+      manifest_el->attributes.push_back(xml::Attribute{
+          xml::kSchemaAndroid, "revisionCode", std::to_string(app_info.revision_code.value())});
     }
 
     std::stringstream split_name;
+    if (app_info.split_name) {
+      split_name << app_info.split_name.value() << ".";
+    }
     split_name << "config." << util::Joiner(constraints.configs, "_");
 
-    manifest_el->attributes.push_back(
-        xml::Attribute{"", "split", split_name.str()});
+    manifest_el->attributes.push_back(xml::Attribute{"", "split", split_name.str()});
 
-    std::unique_ptr<xml::Element> application_el =
-        util::make_unique<xml::Element>();
+    if (app_info.split_name) {
+      manifest_el->attributes.push_back(
+          xml::Attribute{"", "configForSplit", app_info.split_name.value()});
+    }
+
+    std::unique_ptr<xml::Element> application_el = util::make_unique<xml::Element>();
     application_el->name = "application";
-    application_el->attributes.push_back(
-        xml::Attribute{xml::kSchemaAndroid, "hasCode", "false"});
+    application_el->attributes.push_back(xml::Attribute{xml::kSchemaAndroid, "hasCode", "false"});
 
     manifest_el->AppendChild(std::move(application_el));
     namespace_android->AppendChild(std::move(manifest_el));
@@ -1412,6 +1409,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 +1761,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 +1778,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 +1810,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 +1899,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 +1946,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/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 313fe45..0c19c7a 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -29,10 +29,7 @@
 
 namespace aapt {
 
-/**
- * This is how PackageManager builds class names from AndroidManifest.xml
- * entries.
- */
+// This is how PackageManager builds class names from AndroidManifest.xml entries.
 static bool NameIsJavaClassName(xml::Element* el, xml::Attribute* attr,
                                 SourcePathDiagnostics* diag) {
   // We allow unqualified class names (ie: .HelloActivity)
@@ -90,6 +87,36 @@
   };
 }
 
+static bool AutoGenerateIsFeatureSplit(xml::Element* el, SourcePathDiagnostics* diag) {
+  constexpr const char* kFeatureSplit = "featureSplit";
+  constexpr const char* kIsFeatureSplit = "isFeatureSplit";
+
+  xml::Attribute* attr = el->FindAttribute({}, kFeatureSplit);
+  if (attr != nullptr) {
+    // Rewrite the featureSplit attribute to be "split". This is what the
+    // platform recognizes.
+    attr->name = "split";
+
+    // Now inject the android:isFeatureSplit="true" attribute.
+    xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, kIsFeatureSplit);
+    if (attr != nullptr) {
+      if (!ResourceUtils::ParseBool(attr->value).value_or_default(false)) {
+        // The isFeatureSplit attribute is false, which conflicts with the use
+        // of "featureSplit".
+        diag->Error(DiagMessage(el->line_number)
+                    << "attribute 'featureSplit' used in <manifest> but 'android:isFeatureSplit' "
+                       "is not 'true'");
+        return false;
+      }
+
+      // The attribute is already there and set to true, nothing to do.
+    } else {
+      el->attributes.push_back(xml::Attribute{xml::kSchemaAndroid, kIsFeatureSplit, "true"});
+    }
+  }
+  return true;
+}
+
 static bool VerifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
   xml::Attribute* attr = el->FindAttribute({}, "package");
   if (!attr) {
@@ -97,31 +124,34 @@
                 << "<manifest> tag is missing 'package' attribute");
     return false;
   } else if (ResourceUtils::IsReference(attr->value)) {
-    diag->Error(
-        DiagMessage(el->line_number)
-        << "attribute 'package' in <manifest> tag must not be a reference");
+    diag->Error(DiagMessage(el->line_number)
+                << "attribute 'package' in <manifest> tag must not be a reference");
     return false;
   } else if (!util::IsJavaPackageName(attr->value)) {
     diag->Error(DiagMessage(el->line_number)
-                << "attribute 'package' in <manifest> tag is not a valid Java "
-                   "package name: '"
+                << "attribute 'package' in <manifest> tag is not a valid Java package name: '"
                 << attr->value << "'");
     return false;
   }
+
+  attr = el->FindAttribute({}, "split");
+  if (attr) {
+    if (!util::IsJavaPackageName(attr->value)) {
+      diag->Error(DiagMessage(el->line_number) << "attribute 'split' in <manifest> tag is not a "
+                                                  "valid split name");
+      return false;
+    }
+  }
   return true;
 }
 
-/**
- * The coreApp attribute in <manifest> is not a regular AAPT attribute, so type
- * checking on it is manual.
- */
+// The coreApp attribute in <manifest> is not a regular AAPT attribute, so type
+// checking on it is manual.
 static bool FixCoreAppAttribute(xml::Element* el, SourcePathDiagnostics* diag) {
   if (xml::Attribute* attr = el->FindAttribute("", "coreApp")) {
-    std::unique_ptr<BinaryPrimitive> result =
-        ResourceUtils::TryParseBool(attr->value);
+    std::unique_ptr<BinaryPrimitive> result = ResourceUtils::TryParseBool(attr->value);
     if (!result) {
-      diag->Error(DiagMessage(el->line_number)
-                  << "attribute coreApp must be a boolean");
+      diag->Error(DiagMessage(el->line_number) << "attribute coreApp must be a boolean");
       return false;
     }
     attr->compiled_value = std::move(result);
@@ -172,8 +202,7 @@
   }
 
   if (options_.rename_instrumentation_target_package) {
-    if (!util::IsJavaPackageName(
-            options_.rename_instrumentation_target_package.value())) {
+    if (!util::IsJavaPackageName(options_.rename_instrumentation_target_package.value())) {
       diag->Error(DiagMessage()
                   << "invalid instrumentation target package override '"
                   << options_.rename_instrumentation_target_package.value()
@@ -203,6 +232,7 @@
 
   // Manifest actions.
   xml::XmlNodeAction& manifest_action = (*executor)["manifest"];
+  manifest_action.Action(AutoGenerateIsFeatureSplit);
   manifest_action.Action(VerifyManifest);
   manifest_action.Action(FixCoreAppAttribute);
   manifest_action.Action([&](xml::Element* el) -> bool {
@@ -276,6 +306,7 @@
   manifest_action["compatible-screens"]["screen"];
   manifest_action["supports-gl-texture"];
   manifest_action["meta-data"] = meta_data_action;
+  manifest_action["uses-split"].Action(RequiredNameIsJavaPackage);
 
   // Application actions.
   xml::XmlNodeAction& application_action = manifest_action["application"];
@@ -311,15 +342,13 @@
  public:
   using xml::Visitor::Visit;
 
-  explicit FullyQualifiedClassNameVisitor(const StringPiece& package)
-      : package_(package) {}
+  explicit FullyQualifiedClassNameVisitor(const StringPiece& package) : package_(package) {}
 
   void Visit(xml::Element* el) override {
     for (xml::Attribute& attr : el->attributes) {
       if (attr.namespace_uri == xml::kSchemaAndroid &&
           class_attributes_.find(attr.name) != class_attributes_.end()) {
-        if (Maybe<std::string> new_value =
-                util::GetFullyQualifiedClassName(package_, attr.value)) {
+        if (Maybe<std::string> new_value = util::GetFullyQualifiedClassName(package_, attr.value)) {
           attr.value = std::move(new_value.value());
         }
       }
@@ -334,8 +363,7 @@
   std::unordered_set<StringPiece> class_attributes_ = {"name"};
 };
 
-static bool RenameManifestPackage(const StringPiece& package_override,
-                                  xml::Element* manifest_el) {
+static bool RenameManifestPackage(const StringPiece& package_override, xml::Element* manifest_el) {
   xml::Attribute* attr = manifest_el->FindAttribute({}, "package");
 
   // We've already verified that the manifest element is present, with a package
@@ -358,8 +386,7 @@
     return false;
   }
 
-  if ((options_.min_sdk_version_default ||
-       options_.target_sdk_version_default) &&
+  if ((options_.min_sdk_version_default || options_.target_sdk_version_default) &&
       root->FindChild({}, "uses-sdk") == nullptr) {
     // Auto insert a <uses-sdk> element. This must be inserted before the
     // <application> tag. The device runtime PackageParser will make SDK version
@@ -374,8 +401,7 @@
     return false;
   }
 
-  if (!executor.Execute(xml::XmlActionExecutorPolicy::kWhitelist,
-                        context->GetDiagnostics(), doc)) {
+  if (!executor.Execute(xml::XmlActionExecutorPolicy::kWhitelist, context->GetDiagnostics(), doc)) {
     return false;
   }
 
@@ -383,8 +409,7 @@
     // Rename manifest package outside of the XmlActionExecutor.
     // We need to extract the old package name and FullyQualify all class
     // names.
-    if (!RenameManifestPackage(options_.rename_manifest_package.value(),
-                               root)) {
+    if (!RenameManifestPackage(options_.rename_manifest_package.value(), root)) {
       return false;
     }
   }
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/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py
index eb8a1cc..008344c 100755
--- a/tools/fonts/fontchain_lint.py
+++ b/tools/fonts/fontchain_lint.py
@@ -521,36 +521,21 @@
     # add zwj sequences not in the current emoji-zwj-sequences.txt
     adjusted_emoji_zwj_sequences = dict(_emoji_zwj_sequences)
     adjusted_emoji_zwj_sequences.update(_emoji_zwj_sequences)
-    # single parent families
+    # Wrestlers with modifiers
     additional_emoji_zwj = (
-        (0x1F468, 0x200D, 0x1F466),
-        (0x1F468, 0x200D, 0x1F467),
-        (0x1F468, 0x200D, 0x1F466, 0x200D, 0x1F466),
-        (0x1F468, 0x200D, 0x1F467, 0x200D, 0x1F466),
-        (0x1F468, 0x200D, 0x1F467, 0x200D, 0x1F467),
-        (0x1F469, 0x200D, 0x1F466),
-        (0x1F469, 0x200D, 0x1F467),
-        (0x1F469, 0x200D, 0x1F466, 0x200D, 0x1F466),
-        (0x1F469, 0x200D, 0x1F467, 0x200D, 0x1F466),
-        (0x1F469, 0x200D, 0x1F467, 0x200D, 0x1F467),
-    )
-    # sequences formed from man and woman and optional fitzpatrick modifier
-    modified_extensions = (
-        0x2696,
-        0x2708,
-        0x1F3A8,
-        0x1F680,
-        0x1F692,
+        (0x1F93C, 0x1F3FB, 0x200D, 0x2640),
+        (0x1F93C, 0x1F3FB, 0x200D, 0x2642),
+        (0x1F93C, 0x1F3FC, 0x200D, 0x2640),
+        (0x1F93C, 0x1F3FC, 0x200D, 0x2642),
+        (0x1F93C, 0x1F3FD, 0x200D, 0x2640),
+        (0x1F93C, 0x1F3FD, 0x200D, 0x2642),
+        (0x1F93C, 0x1F3FE, 0x200D, 0x2640),
+        (0x1F93C, 0x1F3FE, 0x200D, 0x2642),
+        (0x1F93C, 0x1F3FF, 0x200D, 0x2640),
+        (0x1F93C, 0x1F3FF, 0x200D, 0x2642),
     )
     for seq in additional_emoji_zwj:
         adjusted_emoji_zwj_sequences[seq] = 'Emoji_ZWJ_Sequence'
-    for ext in modified_extensions:
-        for base in (0x1F468, 0x1F469):
-            seq = (base, 0x200D, ext)
-            adjusted_emoji_zwj_sequences[seq] = 'Emoji_ZWJ_Sequence'
-            for modifier in range(0x1F3FB, 0x1F400):
-                seq = (base, modifier, 0x200D, ext)
-                adjusted_emoji_zwj_sequences[seq] = 'Emoji_ZWJ_Sequence'
 
     for sequence in _emoji_sequences.keys():
         sequence = tuple(ch for ch in sequence if ch != EMOJI_VS)
diff --git a/tools/layoutlib/bridge/src/android/graphics/BaseCanvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BaseCanvas_Delegate.java
index b1e71b2..cc71053 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BaseCanvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BaseCanvas_Delegate.java
@@ -24,6 +24,7 @@
 import com.android.ninepatch.NinePatchChunk;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
+import android.annotation.Nullable;
 import android.text.TextUtils;
 
 import java.awt.*;
@@ -31,6 +32,8 @@
 import java.awt.geom.Arc2D;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
 
 public class BaseCanvas_Delegate {
     // ---- delegate manager ----
@@ -646,9 +649,15 @@
         forceSrcMode[0] = false;
 
         // if the bitmap config is alpha_8, then we erase all color value from it
-        // before drawing it.
+        // before drawing it or apply the texture from the shader if present.
         if (bitmap.getConfig() == Bitmap.Config.ALPHA_8) {
-            fixAlpha8Bitmap(image);
+            Shader_Delegate shader = paint.getShader();
+            java.awt.Paint javaPaint = null;
+            if (shader instanceof BitmapShader_Delegate) {
+                javaPaint = shader.getJavaPaint();
+            }
+
+            fixAlpha8Bitmap(image, javaPaint);
         } else if (!bitmap.hasAlpha()) {
             // hasAlpha is merely a rendering hint. There can in fact be alpha values
             // in the bitmap but it should be ignored at drawing time.
@@ -672,16 +681,37 @@
         return image;
     }
 
-    private static void fixAlpha8Bitmap(final BufferedImage image) {
+    /**
+     * This method will apply the correct color to the passed "only alpha" image. Colors on the
+     * passed image will be destroyed.
+     * If the passed javaPaint is null, the color will be set to 0. If a paint is passed, it will
+     * be used to obtain the color that will be applied.
+     * <p/>
+     * This will destroy the passed image color channel.
+     */
+    private static void fixAlpha8Bitmap(final BufferedImage image,
+            @Nullable java.awt.Paint javaPaint) {
         int w = image.getWidth();
         int h = image.getHeight();
+
+        DataBuffer texture = null;
+        if (javaPaint != null) {
+            PaintContext context = javaPaint.createContext(ColorModel.getRGBdefault(), null, null,
+                    new AffineTransform(), null);
+            texture = context.getRaster(0, 0, w, h).getDataBuffer();
+        }
+
         int[] argb = new int[w * h];
         image.getRGB(0, 0, image.getWidth(), image.getHeight(), argb, 0, image.getWidth());
 
         final int length = argb.length;
-        for (int i = 0 ; i < length; i++) {
+        for (int i = 0; i < length; i++) {
             argb[i] &= 0xFF000000;
+            if (texture != null) {
+                argb[i] |= texture.getElem(i) & 0x00FFFFFF;
+            }
         }
+
         image.setRGB(0, 0, w, h, argb, 0, w);
     }
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java
index bd934d0..cb013b6 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java
@@ -66,7 +66,7 @@
     // ---- native methods ----
 
     @LayoutlibDelegate
-    /*package*/ static void destroyFilter(long native_instance) {
+    /*package*/ static void nSafeUnref(long native_instance) {
         sManager.removeJavaReferenceFor(native_instance);
     }
 
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/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/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index 4689491..ffbe7c4 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -18,12 +18,12 @@
 
 import com.android.internal.os.IResultReceiver;
 
-import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.util.MergedConfiguration;
 import android.view.DragEvent;
 import android.view.IWindow;
 
@@ -50,7 +50,7 @@
 
     @Override
     public void resized(Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5, Rect rect6,
-            boolean b, Configuration configuration, Rect rect7, boolean b2, boolean b3, int i0)
+            boolean b, MergedConfiguration mergedConfig, Rect rect7, boolean b2, boolean b3, int i0)
             throws RemoteException {
         // pass for now.
     }
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..2c88394 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
@@ -17,12 +17,12 @@
 package com.android.layoutlib.bridge.android;
 
 import android.content.ClipData;
-import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.util.MergedConfiguration;
 import android.view.IWindow;
 import android.view.IWindowId;
 import android.view.IWindowSession;
@@ -89,25 +89,13 @@
     @Override
     public int relayout(IWindow iWindow, int i, LayoutParams layoutParams, int i2,
             int i3, int i4, int i5, Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5,
-            Rect rect6, Rect rect7, Configuration configuration, Surface surface)
+            Rect rect6, Rect rect7, MergedConfiguration mergedConfig, Surface surface)
             throws RemoteException {
         // pass for now.
         return 0;
     }
 
     @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/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/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index a1099f8..04f9059 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -281,7 +281,9 @@
     public int apChannel = 0;
 
     /**
-     * Pre-shared key for use with WPA-PSK.
+     * Pre-shared key for use with WPA-PSK. Either an ASCII string enclosed in
+     * double quotation marks (e.g., {@code "abcdefghij"} for PSK passphrase or
+     * a string of 64 hex digits for raw PSK.
      * <p/>
      * When the value of this key is read, the actual key is
      * not returned, just a "*" if the key has a value, or the null
@@ -305,7 +307,7 @@
     /**
      * Priority determines the preference given to a network by {@code wpa_supplicant}
      * when choosing an access point with which to associate.
-     * @deprecated Priority is no longer used.
+     * @deprecated This field does not exist anymore.
      */
     @Deprecated
     public int priority;
@@ -434,6 +436,13 @@
     public int dtimInterval = 0;
 
     /**
+     * Flag indicating if this configuration represents a legacy Passpoint configuration
+     * (Release N or older).  This is used for migrating Passpoint configuration from N to O.
+     * This will no longer be needed after O.
+     * @hide
+     */
+    public boolean isLegacyPasspointConfig = false;
+    /**
      * @hide
      * Uid of app creating the configuration
      */
@@ -1961,6 +1970,7 @@
             mCachedConfigKey = null; //force null configKey
             selfAdded = source.selfAdded;
             validatedInternetAccess = source.validatedInternetAccess;
+            isLegacyPasspointConfig = source.isLegacyPasspointConfig;
             ephemeral = source.ephemeral;
             meteredHint = source.meteredHint;
             meteredOverride = source.meteredOverride;
@@ -2037,6 +2047,7 @@
         dest.writeInt(selfAdded ? 1 : 0);
         dest.writeInt(didSelfAdd ? 1 : 0);
         dest.writeInt(validatedInternetAccess ? 1 : 0);
+        dest.writeInt(isLegacyPasspointConfig ? 1 : 0);
         dest.writeInt(ephemeral ? 1 : 0);
         dest.writeInt(meteredHint ? 1 : 0);
         dest.writeInt(meteredOverride ? 1 : 0);
@@ -2103,6 +2114,7 @@
                 config.selfAdded = in.readInt() != 0;
                 config.didSelfAdd = in.readInt() != 0;
                 config.validatedInternetAccess = in.readInt() != 0;
+                config.isLegacyPasspointConfig = in.readInt() != 0;
                 config.ephemeral = in.readInt() != 0;
                 config.meteredHint = in.readInt() != 0;
                 config.meteredOverride = in.readInt() != 0;
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/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 7de55aa..333a4f7d 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -28,6 +28,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -329,6 +330,50 @@
                 mUsageLimitStartTimeInMs, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes);
     }
 
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("UpdateIdentifier: ").append(mUpdateIdentifier).append("\n");
+        builder.append("CredentialPriority: ").append(mCredentialPriority).append("\n");
+        builder.append("SubscriptionCreationTime: ").append(
+                mSubscriptionCreationTimeInMs != Long.MIN_VALUE
+                ? new Date(mSubscriptionCreationTimeInMs) : "Not specified").append("\n");
+        builder.append("SubscriptionExpirationTime: ").append(
+                mSubscriptionExpirationTimeInMs != Long.MIN_VALUE
+                ? new Date(mSubscriptionExpirationTimeInMs) : "Not specified").append("\n");
+        builder.append("UsageLimitStartTime: ").append(mUsageLimitStartTimeInMs != Long.MIN_VALUE
+                ? new Date(mUsageLimitStartTimeInMs) : "Not specified").append("\n");
+        builder.append("UsageTimePeriod: ").append(mUsageLimitUsageTimePeriodInMinutes)
+                .append("\n");
+        builder.append("UsageLimitDataLimit: ").append(mUsageLimitDataLimit).append("\n");
+        builder.append("UsageLimitTimeLimit: ").append(mUsageLimitTimeLimitInMinutes).append("\n");
+        if (mHomeSp != null) {
+            builder.append("HomeSP Begin ---\n");
+            builder.append(mHomeSp);
+            builder.append("HomeSP End ---\n");
+        }
+        if (mCredential != null) {
+            builder.append("Credential Begin ---\n");
+            builder.append(mCredential);
+            builder.append("Credential End ---\n");
+        }
+        if (mPolicy != null) {
+            builder.append("Policy Begin ---\n");
+            builder.append(mPolicy);
+            builder.append("Policy End ---\n");
+        }
+        if (mSubscriptionUpdate != null) {
+            builder.append("SubscriptionUpdate Begin ---\n");
+            builder.append(mSubscriptionUpdate);
+            builder.append("SubscriptionUpdate End ---\n");
+        }
+        if (mTrustRootCertList != null) {
+            builder.append("TrustRootCertServers: ").append(mTrustRootCertList.keySet())
+                    .append("\n");
+        }
+        return builder.toString();
+    }
+
     /**
      * Validate the configuration data.
      *
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index d8da84f..67fa1bb 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -30,6 +30,7 @@
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
 import java.util.Arrays;
+import java.util.Date;
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
@@ -282,6 +283,18 @@
                     mAbleToShare, mEapType, mNonEapInnerMethod);
         }
 
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            builder.append("Username: ").append(mUsername).append("\n");
+            builder.append("MachineManaged: ").append(mMachineManaged).append("\n");
+            builder.append("SoftTokenApp: ").append(mSoftTokenApp).append("\n");
+            builder.append("AbleToShare: ").append(mAbleToShare).append("\n");
+            builder.append("EAPType: ").append(mEapType).append("\n");
+            builder.append("AuthMethod: ").append(mNonEapInnerMethod).append("\n");
+            return builder.toString();
+        }
+
         /**
          * Validate the configuration data.
          *
@@ -440,6 +453,11 @@
             return Objects.hash(mCertType, mCertSha256Fingerprint);
         }
 
+        @Override
+        public String toString() {
+            return "CertificateType: " + mCertType + "\n";
+        }
+
         /**
          * Validate the configuration data.
          *
@@ -562,6 +580,14 @@
         }
 
         @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            builder.append("IMSI: ").append(mImsi).append("\n");
+            builder.append("EAPType: ").append(mEapType).append("\n");
+            return builder.toString();
+        }
+
+        @Override
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeString(mImsi);
             dest.writeInt(mEapType);
@@ -767,6 +793,33 @@
                 mCaCertificate, mClientCertificateChain, mClientPrivateKey);
     }
 
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("Realm: ").append(mRealm).append("\n");
+        builder.append("CreationTime: ").append(mCreationTimeInMs != Long.MIN_VALUE
+                ? new Date(mCreationTimeInMs) : "Not specified").append("\n");
+        builder.append("ExpirationTime: ").append(mExpirationTimeInMs != Long.MIN_VALUE
+                ? new Date(mExpirationTimeInMs) : "Not specified").append("\n");
+        builder.append("CheckAAAServerStatus: ").append(mCheckAaaServerCertStatus).append("\n");
+        if (mUserCredential != null) {
+            builder.append("UserCredential Begin ---\n");
+            builder.append(mUserCredential);
+            builder.append("UserCredential End ---\n");
+        }
+        if (mCertCredential != null) {
+            builder.append("CertificateCredential Begin ---\n");
+            builder.append(mCertCredential);
+            builder.append("CertificateCredential End ---\n");
+        }
+        if (mSimCredential != null) {
+            builder.append("SIMCredential Begin ---\n");
+            builder.append(mSimCredential);
+            builder.append("SIMCredential End ---\n");
+        }
+        return builder.toString();
+    }
+
     /**
      * Validate the configuration data.
      *
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
index 68bdf37..9192ab0 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
@@ -241,6 +241,20 @@
                 mMatchAnyOis, mOtherHomePartners, mRoamingConsortiumOis);
     }
 
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("FQDN: ").append(mFqdn).append("\n");
+        builder.append("FriendlyName: ").append(mFriendlyName).append("\n");
+        builder.append("IconURL: ").append(mIconUrl).append("\n");
+        builder.append("HomeNetworkIDs: ").append(mHomeNetworkIds).append("\n");
+        builder.append("MatchAllOIs: ").append(mMatchAllOis).append("\n");
+        builder.append("MatchAnyOIs: ").append(mMatchAnyOis).append("\n");
+        builder.append("OtherHomePartners: ").append(mOtherHomePartners).append("\n");
+        builder.append("RoamingConsortiumOIs: ").append(mRoamingConsortiumOis).append("\n");
+        return builder.toString();
+    }
+
     /**
      * Validate HomeSp data.
      *
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Policy.java b/wifi/java/android/net/wifi/hotspot2/pps/Policy.java
index da36a11..1df70f8 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Policy.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Policy.java
@@ -249,6 +249,16 @@
             return Objects.hash(mFqdn, mFqdnExactMatch, mPriority, mCountries);
         }
 
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            builder.append("FQDN: ").append(mFqdn).append("\n");
+            builder.append("ExactMatch: ").append("mFqdnExactMatch").append("\n");
+            builder.append("Priority: ").append(mPriority).append("\n");
+            builder.append("Countries: ").append(mCountries).append("\n");
+            return builder.toString();
+        }
+
         /**
          * Validate RoamingParnter data.
          *
@@ -390,6 +400,29 @@
                 mPolicyUpdate);
     }
 
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("MinHomeDownlinkBandwidth: ").append(mMinHomeDownlinkBandwidth)
+                .append("\n");
+        builder.append("MinHomeUplinkBandwidth: ").append(mMinHomeUplinkBandwidth).append("\n");
+        builder.append("MinRoamingDownlinkBandwidth: ").append(mMinRoamingDownlinkBandwidth)
+                .append("\n");
+        builder.append("MinRoamingUplinkBandwidth: ").append(mMinRoamingUplinkBandwidth)
+                .append("\n");
+        builder.append("ExcludedSSIDList: ").append(mExcludedSsidList).append("\n");
+        builder.append("RequiredProtoPortMap: ").append(mRequiredProtoPortMap).append("\n");
+        builder.append("MaximumBSSLoadValue: ").append(mMaximumBssLoadValue).append("\n");
+        builder.append("PreferredRoamingPartnerList: ").append(mPreferredRoamingPartnerList)
+                .append("\n");
+        if (mPolicyUpdate != null) {
+            builder.append("PolicyUpdate Begin ---\n");
+            builder.append(mPolicyUpdate);
+            builder.append("PolicyUpdate End ---\n");
+        }
+        return builder.toString();
+    }
+
     /**
      * Validate Policy data.
      *
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java
index ae051b0..a7adfeb 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java
@@ -247,6 +247,18 @@
                 mTrustRootCertSha256Fingerprint);
     }
 
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("UpdateInterval: ").append(mUpdateIntervalInMinutes).append("\n");
+        builder.append("UpdateMethod: ").append(mUpdateMethod).append("\n");
+        builder.append("Restriction: ").append(mRestriction).append("\n");
+        builder.append("ServerURI: ").append(mServerUri).append("\n");
+        builder.append("Username: ").append(mUsername).append("\n");
+        builder.append("TrustRootCertURL: ").append(mTrustRootCertUrl).append("\n");
+        return builder.toString();
+    }
+
     /**
      * Validate UpdateParameter data.
      *
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)