Merge "Refactor KeySet code."
diff --git a/Android.mk b/Android.mk
index 1df2af3..f7f3652 100644
--- a/Android.mk
+++ b/Android.mk
@@ -45,6 +45,7 @@
        core/java/android/content/EventLogTags.logtags \
        core/java/android/speech/tts/EventLogTags.logtags \
        core/java/android/webkit/EventLogTags.logtags \
+       core/java/com/android/internal/logging/EventLogTags.logtags \
 
 ## READ ME: ########################################################
 ##
@@ -151,6 +152,8 @@
 	core/java/android/hardware/display/IDisplayManager.aidl \
 	core/java/android/hardware/display/IDisplayManagerCallback.aidl \
 	core/java/android/hardware/display/IVirtualDisplayCallback.aidl \
+	core/java/android/hardware/fingerprint/IFingerprintService.aidl \
+	core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl \
 	core/java/android/hardware/hdmi/IHdmiControlCallback.aidl \
 	core/java/android/hardware/hdmi/IHdmiControlService.aidl \
 	core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl \
@@ -226,8 +229,6 @@
 	core/java/android/service/dreams/IDreamManager.aidl \
 	core/java/android/service/dreams/IDreamService.aidl \
 	core/java/android/service/persistentdata/IPersistentDataBlockService.aidl \
-	core/java/android/service/fingerprint/IFingerprintService.aidl \
-	core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl \
 	core/java/android/service/trust/ITrustAgentService.aidl \
 	core/java/android/service/trust/ITrustAgentServiceCallback.aidl \
 	core/java/android/service/voice/IVoiceInteractionService.aidl \
@@ -244,6 +245,7 @@
 	core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
 	core/java/android/view/IApplicationToken.aidl \
 	core/java/android/view/IAssetAtlas.aidl \
+	core/java/android/view/IGraphicsStats.aidl \
 	core/java/android/view/IInputFilter.aidl \
 	core/java/android/view/IInputFilterHost.aidl \
 	core/java/android/view/IOnKeyguardExitResult.aidl \
diff --git a/api/current.txt b/api/current.txt
index c004b15..380fcb4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -92,6 +92,7 @@
     field public static final java.lang.String MOUNT_FORMAT_FILESYSTEMS = "android.permission.MOUNT_FORMAT_FILESYSTEMS";
     field public static final java.lang.String MOUNT_UNMOUNT_FILESYSTEMS = "android.permission.MOUNT_UNMOUNT_FILESYSTEMS";
     field public static final java.lang.String NFC = "android.permission.NFC";
+    field public static final java.lang.String PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS";
     field public static final deprecated java.lang.String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY";
     field public static final java.lang.String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS";
     field public static final java.lang.String READ_CALENDAR = "android.permission.READ_CALENDAR";
@@ -303,8 +304,8 @@
     field public static final int alphabeticShortcut = 16843235; // 0x10101e3
     field public static final int alwaysDrawnWithCache = 16842991; // 0x10100ef
     field public static final int alwaysRetainTaskState = 16843267; // 0x1010203
-    field public static final int amPmBackgroundColor = 16843941; // 0x10104a5
-    field public static final int amPmTextColor = 16843940; // 0x10104a4
+    field public static final deprecated int amPmBackgroundColor = 16843941; // 0x10104a5
+    field public static final deprecated int amPmTextColor = 16843940; // 0x10104a4
     field public static final int ambientShadowAlpha = 16843966; // 0x10104be
     field public static final int angle = 16843168; // 0x10101a0
     field public static final int animateFirstView = 16843477; // 0x10102d5
@@ -329,6 +330,7 @@
     field public static final int autoStart = 16843445; // 0x10102b5
     field public static final deprecated int autoText = 16843114; // 0x101016a
     field public static final int autoUrlDetect = 16843404; // 0x101028c
+    field public static final int autoVerify = 16844010; // 0x10104ea
     field public static final int background = 16842964; // 0x10100d4
     field public static final int backgroundDimAmount = 16842802; // 0x1010032
     field public static final int backgroundDimEnabled = 16843295; // 0x101021f
@@ -352,6 +354,7 @@
     field public static final int bottomRightRadius = 16843180; // 0x10101ac
     field public static final int breadCrumbShortTitle = 16843524; // 0x1010304
     field public static final int breadCrumbTitle = 16843523; // 0x1010303
+    field public static final int breakStrategy = 16844011; // 0x10104eb
     field public static final int bufferType = 16843086; // 0x101014e
     field public static final int button = 16843015; // 0x1010107
     field public static final int buttonBarButtonStyle = 16843567; // 0x101032f
@@ -464,8 +467,8 @@
     field public static final int datePickerMode = 16843955; // 0x10104b3
     field public static final int datePickerStyle = 16843612; // 0x101035c
     field public static final int dateTextAppearance = 16843593; // 0x1010349
-    field public static final int dayOfWeekBackground = 16843924; // 0x1010494
-    field public static final int dayOfWeekTextAppearance = 16843925; // 0x1010495
+    field public static final deprecated int dayOfWeekBackground = 16843924; // 0x1010494
+    field public static final deprecated int dayOfWeekTextAppearance = 16843925; // 0x1010495
     field public static final int debuggable = 16842767; // 0x101000f
     field public static final int defaultValue = 16843245; // 0x10101ed
     field public static final int delay = 16843212; // 0x10101cc
@@ -555,6 +558,7 @@
     field public static final int expandableListViewWhiteStyle = 16843446; // 0x10102b6
     field public static final int exported = 16842768; // 0x1010010
     field public static final int extraTension = 16843371; // 0x101026b
+    field public static final int extractNativeLibs = 16844008; // 0x10104e8
     field public static final int factor = 16843219; // 0x10101d3
     field public static final int fadeDuration = 16843384; // 0x1010278
     field public static final int fadeEnabled = 16843390; // 0x101027e
@@ -587,7 +591,7 @@
     field public static final int flipInterval = 16843129; // 0x1010179
     field public static final int focusable = 16842970; // 0x10100da
     field public static final int focusableInTouchMode = 16842971; // 0x10100db
-    field public static final int focusedMonthDateColor = 16843587; // 0x1010343
+    field public static final deprecated int focusedMonthDateColor = 16843587; // 0x1010343
     field public static final int fontFamily = 16843692; // 0x10103ac
     field public static final int fontFeatureSettings = 16843959; // 0x10104b7
     field public static final int footerDividersEnabled = 16843311; // 0x101022f
@@ -649,13 +653,13 @@
     field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
     field public static final int hardwareAccelerated = 16843475; // 0x10102d3
     field public static final int hasCode = 16842764; // 0x101000c
-    field public static final int headerAmPmTextAppearance = 16843936; // 0x10104a0
+    field public static final deprecated int headerAmPmTextAppearance = 16843936; // 0x10104a0
     field public static final int headerBackground = 16843055; // 0x101012f
-    field public static final int headerDayOfMonthTextAppearance = 16843927; // 0x1010497
+    field public static final deprecated int headerDayOfMonthTextAppearance = 16843927; // 0x1010497
     field public static final int headerDividersEnabled = 16843310; // 0x101022e
-    field public static final int headerMonthTextAppearance = 16843926; // 0x1010496
-    field public static final int headerTimeTextAppearance = 16843935; // 0x101049f
-    field public static final int headerYearTextAppearance = 16843928; // 0x1010498
+    field public static final deprecated int headerMonthTextAppearance = 16843926; // 0x1010496
+    field public static final deprecated int headerTimeTextAppearance = 16843935; // 0x101049f
+    field public static final deprecated int headerYearTextAppearance = 16843928; // 0x1010498
     field public static final int height = 16843093; // 0x1010155
     field public static final int hideOnContentScroll = 16843843; // 0x1010443
     field public static final int hint = 16843088; // 0x1010150
@@ -1094,8 +1098,8 @@
     field public static final int selectable = 16843238; // 0x10101e6
     field public static final int selectableItemBackground = 16843534; // 0x101030e
     field public static final int selectableItemBackgroundBorderless = 16843868; // 0x101045c
-    field public static final int selectedDateVerticalBar = 16843591; // 0x1010347
-    field public static final int selectedWeekBackgroundColor = 16843586; // 0x1010342
+    field public static final deprecated int selectedDateVerticalBar = 16843591; // 0x1010347
+    field public static final deprecated int selectedWeekBackgroundColor = 16843586; // 0x1010342
     field public static final int sessionService = 16843837; // 0x101043d
     field public static final int settingsActivity = 16843301; // 0x1010225
     field public static final int setupActivity = 16843766; // 0x10103f6
@@ -1114,8 +1118,8 @@
     field public static final int showOnLockScreen = 16843721; // 0x10103c9
     field public static final int showSilent = 16843259; // 0x10101fb
     field public static final int showText = 16843949; // 0x10104ad
-    field public static final int showWeekNumber = 16843582; // 0x101033e
-    field public static final int shownWeekCount = 16843585; // 0x1010341
+    field public static final deprecated int showWeekNumber = 16843582; // 0x101033e
+    field public static final deprecated int shownWeekCount = 16843585; // 0x1010341
     field public static final int shrinkColumns = 16843082; // 0x101014a
     field public static final deprecated int singleLine = 16843101; // 0x101015d
     field public static final int singleUser = 16843711; // 0x10103bf
@@ -1352,13 +1356,14 @@
     field public static final int typeface = 16842902; // 0x1010096
     field public static final int uiOptions = 16843672; // 0x1010398
     field public static final int uncertainGestureColor = 16843382; // 0x1010276
-    field public static final int unfocusedMonthDateColor = 16843588; // 0x1010344
+    field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
     field public static final int unselectedAlpha = 16843278; // 0x101020e
     field public static final int updatePeriodMillis = 16843344; // 0x1010250
     field public static final int useDefaultMargins = 16843641; // 0x1010379
     field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
     field public static final int useLevel = 16843167; // 0x101019f
     field public static final int userVisible = 16843409; // 0x1010291
+    field public static final int usesCleartextTraffic = 16844009; // 0x10104e9
     field public static final int value = 16842788; // 0x1010024
     field public static final int valueFrom = 16843486; // 0x10102de
     field public static final int valueTo = 16843487; // 0x10102df
@@ -1394,8 +1399,8 @@
     field public static final int webTextViewStyle = 16843449; // 0x10102b9
     field public static final int webViewStyle = 16842885; // 0x1010085
     field public static final int weekDayTextAppearance = 16843592; // 0x1010348
-    field public static final int weekNumberColor = 16843589; // 0x1010345
-    field public static final int weekSeparatorLineColor = 16843590; // 0x1010346
+    field public static final deprecated int weekNumberColor = 16843589; // 0x1010345
+    field public static final deprecated int weekSeparatorLineColor = 16843590; // 0x1010346
     field public static final int weightSum = 16843048; // 0x1010128
     field public static final int widgetCategory = 16843716; // 0x10103c4
     field public static final int widgetLayout = 16843243; // 0x10101eb
@@ -1453,8 +1458,8 @@
     field public static final int x = 16842924; // 0x10100ac
     field public static final int xlargeScreens = 16843455; // 0x10102bf
     field public static final int y = 16842925; // 0x10100ad
-    field public static final int yearListItemTextAppearance = 16843929; // 0x1010499
-    field public static final int yearListSelectorColor = 16843930; // 0x101049a
+    field public static final deprecated int yearListItemTextAppearance = 16843929; // 0x1010499
+    field public static final deprecated int yearListSelectorColor = 16843930; // 0x101049a
     field public static final int yesNoPreferenceStyle = 16842896; // 0x1010090
     field public static final int zAdjustment = 16843201; // 0x10101c1
   }
@@ -2732,6 +2737,7 @@
   }
 
   public class AccountManager {
+    method public boolean accountAuthenticated(android.accounts.Account);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle);
     method public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean);
@@ -2792,6 +2798,7 @@
     field public static final java.lang.String KEY_ERROR_CODE = "errorCode";
     field public static final java.lang.String KEY_ERROR_MESSAGE = "errorMessage";
     field public static final java.lang.String KEY_INTENT = "intent";
+    field public static final java.lang.String KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH = "lastAuthenticatedTimeMillisEpoch";
     field public static final java.lang.String KEY_PASSWORD = "password";
     field public static final java.lang.String KEY_USERDATA = "userdata";
     field public static final java.lang.String LOGIN_ACCOUNTS_CHANGED_ACTION = "android.accounts.LOGIN_ACCOUNTS_CHANGED";
@@ -3431,6 +3438,7 @@
     method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
     method public void onProvideAssistContent(android.app.AssistContent);
     method public void onProvideAssistData(android.os.Bundle);
+    method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
     method protected void onRestart();
     method protected void onRestoreInstanceState(android.os.Bundle);
     method public void onRestoreInstanceState(android.os.Bundle, android.os.PersistableBundle);
@@ -3461,6 +3469,7 @@
     method public boolean releaseInstance();
     method public final deprecated void removeDialog(int);
     method public void reportFullyDrawn();
+    method public final void requestPermissions(java.lang.String[], int);
     method public boolean requestVisibleBehind(boolean);
     method public final boolean requestWindowFeature(int);
     method public final void runOnUiThread(java.lang.Runnable);
@@ -3977,22 +3986,25 @@
     method public int describeContents();
     method public android.content.ComponentName getActivityComponent();
     method public static android.app.AssistStructure getAssistStructure(android.os.Bundle);
-    method public void getWindowAt(int, android.app.AssistStructure.ViewNode);
-    method public int getWindowCount();
+    method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
+    method public int getWindowNodeCount();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final java.lang.String ASSIST_KEY = "android:assist_structure";
     field public static final android.os.Parcelable.Creator<android.app.AssistStructure> CREATOR;
   }
 
   public static class AssistStructure.ViewNode {
-    ctor public AssistStructure.ViewNode();
-    method public void getChildAt(int, android.app.AssistStructure.ViewNode);
+    method public android.app.AssistStructure.ViewNode getChildAt(int);
     method public int getChildCount();
     method public java.lang.String getClassName();
-    method public java.lang.String getContentDescription();
+    method public java.lang.CharSequence getContentDescription();
     method public android.os.Bundle getExtras();
     method public int getHeight();
     method public java.lang.String getHint();
+    method public int getId();
+    method public java.lang.String getIdEntry();
+    method public java.lang.String getIdPackage();
+    method public java.lang.String getIdType();
     method public int getLeft();
     method public int getScrollX();
     method public int getScrollY();
@@ -4023,6 +4035,15 @@
     field public static final int TEXT_STYLE_UNDERLINE = 4; // 0x4
   }
 
+  public static class AssistStructure.WindowNode {
+    method public int getHeight();
+    method public int getLeft();
+    method public android.app.AssistStructure.ViewNode getRootViewNode();
+    method public java.lang.CharSequence getTitle();
+    method public int getTop();
+    method public int getWidth();
+  }
+
   public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener {
     ctor public DatePickerDialog(android.content.Context, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
     ctor public DatePickerDialog(android.content.Context, int, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
@@ -4303,6 +4324,7 @@
     method public void onOptionsMenuClosed(android.view.Menu);
     method public void onPause();
     method public void onPrepareOptionsMenu(android.view.Menu);
+    method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
     method public void onResume();
     method public void onSaveInstanceState(android.os.Bundle);
     method public void onStart();
@@ -4311,6 +4333,7 @@
     method public void onViewCreated(android.view.View, android.os.Bundle);
     method public void onViewStateRestored(android.os.Bundle);
     method public void registerForContextMenu(android.view.View);
+    method public final void requestPermissions(java.lang.String[], int);
     method public void setAllowEnterTransitionOverlap(boolean);
     method public void setAllowReturnTransitionOverlap(boolean);
     method public void setArguments(android.os.Bundle);
@@ -4684,6 +4707,7 @@
     field public static final java.lang.String CATEGORY_PROGRESS = "progress";
     field public static final java.lang.String CATEGORY_PROMO = "promo";
     field public static final java.lang.String CATEGORY_RECOMMENDATION = "recommendation";
+    field public static final java.lang.String CATEGORY_REMINDER = "reminder";
     field public static final java.lang.String CATEGORY_SERVICE = "service";
     field public static final java.lang.String CATEGORY_SOCIAL = "social";
     field public static final java.lang.String CATEGORY_STATUS = "status";
@@ -5663,12 +5687,18 @@
     field public static final java.lang.String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
     field public static final java.lang.String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
     field public static final java.lang.String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE";
+    field public static final java.lang.String EXTRA_PROVISIONING_BT_DEVICE_ID = "android.app.extra.PROVISIONING_BT_DEVICE_ID";
+    field public static final java.lang.String EXTRA_PROVISIONING_BT_MAC_ADDRESS = "android.app.extra.PROVISIONING_BT_MAC_ADDRESS";
+    field public static final java.lang.String EXTRA_PROVISIONING_BT_USE_PROXY = "android.app.extra.PROVISIONING_BT_USE_PROXY";
+    field public static final java.lang.String EXTRA_PROVISIONING_BT_UUID = "android.app.extra.PROVISIONING_BT_UUID";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME";
+    field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE = "android.app.extra.PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION";
     field public static final deprecated java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME";
+    field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION";
@@ -5697,6 +5727,7 @@
     field public static final int KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS = 8; // 0x8
     field public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1; // 0x1
     field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
+    field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC_V2 = "application/com.android.managedprovisioning.v2";
     field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000
     field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000
     field public static final int PASSWORD_QUALITY_BIOMETRIC_WEAK = 32768; // 0x8000
@@ -5882,6 +5913,37 @@
     field public static final android.os.Parcelable.Creator<android.app.usage.ConfigurationStats> CREATOR;
   }
 
+  public class NetworkStatsManager {
+    method public android.app.usage.NetworkUsageStats queryDetails(int, java.lang.String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+    method public android.app.usage.NetworkUsageStats queryDetailsForUid(int, java.lang.String, long, long, int) throws android.os.RemoteException, java.lang.SecurityException;
+    method public android.app.usage.NetworkUsageStats querySummary(int, java.lang.String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+    method public android.app.usage.NetworkUsageStats.Bucket querySummaryForDevice(int, java.lang.String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+    method public android.app.usage.NetworkUsageStats.Bucket querySummaryForUser(int, java.lang.String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+  }
+
+  public final class NetworkUsageStats implements java.lang.AutoCloseable {
+    method public void close();
+    method public boolean getNextBucket(android.app.usage.NetworkUsageStats.Bucket);
+    method public boolean hasNextBucket();
+  }
+
+  public static class NetworkUsageStats.Bucket {
+    ctor public NetworkUsageStats.Bucket();
+    method public long getEndTimeStamp();
+    method public long getRxBytes();
+    method public long getRxPackets();
+    method public long getStartTimeStamp();
+    method public int getState();
+    method public long getTxBytes();
+    method public long getTxPackets();
+    method public int getUid();
+    field public static final int STATE_ALL = -1; // 0xffffffff
+    field public static final int STATE_DEFAULT = 1; // 0x1
+    field public static final int STATE_FOREGROUND = 2; // 0x2
+    field public static final int UID_REMOVED = -4; // 0xfffffffc
+    field public static final int UID_TETHERING = -5; // 0xfffffffb
+  }
+
   public final class UsageEvents implements android.os.Parcelable {
     method public int describeContents();
     method public boolean getNextEvent(android.app.usage.UsageEvents.Event);
@@ -6931,6 +6993,7 @@
     field public static final int SCAN_MODE_BALANCED = 1; // 0x1
     field public static final int SCAN_MODE_LOW_LATENCY = 2; // 0x2
     field public static final int SCAN_MODE_LOW_POWER = 0; // 0x0
+    field public static final int SCAN_MODE_OPPORTUNISTIC = -1; // 0xffffffff
   }
 
   public static final class ScanSettings.Builder {
@@ -7395,6 +7458,7 @@
     method public abstract int checkCallingPermission(java.lang.String);
     method public abstract int checkCallingUriPermission(android.net.Uri, int);
     method public abstract int checkPermission(java.lang.String, int, int);
+    method public abstract int checkSelfPermission(java.lang.String);
     method public abstract int checkUriPermission(android.net.Uri, int, int, int);
     method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public abstract deprecated void clearWallpaper() throws java.io.IOException;
@@ -7538,6 +7602,7 @@
     field public static final int MODE_PRIVATE = 0; // 0x0
     field public static final deprecated int MODE_WORLD_READABLE = 1; // 0x1
     field public static final deprecated int MODE_WORLD_WRITEABLE = 2; // 0x2
+    field public static final java.lang.String NETWORK_STATS_SERVICE = "netstats";
     field public static final java.lang.String NFC_SERVICE = "nfc";
     field public static final java.lang.String NOTIFICATION_SERVICE = "notification";
     field public static final java.lang.String NSD_SERVICE = "servicediscovery";
@@ -7572,6 +7637,7 @@
     method public int checkCallingPermission(java.lang.String);
     method public int checkCallingUriPermission(android.net.Uri, int);
     method public int checkPermission(java.lang.String, int, int);
+    method public int checkSelfPermission(java.lang.String);
     method public int checkUriPermission(android.net.Uri, int, int, int);
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public deprecated void clearWallpaper() throws java.io.IOException;
@@ -8192,6 +8258,8 @@
     field public static final int NO_MATCH_CATEGORY = -4; // 0xfffffffc
     field public static final int NO_MATCH_DATA = -2; // 0xfffffffe
     field public static final int NO_MATCH_TYPE = -1; // 0xffffffff
+    field public static final java.lang.String SCHEME_HTTP = "http";
+    field public static final java.lang.String SCHEME_HTTPS = "https";
     field public static final int SYSTEM_HIGH_PRIORITY = 1000; // 0x3e8
     field public static final int SYSTEM_LOW_PRIORITY = -1000; // 0xfffffc18
   }
@@ -8313,6 +8381,7 @@
     ctor public RestrictionEntry(java.lang.String, boolean);
     ctor public RestrictionEntry(java.lang.String, java.lang.String[]);
     ctor public RestrictionEntry(java.lang.String, int);
+    ctor public RestrictionEntry(java.lang.String, android.content.RestrictionEntry[], boolean);
     ctor public RestrictionEntry(android.os.Parcel);
     method public int describeContents();
     method public java.lang.String[] getAllSelectedStrings();
@@ -8321,6 +8390,7 @@
     method public java.lang.String getDescription();
     method public int getIntValue();
     method public java.lang.String getKey();
+    method public android.content.RestrictionEntry[] getRestrictions();
     method public boolean getSelectedState();
     method public java.lang.String getSelectedString();
     method public java.lang.String getTitle();
@@ -8332,6 +8402,7 @@
     method public void setChoiceValues(android.content.Context, int);
     method public void setDescription(java.lang.String);
     method public void setIntValue(int);
+    method public void setRestrictions(android.content.RestrictionEntry[]);
     method public void setSelectedState(boolean);
     method public void setSelectedString(java.lang.String);
     method public void setTitle(java.lang.String);
@@ -8339,6 +8410,8 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.RestrictionEntry> CREATOR;
     field public static final int TYPE_BOOLEAN = 1; // 0x1
+    field public static final int TYPE_BUNDLE = 7; // 0x7
+    field public static final int TYPE_BUNDLE_ARRAY = 8; // 0x8
     field public static final int TYPE_CHOICE = 2; // 0x2
     field public static final int TYPE_INTEGER = 5; // 0x5
     field public static final int TYPE_MULTI_SELECT = 4; // 0x4
@@ -8347,6 +8420,7 @@
   }
 
   public class RestrictionsManager {
+    method public static android.os.Bundle convertRestrictionsToBundle(java.util.List<android.content.RestrictionEntry>);
     method public android.content.Intent createLocalApprovalIntent();
     method public android.os.Bundle getApplicationRestrictions();
     method public java.util.List<android.content.RestrictionEntry> getManifestRestrictions(java.lang.String);
@@ -8645,6 +8719,7 @@
     field public static final int FLAG_ALLOW_TASK_REPARENTING = 32; // 0x20
     field public static final int FLAG_DEBUGGABLE = 2; // 0x2
     field public static final int FLAG_EXTERNAL_STORAGE = 262144; // 0x40000
+    field public static final int FLAG_EXTRACT_NATIVE_LIBS = 268435456; // 0x10000000
     field public static final int FLAG_FACTORY_TEST = 16; // 0x10
     field public static final int FLAG_FULL_BACKUP_ONLY = 67108864; // 0x4000000
     field public static final int FLAG_HAS_CODE = 4; // 0x4
@@ -8667,6 +8742,7 @@
     field public static final int FLAG_SYSTEM = 1; // 0x1
     field public static final int FLAG_TEST_ONLY = 256; // 0x100
     field public static final int FLAG_UPDATED_SYSTEM_APP = 128; // 0x80
+    field public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 134217728; // 0x8000000
     field public static final int FLAG_VM_SAFE_MODE = 16384; // 0x4000
     field public java.lang.String backupAgentName;
     field public java.lang.String className;
@@ -8769,6 +8845,25 @@
     field public java.lang.String targetPackage;
   }
 
+  public final class IntentFilterVerificationInfo implements android.os.Parcelable {
+    ctor public IntentFilterVerificationInfo();
+    ctor public IntentFilterVerificationInfo(java.lang.String, java.lang.String[]);
+    ctor public IntentFilterVerificationInfo(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    ctor public IntentFilterVerificationInfo(android.os.Parcel);
+    method public int describeContents();
+    method public java.lang.String[] getDomains();
+    method public java.lang.String getDomainsString();
+    method public java.lang.String getPackageName();
+    method public int getStatus();
+    method public java.lang.String getStatusString();
+    method public static java.lang.String getStatusStringFromValue(int);
+    method public void readFromXml(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void setStatus(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    method public void writeToXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
+    field public static final android.os.Parcelable.Creator<android.content.pm.IntentFilterVerificationInfo> CREATOR;
+  }
+
   public class LabeledIntent extends android.content.Intent {
     ctor public LabeledIntent(android.content.Intent, java.lang.String, int, int);
     ctor public LabeledIntent(android.content.Intent, java.lang.String, java.lang.CharSequence, int);
@@ -8824,7 +8919,6 @@
     field public static final int INSTALL_LOCATION_INTERNAL_ONLY = 1; // 0x1
     field public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2; // 0x2
     field public static final int REQUESTED_PERMISSION_GRANTED = 2; // 0x2
-    field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
     field public android.content.pm.ActivityInfo[] activities;
     field public android.content.pm.ApplicationInfo applicationInfo;
     field public int baseRevisionCode;
@@ -8998,6 +9092,7 @@
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
     method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public abstract java.lang.String getNameForUid(int);
@@ -9084,6 +9179,7 @@
     field public static final java.lang.String FEATURE_LOCATION_NETWORK = "android.hardware.location.network";
     field public static final java.lang.String FEATURE_MANAGED_USERS = "android.software.managed_users";
     field public static final java.lang.String FEATURE_MICROPHONE = "android.hardware.microphone";
+    field public static final java.lang.String FEATURE_MIDI = "android.software.midi";
     field public static final java.lang.String FEATURE_NFC = "android.hardware.nfc";
     field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce";
     field public static final java.lang.String FEATURE_OPENGLES_EXTENSION_PACK = "android.hardware.opengles.aep";
@@ -9353,11 +9449,10 @@
 
   public class ColorStateList implements android.os.Parcelable {
     ctor public ColorStateList(int[][], int[]);
-    method public void applyTheme(android.content.res.Resources.Theme);
-    method public boolean canApplyTheme();
     method public static deprecated android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public static android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public int describeContents();
+    method public int getChangingConfigurations();
     method public int getColorForState(int[], int);
     method public int getDefaultColor();
     method public boolean isOpaque();
@@ -11075,7 +11170,9 @@
     field public static final int JPEG = 256; // 0x100
     field public static final int NV16 = 16; // 0x10
     field public static final int NV21 = 17; // 0x11
+    field public static final int PRIVATE = 34; // 0x22
     field public static final int RAW10 = 37; // 0x25
+    field public static final int RAW12 = 38; // 0x26
     field public static final int RAW_SENSOR = 32; // 0x20
     field public static final int RGB_565 = 4; // 0x4
     field public static final int UNKNOWN = 0; // 0x0
@@ -11291,6 +11388,7 @@
     method public int getTextWidths(java.lang.String, float[]);
     method public android.graphics.Typeface getTypeface();
     method public android.graphics.Xfermode getXfermode();
+    method public boolean hasGlyph(java.lang.String);
     method public final boolean isAntiAlias();
     method public final boolean isDither();
     method public boolean isElegantTextHeight();
@@ -11871,9 +11969,12 @@
 
   public class AnimatedVectorDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Animatable {
     ctor public AnimatedVectorDrawable();
+    method public void addListener(android.animation.Animator.AnimatorListener);
     method public void draw(android.graphics.Canvas);
+    method public java.util.List<android.animation.Animator.AnimatorListener> getListeners();
     method public int getOpacity();
     method public boolean isRunning();
+    method public void removeListener(android.animation.Animator.AnimatorListener);
     method public void setAlpha(int);
     method public void setColorFilter(android.graphics.ColorFilter);
     method public void start();
@@ -14547,6 +14648,14 @@
     field public static final int SUCCESS = 0; // 0x0
   }
 
+  public static class AudioRecord.Builder {
+    ctor public AudioRecord.Builder();
+    method public android.media.AudioRecord build() throws java.lang.UnsupportedOperationException;
+    method public android.media.AudioRecord.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException;
+    method public android.media.AudioRecord.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException;
+    method public android.media.AudioRecord.Builder setCapturePreset(int) throws java.lang.IllegalArgumentException;
+  }
+
   public static abstract interface AudioRecord.OnRecordPositionUpdateListener {
     method public abstract void onMarkerReached(android.media.AudioRecord);
     method public abstract void onPeriodicNotification(android.media.AudioRecord);
@@ -15168,6 +15277,7 @@
     method public android.graphics.Bitmap getIconBitmap();
     method public android.net.Uri getIconUri();
     method public java.lang.String getMediaId();
+    method public android.net.Uri getMediaUri();
     method public java.lang.CharSequence getSubtitle();
     method public java.lang.CharSequence getTitle();
     method public void writeToParcel(android.os.Parcel, int);
@@ -15182,6 +15292,7 @@
     method public android.media.MediaDescription.Builder setIconBitmap(android.graphics.Bitmap);
     method public android.media.MediaDescription.Builder setIconUri(android.net.Uri);
     method public android.media.MediaDescription.Builder setMediaId(java.lang.String);
+    method public android.media.MediaDescription.Builder setMediaUri(android.net.Uri);
     method public android.media.MediaDescription.Builder setSubtitle(java.lang.CharSequence);
     method public android.media.MediaDescription.Builder setTitle(java.lang.CharSequence);
   }
@@ -15223,6 +15334,9 @@
     field public static final java.lang.String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
     field public static final java.lang.String PROPERTY_VENDOR = "vendor";
     field public static final java.lang.String PROPERTY_VERSION = "version";
+    field public static final int REQUEST_TYPE_INITIAL = 0; // 0x0
+    field public static final int REQUEST_TYPE_RELEASE = 2; // 0x2
+    field public static final int REQUEST_TYPE_RENEWAL = 1; // 0x1
   }
 
   public final class MediaDrm.CryptoSession {
@@ -15235,6 +15349,7 @@
   public static final class MediaDrm.KeyRequest {
     method public byte[] getData();
     method public java.lang.String getDefaultUrl();
+    method public int getRequestType();
   }
 
   public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException {
@@ -16995,6 +17110,7 @@
     method public void play();
     method public void playFromMediaId(java.lang.String, android.os.Bundle);
     method public void playFromSearch(java.lang.String, android.os.Bundle);
+    method public void playFromUri(android.net.Uri, android.os.Bundle);
     method public void rewind();
     method public void seekTo(long);
     method public void sendCustomAction(android.media.session.PlaybackState.CustomAction, android.os.Bundle);
@@ -17042,6 +17158,7 @@
     method public void onPlay();
     method public void onPlayFromMediaId(java.lang.String, android.os.Bundle);
     method public void onPlayFromSearch(java.lang.String, android.os.Bundle);
+    method public void onPlayFromUri(android.net.Uri, android.os.Bundle);
     method public void onRewind();
     method public void onSeekTo(long);
     method public void onSetRating(android.media.Rating);
@@ -17096,6 +17213,7 @@
     field public static final long ACTION_PLAY = 4L; // 0x4L
     field public static final long ACTION_PLAY_FROM_MEDIA_ID = 1024L; // 0x400L
     field public static final long ACTION_PLAY_FROM_SEARCH = 2048L; // 0x800L
+    field public static final long ACTION_PLAY_FROM_URI = 8192L; // 0x2000L
     field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L
     field public static final long ACTION_REWIND = 8L; // 0x8L
     field public static final long ACTION_SEEK_TO = 256L; // 0x100L
@@ -17266,6 +17384,10 @@
     field public static final java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
     field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
     field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
     field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
     field public static final java.lang.String COLUMN_SEASON_NUMBER = "season_number";
@@ -18547,7 +18669,10 @@
     field public int frequency;
     field public boolean is80211McRTTResponder;
     field public int level;
+    field public java.lang.String operatorFriendlyName;
+    field public boolean passpointNetwork;
     field public long timestamp;
+    field public java.lang.String venueName;
   }
 
   public final class SupplicantState extends java.lang.Enum implements android.os.Parcelable {
@@ -18574,6 +18699,7 @@
   public class WifiConfiguration implements android.os.Parcelable {
     ctor public WifiConfiguration();
     method public int describeContents();
+    method public boolean isPasspoint();
     method public void writeToParcel(android.os.Parcel, int);
     field public java.lang.String BSSID;
     field public java.lang.String FQDN;
@@ -18677,6 +18803,7 @@
 
   public static final class WifiEnterpriseConfig.Eap {
     field public static final int AKA = 5; // 0x5
+    field public static final int AKA_PRIME = 6; // 0x6
     field public static final int NONE = -1; // 0xffffffff
     field public static final int PEAP = 0; // 0x0
     field public static final int PWD = 3; // 0x3
@@ -22420,6 +22547,7 @@
     field public static java.lang.String DIRECTORY_RINGTONES;
     field public static final java.lang.String MEDIA_BAD_REMOVAL = "bad_removal";
     field public static final java.lang.String MEDIA_CHECKING = "checking";
+    field public static final java.lang.String MEDIA_EJECTING = "ejecting";
     field public static final java.lang.String MEDIA_MOUNTED = "mounted";
     field public static final java.lang.String MEDIA_MOUNTED_READ_ONLY = "mounted_ro";
     field public static final java.lang.String MEDIA_NOFS = "nofs";
@@ -22851,6 +22979,7 @@
   }
 
   public final class PowerManager {
+    method public boolean isDeviceIdleMode();
     method public boolean isInteractive();
     method public boolean isPowerSaveMode();
     method public deprecated boolean isScreenOn();
@@ -22858,6 +22987,7 @@
     method public android.os.PowerManager.WakeLock newWakeLock(int, java.lang.String);
     method public void reboot(java.lang.String);
     field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
+    field public static final java.lang.String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
     field public static final java.lang.String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED";
     field public static final deprecated int FULL_WAKE_LOCK = 26; // 0x1a
     field public static final int ON_AFTER_RELEASE = 536870912; // 0x20000000
@@ -26664,6 +26794,16 @@
     method public void copy1DRangeFromUnchecked(int, int, short[]);
     method public void copy1DRangeFromUnchecked(int, int, byte[]);
     method public void copy1DRangeFromUnchecked(int, int, float[]);
+    method public void copy1DRangeTo(int, int, java.lang.Object);
+    method public void copy1DRangeTo(int, int, int[]);
+    method public void copy1DRangeTo(int, int, short[]);
+    method public void copy1DRangeTo(int, int, byte[]);
+    method public void copy1DRangeTo(int, int, float[]);
+    method public void copy1DRangeToUnchecked(int, int, java.lang.Object);
+    method public void copy1DRangeToUnchecked(int, int, int[]);
+    method public void copy1DRangeToUnchecked(int, int, short[]);
+    method public void copy1DRangeToUnchecked(int, int, byte[]);
+    method public void copy1DRangeToUnchecked(int, int, float[]);
     method public void copy2DRangeFrom(int, int, int, int, java.lang.Object);
     method public void copy2DRangeFrom(int, int, int, int, byte[]);
     method public void copy2DRangeFrom(int, int, int, int, short[]);
@@ -26671,6 +26811,14 @@
     method public void copy2DRangeFrom(int, int, int, int, float[]);
     method public void copy2DRangeFrom(int, int, int, int, android.renderscript.Allocation, int, int);
     method public void copy2DRangeFrom(int, int, android.graphics.Bitmap);
+    method public void copy2DRangeTo(int, int, int, int, java.lang.Object);
+    method public void copy2DRangeTo(int, int, int, int, byte[]);
+    method public void copy2DRangeTo(int, int, int, int, short[]);
+    method public void copy2DRangeTo(int, int, int, int, int[]);
+    method public void copy2DRangeTo(int, int, int, int, float[]);
+    method public void copy3DRangeFrom(int, int, int, int, int, int, java.lang.Object);
+    method public void copy3DRangeFrom(int, int, int, int, int, int, android.renderscript.Allocation, int, int, int);
+    method public void copy3DRangeTo(int, int, int, int, int, int, java.lang.Object);
     method public void copyFrom(android.renderscript.BaseObj[]);
     method public void copyFrom(java.lang.Object);
     method public void copyFrom(int[]);
@@ -26690,6 +26838,7 @@
     method public void copyTo(short[]);
     method public void copyTo(int[]);
     method public void copyTo(float[]);
+    method public void copyToFieldPacker(int, int, int, int, android.renderscript.FieldPacker);
     method public static android.renderscript.Allocation createCubemapFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap, android.renderscript.Allocation.MipmapControl, int);
     method public static android.renderscript.Allocation createCubemapFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap);
     method public static android.renderscript.Allocation createCubemapFromCubeFaces(android.renderscript.RenderScript, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.renderscript.Allocation.MipmapControl, int);
@@ -26713,8 +26862,10 @@
     method public void ioReceive();
     method public void ioSend();
     method public deprecated synchronized void resize(int);
+    method public void setAutoPadding(boolean);
     method public void setFromFieldPacker(int, android.renderscript.FieldPacker);
     method public void setFromFieldPacker(int, int, android.renderscript.FieldPacker);
+    method public void setFromFieldPacker(int, int, int, int, android.renderscript.FieldPacker);
     method public void setOnBufferAvailableListener(android.renderscript.Allocation.OnBufferAvailableListener);
     method public void setSurface(android.view.Surface);
     method public void syncAll(int);
@@ -26811,6 +26962,10 @@
     method public static android.renderscript.Element A_8(android.renderscript.RenderScript);
     method public static android.renderscript.Element BOOLEAN(android.renderscript.RenderScript);
     method public static android.renderscript.Element ELEMENT(android.renderscript.RenderScript);
+    method public static android.renderscript.Element F16(android.renderscript.RenderScript);
+    method public static android.renderscript.Element F16_2(android.renderscript.RenderScript);
+    method public static android.renderscript.Element F16_3(android.renderscript.RenderScript);
+    method public static android.renderscript.Element F16_4(android.renderscript.RenderScript);
     method public static android.renderscript.Element F32(android.renderscript.RenderScript);
     method public static android.renderscript.Element F32_2(android.renderscript.RenderScript);
     method public static android.renderscript.Element F32_3(android.renderscript.RenderScript);
@@ -26909,6 +27064,7 @@
     method public static android.renderscript.Element.DataType valueOf(java.lang.String);
     method public static final android.renderscript.Element.DataType[] values();
     enum_constant public static final android.renderscript.Element.DataType BOOLEAN;
+    enum_constant public static final android.renderscript.Element.DataType FLOAT_16;
     enum_constant public static final android.renderscript.Element.DataType FLOAT_32;
     enum_constant public static final android.renderscript.Element.DataType FLOAT_64;
     enum_constant public static final android.renderscript.Element.DataType MATRIX_2X2;
@@ -27182,11 +27338,14 @@
     method public static android.renderscript.RenderScript create(android.content.Context);
     method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType);
     method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType, int);
+    method public static android.renderscript.RenderScript createMultiContext(android.content.Context, android.renderscript.RenderScript.ContextType, int, int);
     method public void destroy();
     method public void finish();
     method public final android.content.Context getApplicationContext();
     method public android.renderscript.RenderScript.RSErrorHandler getErrorHandler();
     method public android.renderscript.RenderScript.RSMessageHandler getMessageHandler();
+    method public static long getMinorID();
+    method public static void releaseAllContexts();
     method public void sendMessage(int, int[]);
     method public void setErrorHandler(android.renderscript.RenderScript.RSErrorHandler);
     method public void setMessageHandler(android.renderscript.RenderScript.RSMessageHandler);
@@ -27268,9 +27427,12 @@
   public class Script extends android.renderscript.BaseObj {
     method public void bindAllocation(android.renderscript.Allocation, int);
     method protected android.renderscript.Script.FieldID createFieldID(int, android.renderscript.Element);
+    method protected android.renderscript.Script.InvokeID createInvokeID(int);
     method protected android.renderscript.Script.KernelID createKernelID(int, int, android.renderscript.Element, android.renderscript.Element);
     method protected void forEach(int, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.FieldPacker);
     method protected void forEach(int, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.FieldPacker, android.renderscript.Script.LaunchOptions);
+    method protected void forEach(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.FieldPacker);
+    method protected void forEach(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.FieldPacker, android.renderscript.Script.LaunchOptions);
     method public boolean getVarB(int);
     method public double getVarD(int);
     method public float getVarF(int);
@@ -27308,6 +27470,9 @@
   public static final class Script.FieldID extends android.renderscript.BaseObj {
   }
 
+  public static final class Script.InvokeID extends android.renderscript.BaseObj {
+  }
+
   public static final class Script.KernelID extends android.renderscript.BaseObj {
   }
 
@@ -27345,6 +27510,41 @@
     method public android.renderscript.ScriptGroup create();
   }
 
+  public class ScriptGroup2 extends android.renderscript.BaseObj {
+    ctor public ScriptGroup2(long, android.renderscript.RenderScript);
+    method public java.lang.Object[] execute(java.lang.Object...);
+  }
+
+  public static final class ScriptGroup2.Binding {
+    ctor public ScriptGroup2.Binding(android.renderscript.Script.FieldID, java.lang.Object);
+    method public android.renderscript.Script.FieldID getField();
+    method public java.lang.Object getValue();
+  }
+
+  public static final class ScriptGroup2.Builder {
+    ctor public ScriptGroup2.Builder(android.renderscript.RenderScript);
+    method public android.renderscript.ScriptGroup2.UnboundValue addInput();
+    method public android.renderscript.ScriptGroup2.Closure addInvoke(android.renderscript.Script.InvokeID, java.lang.Object[], java.util.Map<android.renderscript.Script.FieldID, java.lang.Object>);
+    method public android.renderscript.ScriptGroup2.Closure addInvoke(android.renderscript.Script.InvokeID, java.lang.Object...);
+    method public android.renderscript.ScriptGroup2.Closure addKernel(android.renderscript.Script.KernelID, android.renderscript.Type, java.lang.Object[], java.util.Map<android.renderscript.Script.FieldID, java.lang.Object>);
+    method public android.renderscript.ScriptGroup2.Closure addKernel(android.renderscript.Script.KernelID, android.renderscript.Type, java.lang.Object...);
+    method public android.renderscript.ScriptGroup2 create(android.renderscript.ScriptGroup2.Future...);
+  }
+
+  public static class ScriptGroup2.Closure extends android.renderscript.BaseObj {
+    ctor public ScriptGroup2.Closure(long, android.renderscript.RenderScript);
+    ctor public ScriptGroup2.Closure(android.renderscript.RenderScript, android.renderscript.Script.KernelID, android.renderscript.Type, java.lang.Object[], java.util.Map<android.renderscript.Script.FieldID, java.lang.Object>);
+    ctor public ScriptGroup2.Closure(android.renderscript.RenderScript, android.renderscript.Script.InvokeID, java.lang.Object[], java.util.Map<android.renderscript.Script.FieldID, java.lang.Object>);
+    method public android.renderscript.ScriptGroup2.Future getGlobal(android.renderscript.Script.FieldID);
+    method public android.renderscript.ScriptGroup2.Future getReturn();
+  }
+
+  public static class ScriptGroup2.Future {
+  }
+
+  public static class ScriptGroup2.UnboundValue {
+  }
+
   public abstract class ScriptIntrinsic extends android.renderscript.Script {
   }
 
@@ -27518,6 +27718,8 @@
     method public static android.renderscript.Type createX(android.renderscript.RenderScript, android.renderscript.Element, int);
     method public static android.renderscript.Type createXY(android.renderscript.RenderScript, android.renderscript.Element, int, int);
     method public static android.renderscript.Type createXYZ(android.renderscript.RenderScript, android.renderscript.Element, int, int, int);
+    method public int getArray(int);
+    method public int getArrayCount();
     method public int getCount();
     method public android.renderscript.Element getElement();
     method public int getX();
@@ -27531,6 +27733,7 @@
   public static class Type.Builder {
     ctor public Type.Builder(android.renderscript.RenderScript, android.renderscript.Element);
     method public android.renderscript.Type create();
+    method public android.renderscript.Type.Builder setArray(int, int);
     method public android.renderscript.Type.Builder setFaces(boolean);
     method public android.renderscript.Type.Builder setMipmaps(boolean);
     method public android.renderscript.Type.Builder setX(int);
@@ -27660,6 +27863,11 @@
     method public android.security.KeyStoreParameter.Builder setEncryptionRequired(boolean);
   }
 
+  public class NetworkSecurityPolicy {
+    method public static android.security.NetworkSecurityPolicy getInstance();
+    method public boolean isCleartextTrafficPermitted();
+  }
+
 }
 
 package android.service.carrier {
@@ -29678,6 +29886,7 @@
     method public boolean setOperatorBrandOverride(java.lang.String);
     method public boolean setPreferredNetworkTypeToGlobal();
     method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
+    field public static final java.lang.String ACTION_EMERGENCY_ASSISTANCE = "android.telephony.action.EMERGENCY_ASSISTANCE";
     field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
     field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
     field public static final int CALL_STATE_IDLE = 0; // 0x0
@@ -30174,6 +30383,7 @@
     method public int checkCallingPermission(java.lang.String);
     method public int checkCallingUriPermission(android.net.Uri, int);
     method public int checkPermission(java.lang.String, int, int);
+    method public int checkSelfPermission(java.lang.String);
     method public int checkUriPermission(android.net.Uri, int, int, int);
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public void clearWallpaper();
@@ -30348,6 +30558,7 @@
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
     method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public java.lang.String getNameForUid(int);
@@ -30712,6 +30923,9 @@
     method public final void increaseWidthTo(int);
     method public boolean isRtlCharAt(int);
     method protected final boolean isSpanned();
+    field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
+    field public static final int BREAK_STRATEGY_HIGH_QUALITY = 1; // 0x1
+    field public static final int BREAK_STRATEGY_SIMPLE = 0; // 0x0
     field public static final int DIR_LEFT_TO_RIGHT = 1; // 0x1
     field public static final int DIR_RIGHT_TO_LEFT = -1; // 0xffffffff
   }
@@ -34126,8 +34340,10 @@
     method public long getTimeDelta();
     method public boolean isInProgress();
     method public boolean isQuickScaleEnabled();
+    method public boolean isSecondaryButtonScaleEnabled();
     method public boolean onTouchEvent(android.view.MotionEvent);
     method public void setQuickScaleEnabled(boolean);
+    method public void setSecondaryButtonScaleEnabled(boolean);
   }
 
   public static abstract interface ScaleGestureDetector.OnScaleGestureListener {
@@ -34348,6 +34564,7 @@
     method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
     method public boolean dispatchNestedScroll(int, int, int, int, int[]);
     method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+    method public void dispatchProvideAssistStructure(android.view.ViewAssistStructure);
     method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>);
     method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>);
     method protected void dispatchSetActivated(boolean);
@@ -34408,6 +34625,10 @@
     method public boolean getFitsSystemWindows();
     method public java.util.ArrayList<android.view.View> getFocusables(int);
     method public void getFocusedRect(android.graphics.Rect);
+    method public android.graphics.drawable.Drawable getForeground();
+    method public int getForegroundGravity();
+    method public android.content.res.ColorStateList getForegroundTintList();
+    method public android.graphics.PorterDuff.Mode getForegroundTintMode();
     method public boolean getGlobalVisibleRect(android.graphics.Rect, android.graphics.Point);
     method public final boolean getGlobalVisibleRect(android.graphics.Rect);
     method public android.os.Handler getHandler();
@@ -34579,6 +34800,7 @@
     method protected void onDisplayHint(int);
     method public boolean onDragEvent(android.view.DragEvent);
     method protected void onDraw(android.graphics.Canvas);
+    method public void onDrawForeground(android.graphics.Canvas);
     method protected final void onDrawScrollBars(android.graphics.Canvas);
     method public boolean onFilterTouchEventForSecurity(android.view.MotionEvent);
     method protected void onFinishInflate();
@@ -34599,7 +34821,8 @@
     method protected void onMeasure(int, int);
     method protected void onOverScrolled(int, int, boolean, boolean);
     method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
-    method public void onProvideAssistStructure(android.view.ViewAssistStructure, android.os.Bundle);
+    method public void onProvideAssistStructure(android.view.ViewAssistStructure);
+    method public void onProvideVirtualAssistStructure(android.view.ViewAssistStructure);
     method protected void onRestoreInstanceState(android.os.Parcelable);
     method public void onRtlPropertiesChanged(int);
     method protected android.os.Parcelable onSaveInstanceState();
@@ -34684,6 +34907,10 @@
     method public void setFitsSystemWindows(boolean);
     method public void setFocusable(boolean);
     method public void setFocusableInTouchMode(boolean);
+    method public void setForeground(android.graphics.drawable.Drawable);
+    method public void setForegroundGravity(int);
+    method public void setForegroundTintList(android.content.res.ColorStateList);
+    method public void setForegroundTintMode(android.graphics.PorterDuff.Mode);
     method public void setHapticFeedbackEnabled(boolean);
     method public void setHasTransientState(boolean);
     method public void setHorizontalFadingEdgeEnabled(boolean);
@@ -35005,14 +35232,34 @@
 
   public abstract class ViewAssistStructure {
     ctor public ViewAssistStructure();
+    method public abstract void clearExtras();
+    method public abstract android.os.Bundle editExtras();
+    method public abstract int getChildCount();
     method public abstract java.lang.CharSequence getHint();
     method public abstract java.lang.CharSequence getText();
     method public abstract int getTextSelectionEnd();
     method public abstract int getTextSelectionStart();
+    method public abstract android.view.ViewAssistStructure newChild(int);
+    method public abstract void setAccessibilityFocused(boolean);
+    method public abstract void setActivated(boolean);
+    method public abstract void setCheckable(boolean);
+    method public abstract void setChecked(boolean);
+    method public abstract void setChildCount(int);
+    method public abstract void setClassName(java.lang.String);
+    method public abstract void setClickable(boolean);
+    method public abstract void setContentDescription(java.lang.CharSequence);
+    method public abstract void setDimens(int, int, int, int, int, int);
+    method public abstract void setEnabled(boolean);
+    method public abstract void setFocusable(boolean);
+    method public abstract void setFocused(boolean);
     method public abstract void setHint(java.lang.CharSequence);
+    method public abstract void setId(int, java.lang.String, java.lang.String, java.lang.String);
+    method public abstract void setLongClickable(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);
     method public abstract void setTextPaint(android.text.TextPaint);
+    method public abstract void setVisibility(int);
   }
 
   public class ViewConfiguration {
@@ -36105,6 +36352,7 @@
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SELECT;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_SELECTION;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_TEXT;
+    field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SHOW_ON_SCREEN;
   }
 
   public static final class AccessibilityNodeInfo.CollectionInfo {
@@ -37646,7 +37894,6 @@
     method public boolean shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String);
     field public static final int ERROR_AUTHENTICATION = -4; // 0xfffffffc
     field public static final int ERROR_BAD_URL = -12; // 0xfffffff4
-    field public static final int ERROR_BLOCKED = -16; // 0xfffffff0
     field public static final int ERROR_CONNECT = -6; // 0xfffffffa
     field public static final int ERROR_FAILED_SSL_HANDSHAKE = -11; // 0xfffffff5
     field public static final int ERROR_FILE = -13; // 0xfffffff3
@@ -38009,7 +38256,7 @@
     field protected android.database.Cursor mDataCursor;
   }
 
-  public class AnalogClock extends android.view.View {
+  public deprecated class AnalogClock extends android.view.View {
     ctor public AnalogClock(android.content.Context);
     ctor public AnalogClock(android.content.Context, android.util.AttributeSet);
     ctor public AnalogClock(android.content.Context, android.util.AttributeSet, int);
@@ -38152,34 +38399,34 @@
     method public long getDate();
     method public int getDateTextAppearance();
     method public int getFirstDayOfWeek();
-    method public int getFocusedMonthDateColor();
+    method public deprecated int getFocusedMonthDateColor();
     method public long getMaxDate();
     method public long getMinDate();
-    method public android.graphics.drawable.Drawable getSelectedDateVerticalBar();
-    method public int getSelectedWeekBackgroundColor();
+    method public deprecated android.graphics.drawable.Drawable getSelectedDateVerticalBar();
+    method public deprecated int getSelectedWeekBackgroundColor();
     method public boolean getShowWeekNumber();
-    method public int getShownWeekCount();
-    method public int getUnfocusedMonthDateColor();
+    method public deprecated int getShownWeekCount();
+    method public deprecated int getUnfocusedMonthDateColor();
     method public int getWeekDayTextAppearance();
-    method public int getWeekNumberColor();
-    method public int getWeekSeparatorLineColor();
+    method public deprecated int getWeekNumberColor();
+    method public deprecated int getWeekSeparatorLineColor();
     method public void setDate(long);
     method public void setDate(long, boolean, boolean);
     method public void setDateTextAppearance(int);
     method public void setFirstDayOfWeek(int);
-    method public void setFocusedMonthDateColor(int);
+    method public deprecated void setFocusedMonthDateColor(int);
     method public void setMaxDate(long);
     method public void setMinDate(long);
     method public void setOnDateChangeListener(android.widget.CalendarView.OnDateChangeListener);
-    method public void setSelectedDateVerticalBar(int);
-    method public void setSelectedDateVerticalBar(android.graphics.drawable.Drawable);
-    method public void setSelectedWeekBackgroundColor(int);
+    method public deprecated void setSelectedDateVerticalBar(int);
+    method public deprecated void setSelectedDateVerticalBar(android.graphics.drawable.Drawable);
+    method public deprecated void setSelectedWeekBackgroundColor(int);
     method public void setShowWeekNumber(boolean);
-    method public void setShownWeekCount(int);
-    method public void setUnfocusedMonthDateColor(int);
+    method public deprecated void setShownWeekCount(int);
+    method public deprecated void setUnfocusedMonthDateColor(int);
     method public void setWeekDayTextAppearance(int);
-    method public void setWeekNumberColor(int);
-    method public void setWeekSeparatorLineColor(int);
+    method public deprecated void setWeekNumberColor(int);
+    method public deprecated void setWeekSeparatorLineColor(int);
   }
 
   public static abstract interface CalendarView.OnDateChangeListener {
@@ -38512,16 +38759,8 @@
     ctor public FrameLayout(android.content.Context, android.util.AttributeSet, int);
     ctor public FrameLayout(android.content.Context, android.util.AttributeSet, int, int);
     method public deprecated boolean getConsiderGoneChildrenWhenMeasuring();
-    method public android.graphics.drawable.Drawable getForeground();
-    method public int getForegroundGravity();
-    method public android.content.res.ColorStateList getForegroundTintList();
-    method public android.graphics.PorterDuff.Mode getForegroundTintMode();
     method public boolean getMeasureAllChildren();
     method protected void onLayout(boolean, int, int, int, int);
-    method public void setForeground(android.graphics.drawable.Drawable);
-    method public void setForegroundGravity(int);
-    method public void setForegroundTintList(android.content.res.ColorStateList);
-    method public void setForegroundTintMode(android.graphics.PorterDuff.Mode);
     method public void setMeasureAllChildren(boolean);
   }
 
@@ -39112,7 +39351,7 @@
     method public void setTouchInterceptor(android.view.View.OnTouchListener);
     method public void setTouchable(boolean);
     method public void setWidth(int);
-    method public void setWindowLayoutMode(int, int);
+    method public deprecated void setWindowLayoutMode(int, int);
     method public void showAsDropDown(android.view.View);
     method public void showAsDropDown(android.view.View, int, int);
     method public void showAsDropDown(android.view.View, int, int, int);
@@ -39871,6 +40110,7 @@
     method public void endBatchEdit();
     method public boolean extractText(android.view.inputmethod.ExtractedTextRequest, android.view.inputmethod.ExtractedText);
     method public final int getAutoLinkMask();
+    method public int getBreakStrategy();
     method public int getCompoundDrawablePadding();
     method public android.content.res.ColorStateList getCompoundDrawableTintList();
     method public android.graphics.PorterDuff.Mode getCompoundDrawableTintMode();
@@ -39972,6 +40212,7 @@
     method public void removeTextChangedListener(android.text.TextWatcher);
     method public void setAllCaps(boolean);
     method public final void setAutoLinkMask(int);
+    method public void setBreakStrategy(int);
     method public void setCompoundDrawablePadding(int);
     method public void setCompoundDrawableTintList(android.content.res.ColorStateList);
     method public void setCompoundDrawableTintMode(android.graphics.PorterDuff.Mode);
@@ -40078,12 +40319,16 @@
     ctor public TimePicker(android.content.Context, android.util.AttributeSet);
     ctor public TimePicker(android.content.Context, android.util.AttributeSet, int);
     ctor public TimePicker(android.content.Context, android.util.AttributeSet, int, int);
-    method public java.lang.Integer getCurrentHour();
-    method public java.lang.Integer getCurrentMinute();
+    method public deprecated java.lang.Integer getCurrentHour();
+    method public deprecated java.lang.Integer getCurrentMinute();
+    method public int getHour();
+    method public int getMinute();
     method public boolean is24HourView();
-    method public void setCurrentHour(java.lang.Integer);
-    method public void setCurrentMinute(java.lang.Integer);
+    method public deprecated void setCurrentHour(java.lang.Integer);
+    method public deprecated void setCurrentMinute(java.lang.Integer);
+    method public void setHour(int);
     method public void setIs24HourView(java.lang.Boolean);
+    method public void setMinute(int);
     method public void setOnTimeChangedListener(android.widget.TimePicker.OnTimeChangedListener);
   }
 
diff --git a/api/removed.txt b/api/removed.txt
index 1b209a9..c2b9d3e 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -1,3 +1,11 @@
+package android.content.pm {
+
+  public class PackageInfo implements android.os.Parcelable {
+    field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
+  }
+
+}
+
 package android.media {
 
   public class AudioFormat {
diff --git a/api/system-current.txt b/api/system-current.txt
index aa20ff1..40accaa 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -103,6 +103,7 @@
     field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER";
     field public static final java.lang.String INSTALL_PACKAGES = "android.permission.INSTALL_PACKAGES";
     field public static final java.lang.String INSTALL_SHORTCUT = "com.android.launcher.permission.INSTALL_SHORTCUT";
+    field public static final java.lang.String INTENT_FILTER_VERIFICATION_AGENT = "android.permission.INTENT_FILTER_VERIFICATION_AGENT";
     field public static final java.lang.String INTERACT_ACROSS_USERS = "android.permission.INTERACT_ACROSS_USERS";
     field public static final java.lang.String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW";
     field public static final java.lang.String INTERNET = "android.permission.INTERNET";
@@ -134,6 +135,7 @@
     field public static final java.lang.String PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS";
     field public static final java.lang.String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT";
     field public static final java.lang.String PERFORM_CDMA_PROVISIONING = "android.permission.PERFORM_CDMA_PROVISIONING";
+    field public static final java.lang.String PERFORM_SIM_ACTIVATION = "android.permission.PERFORM_SIM_ACTIVATION";
     field public static final deprecated java.lang.String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY";
     field public static final java.lang.String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS";
     field public static final java.lang.String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT";
@@ -375,8 +377,8 @@
     field public static final int alphabeticShortcut = 16843235; // 0x10101e3
     field public static final int alwaysDrawnWithCache = 16842991; // 0x10100ef
     field public static final int alwaysRetainTaskState = 16843267; // 0x1010203
-    field public static final int amPmBackgroundColor = 16843941; // 0x10104a5
-    field public static final int amPmTextColor = 16843940; // 0x10104a4
+    field public static final deprecated int amPmBackgroundColor = 16843941; // 0x10104a5
+    field public static final deprecated int amPmTextColor = 16843940; // 0x10104a4
     field public static final int ambientShadowAlpha = 16843966; // 0x10104be
     field public static final int angle = 16843168; // 0x10101a0
     field public static final int animateFirstView = 16843477; // 0x10102d5
@@ -401,6 +403,7 @@
     field public static final int autoStart = 16843445; // 0x10102b5
     field public static final deprecated int autoText = 16843114; // 0x101016a
     field public static final int autoUrlDetect = 16843404; // 0x101028c
+    field public static final int autoVerify = 16844010; // 0x10104ea
     field public static final int background = 16842964; // 0x10100d4
     field public static final int backgroundDimAmount = 16842802; // 0x1010032
     field public static final int backgroundDimEnabled = 16843295; // 0x101021f
@@ -424,6 +427,7 @@
     field public static final int bottomRightRadius = 16843180; // 0x10101ac
     field public static final int breadCrumbShortTitle = 16843524; // 0x1010304
     field public static final int breadCrumbTitle = 16843523; // 0x1010303
+    field public static final int breakStrategy = 16844011; // 0x10104eb
     field public static final int bufferType = 16843086; // 0x101014e
     field public static final int button = 16843015; // 0x1010107
     field public static final int buttonBarButtonStyle = 16843567; // 0x101032f
@@ -536,8 +540,8 @@
     field public static final int datePickerMode = 16843955; // 0x10104b3
     field public static final int datePickerStyle = 16843612; // 0x101035c
     field public static final int dateTextAppearance = 16843593; // 0x1010349
-    field public static final int dayOfWeekBackground = 16843924; // 0x1010494
-    field public static final int dayOfWeekTextAppearance = 16843925; // 0x1010495
+    field public static final deprecated int dayOfWeekBackground = 16843924; // 0x1010494
+    field public static final deprecated int dayOfWeekTextAppearance = 16843925; // 0x1010495
     field public static final int debuggable = 16842767; // 0x101000f
     field public static final int defaultValue = 16843245; // 0x10101ed
     field public static final int delay = 16843212; // 0x10101cc
@@ -627,6 +631,7 @@
     field public static final int expandableListViewWhiteStyle = 16843446; // 0x10102b6
     field public static final int exported = 16842768; // 0x1010010
     field public static final int extraTension = 16843371; // 0x101026b
+    field public static final int extractNativeLibs = 16844008; // 0x10104e8
     field public static final int factor = 16843219; // 0x10101d3
     field public static final int fadeDuration = 16843384; // 0x1010278
     field public static final int fadeEnabled = 16843390; // 0x101027e
@@ -659,7 +664,7 @@
     field public static final int flipInterval = 16843129; // 0x1010179
     field public static final int focusable = 16842970; // 0x10100da
     field public static final int focusableInTouchMode = 16842971; // 0x10100db
-    field public static final int focusedMonthDateColor = 16843587; // 0x1010343
+    field public static final deprecated int focusedMonthDateColor = 16843587; // 0x1010343
     field public static final int fontFamily = 16843692; // 0x10103ac
     field public static final int fontFeatureSettings = 16843959; // 0x10104b7
     field public static final int footerDividersEnabled = 16843311; // 0x101022f
@@ -721,13 +726,13 @@
     field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
     field public static final int hardwareAccelerated = 16843475; // 0x10102d3
     field public static final int hasCode = 16842764; // 0x101000c
-    field public static final int headerAmPmTextAppearance = 16843936; // 0x10104a0
+    field public static final deprecated int headerAmPmTextAppearance = 16843936; // 0x10104a0
     field public static final int headerBackground = 16843055; // 0x101012f
-    field public static final int headerDayOfMonthTextAppearance = 16843927; // 0x1010497
+    field public static final deprecated int headerDayOfMonthTextAppearance = 16843927; // 0x1010497
     field public static final int headerDividersEnabled = 16843310; // 0x101022e
-    field public static final int headerMonthTextAppearance = 16843926; // 0x1010496
-    field public static final int headerTimeTextAppearance = 16843935; // 0x101049f
-    field public static final int headerYearTextAppearance = 16843928; // 0x1010498
+    field public static final deprecated int headerMonthTextAppearance = 16843926; // 0x1010496
+    field public static final deprecated int headerTimeTextAppearance = 16843935; // 0x101049f
+    field public static final deprecated int headerYearTextAppearance = 16843928; // 0x1010498
     field public static final int height = 16843093; // 0x1010155
     field public static final int hideOnContentScroll = 16843843; // 0x1010443
     field public static final int hint = 16843088; // 0x1010150
@@ -1170,8 +1175,8 @@
     field public static final int selectable = 16843238; // 0x10101e6
     field public static final int selectableItemBackground = 16843534; // 0x101030e
     field public static final int selectableItemBackgroundBorderless = 16843868; // 0x101045c
-    field public static final int selectedDateVerticalBar = 16843591; // 0x1010347
-    field public static final int selectedWeekBackgroundColor = 16843586; // 0x1010342
+    field public static final deprecated int selectedDateVerticalBar = 16843591; // 0x1010347
+    field public static final deprecated int selectedWeekBackgroundColor = 16843586; // 0x1010342
     field public static final int sessionService = 16843837; // 0x101043d
     field public static final int settingsActivity = 16843301; // 0x1010225
     field public static final int setupActivity = 16843766; // 0x10103f6
@@ -1190,8 +1195,8 @@
     field public static final int showOnLockScreen = 16843721; // 0x10103c9
     field public static final int showSilent = 16843259; // 0x10101fb
     field public static final int showText = 16843949; // 0x10104ad
-    field public static final int showWeekNumber = 16843582; // 0x101033e
-    field public static final int shownWeekCount = 16843585; // 0x1010341
+    field public static final deprecated int showWeekNumber = 16843582; // 0x101033e
+    field public static final deprecated int shownWeekCount = 16843585; // 0x1010341
     field public static final int shrinkColumns = 16843082; // 0x101014a
     field public static final deprecated int singleLine = 16843101; // 0x101015d
     field public static final int singleUser = 16843711; // 0x10103bf
@@ -1428,13 +1433,14 @@
     field public static final int typeface = 16842902; // 0x1010096
     field public static final int uiOptions = 16843672; // 0x1010398
     field public static final int uncertainGestureColor = 16843382; // 0x1010276
-    field public static final int unfocusedMonthDateColor = 16843588; // 0x1010344
+    field public static final deprecated int unfocusedMonthDateColor = 16843588; // 0x1010344
     field public static final int unselectedAlpha = 16843278; // 0x101020e
     field public static final int updatePeriodMillis = 16843344; // 0x1010250
     field public static final int useDefaultMargins = 16843641; // 0x1010379
     field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
     field public static final int useLevel = 16843167; // 0x101019f
     field public static final int userVisible = 16843409; // 0x1010291
+    field public static final int usesCleartextTraffic = 16844009; // 0x10104e9
     field public static final int value = 16842788; // 0x1010024
     field public static final int valueFrom = 16843486; // 0x10102de
     field public static final int valueTo = 16843487; // 0x10102df
@@ -1470,8 +1476,8 @@
     field public static final int webTextViewStyle = 16843449; // 0x10102b9
     field public static final int webViewStyle = 16842885; // 0x1010085
     field public static final int weekDayTextAppearance = 16843592; // 0x1010348
-    field public static final int weekNumberColor = 16843589; // 0x1010345
-    field public static final int weekSeparatorLineColor = 16843590; // 0x1010346
+    field public static final deprecated int weekNumberColor = 16843589; // 0x1010345
+    field public static final deprecated int weekSeparatorLineColor = 16843590; // 0x1010346
     field public static final int weightSum = 16843048; // 0x1010128
     field public static final int widgetCategory = 16843716; // 0x10103c4
     field public static final int widgetLayout = 16843243; // 0x10101eb
@@ -1529,8 +1535,8 @@
     field public static final int x = 16842924; // 0x10100ac
     field public static final int xlargeScreens = 16843455; // 0x10102bf
     field public static final int y = 16842925; // 0x10100ad
-    field public static final int yearListItemTextAppearance = 16843929; // 0x1010499
-    field public static final int yearListSelectorColor = 16843930; // 0x101049a
+    field public static final deprecated int yearListItemTextAppearance = 16843929; // 0x1010499
+    field public static final deprecated int yearListSelectorColor = 16843930; // 0x101049a
     field public static final int yesNoPreferenceStyle = 16842896; // 0x1010090
     field public static final int zAdjustment = 16843201; // 0x10101c1
   }
@@ -2811,6 +2817,7 @@
   }
 
   public class AccountManager {
+    method public boolean accountAuthenticated(android.accounts.Account);
     method public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
     method public boolean addAccountExplicitly(android.accounts.Account, java.lang.String, android.os.Bundle);
     method public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean);
@@ -2871,6 +2878,7 @@
     field public static final java.lang.String KEY_ERROR_CODE = "errorCode";
     field public static final java.lang.String KEY_ERROR_MESSAGE = "errorMessage";
     field public static final java.lang.String KEY_INTENT = "intent";
+    field public static final java.lang.String KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH = "lastAuthenticatedTimeMillisEpoch";
     field public static final java.lang.String KEY_PASSWORD = "password";
     field public static final java.lang.String KEY_USERDATA = "userdata";
     field public static final java.lang.String LOGIN_ACCOUNTS_CHANGED_ACTION = "android.accounts.LOGIN_ACCOUNTS_CHANGED";
@@ -3514,6 +3522,7 @@
     method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
     method public void onProvideAssistContent(android.app.AssistContent);
     method public void onProvideAssistData(android.os.Bundle);
+    method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
     method protected void onRestart();
     method protected void onRestoreInstanceState(android.os.Bundle);
     method public void onRestoreInstanceState(android.os.Bundle, android.os.PersistableBundle);
@@ -3544,6 +3553,7 @@
     method public boolean releaseInstance();
     method public final deprecated void removeDialog(int);
     method public void reportFullyDrawn();
+    method public final void requestPermissions(java.lang.String[], int);
     method public boolean requestVisibleBehind(boolean);
     method public final boolean requestWindowFeature(int);
     method public final void runOnUiThread(java.lang.Runnable);
@@ -4067,22 +4077,25 @@
     method public int describeContents();
     method public android.content.ComponentName getActivityComponent();
     method public static android.app.AssistStructure getAssistStructure(android.os.Bundle);
-    method public void getWindowAt(int, android.app.AssistStructure.ViewNode);
-    method public int getWindowCount();
+    method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
+    method public int getWindowNodeCount();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final java.lang.String ASSIST_KEY = "android:assist_structure";
     field public static final android.os.Parcelable.Creator<android.app.AssistStructure> CREATOR;
   }
 
   public static class AssistStructure.ViewNode {
-    ctor public AssistStructure.ViewNode();
-    method public void getChildAt(int, android.app.AssistStructure.ViewNode);
+    method public android.app.AssistStructure.ViewNode getChildAt(int);
     method public int getChildCount();
     method public java.lang.String getClassName();
-    method public java.lang.String getContentDescription();
+    method public java.lang.CharSequence getContentDescription();
     method public android.os.Bundle getExtras();
     method public int getHeight();
     method public java.lang.String getHint();
+    method public int getId();
+    method public java.lang.String getIdEntry();
+    method public java.lang.String getIdPackage();
+    method public java.lang.String getIdType();
     method public int getLeft();
     method public int getScrollX();
     method public int getScrollY();
@@ -4113,6 +4126,15 @@
     field public static final int TEXT_STYLE_UNDERLINE = 4; // 0x4
   }
 
+  public static class AssistStructure.WindowNode {
+    method public int getHeight();
+    method public int getLeft();
+    method public android.app.AssistStructure.ViewNode getRootViewNode();
+    method public java.lang.CharSequence getTitle();
+    method public int getTop();
+    method public int getWidth();
+  }
+
   public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener {
     ctor public DatePickerDialog(android.content.Context, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
     ctor public DatePickerDialog(android.content.Context, int, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
@@ -4393,6 +4415,7 @@
     method public void onOptionsMenuClosed(android.view.Menu);
     method public void onPause();
     method public void onPrepareOptionsMenu(android.view.Menu);
+    method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
     method public void onResume();
     method public void onSaveInstanceState(android.os.Bundle);
     method public void onStart();
@@ -4401,6 +4424,7 @@
     method public void onViewCreated(android.view.View, android.os.Bundle);
     method public void onViewStateRestored(android.os.Bundle);
     method public void registerForContextMenu(android.view.View);
+    method public final void requestPermissions(java.lang.String[], int);
     method public void setAllowEnterTransitionOverlap(boolean);
     method public void setAllowReturnTransitionOverlap(boolean);
     method public void setArguments(android.os.Bundle);
@@ -4774,6 +4798,7 @@
     field public static final java.lang.String CATEGORY_PROGRESS = "progress";
     field public static final java.lang.String CATEGORY_PROMO = "promo";
     field public static final java.lang.String CATEGORY_RECOMMENDATION = "recommendation";
+    field public static final java.lang.String CATEGORY_REMINDER = "reminder";
     field public static final java.lang.String CATEGORY_SERVICE = "service";
     field public static final java.lang.String CATEGORY_SOCIAL = "social";
     field public static final java.lang.String CATEGORY_STATUS = "status";
@@ -5767,12 +5792,18 @@
     field public static final java.lang.String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
     field public static final java.lang.String EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE = "android.app.extra.PROVISIONING_ACCOUNT_TO_MIGRATE";
     field public static final java.lang.String EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE = "android.app.extra.PROVISIONING_ADMIN_EXTRAS_BUNDLE";
+    field public static final java.lang.String EXTRA_PROVISIONING_BT_DEVICE_ID = "android.app.extra.PROVISIONING_BT_DEVICE_ID";
+    field public static final java.lang.String EXTRA_PROVISIONING_BT_MAC_ADDRESS = "android.app.extra.PROVISIONING_BT_MAC_ADDRESS";
+    field public static final java.lang.String EXTRA_PROVISIONING_BT_USE_PROXY = "android.app.extra.PROVISIONING_BT_USE_PROXY";
+    field public static final java.lang.String EXTRA_PROVISIONING_BT_UUID = "android.app.extra.PROVISIONING_BT_UUID";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME";
+    field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE = "android.app.extra.PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION";
     field public static final deprecated java.lang.String EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME";
+    field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER";
     field public static final java.lang.String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION";
@@ -5801,6 +5832,7 @@
     field public static final int KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS = 8; // 0x8
     field public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1; // 0x1
     field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC = "application/com.android.managedprovisioning";
+    field public static final java.lang.String MIME_TYPE_PROVISIONING_NFC_V2 = "application/com.android.managedprovisioning.v2";
     field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000
     field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000
     field public static final int PASSWORD_QUALITY_BIOMETRIC_WEAK = 32768; // 0x8000
@@ -5885,6 +5917,7 @@
     ctor public BackupTransport();
     method public int abortFullRestore();
     method public void cancelFullBackup();
+    method public int checkFullBackupSize(long);
     method public int clearBackupData(android.content.pm.PackageInfo);
     method public android.content.Intent configurationIntent();
     method public java.lang.String currentDestinationString();
@@ -6062,6 +6095,37 @@
     field public static final android.os.Parcelable.Creator<android.app.usage.ConfigurationStats> CREATOR;
   }
 
+  public class NetworkStatsManager {
+    method public android.app.usage.NetworkUsageStats queryDetails(int, java.lang.String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+    method public android.app.usage.NetworkUsageStats queryDetailsForUid(int, java.lang.String, long, long, int) throws android.os.RemoteException, java.lang.SecurityException;
+    method public android.app.usage.NetworkUsageStats querySummary(int, java.lang.String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+    method public android.app.usage.NetworkUsageStats.Bucket querySummaryForDevice(int, java.lang.String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+    method public android.app.usage.NetworkUsageStats.Bucket querySummaryForUser(int, java.lang.String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+  }
+
+  public final class NetworkUsageStats implements java.lang.AutoCloseable {
+    method public void close();
+    method public boolean getNextBucket(android.app.usage.NetworkUsageStats.Bucket);
+    method public boolean hasNextBucket();
+  }
+
+  public static class NetworkUsageStats.Bucket {
+    ctor public NetworkUsageStats.Bucket();
+    method public long getEndTimeStamp();
+    method public long getRxBytes();
+    method public long getRxPackets();
+    method public long getStartTimeStamp();
+    method public int getState();
+    method public long getTxBytes();
+    method public long getTxPackets();
+    method public int getUid();
+    field public static final int STATE_ALL = -1; // 0xffffffff
+    field public static final int STATE_DEFAULT = 1; // 0x1
+    field public static final int STATE_FOREGROUND = 2; // 0x2
+    field public static final int UID_REMOVED = -4; // 0xfffffffc
+    field public static final int UID_TETHERING = -5; // 0xfffffffb
+  }
+
   public final class UsageEvents implements android.os.Parcelable {
     method public int describeContents();
     method public boolean getNextEvent(android.app.usage.UsageEvents.Event);
@@ -6270,6 +6334,7 @@
     method public android.bluetooth.BluetoothDevice getRemoteDevice(byte[]);
     method public int getScanMode();
     method public int getState();
+    method public boolean isBleScanAlwaysAvailable();
     method public boolean isDiscovering();
     method public boolean isEnabled();
     method public boolean isMultipleAdvertisementSupported();
@@ -6286,6 +6351,7 @@
     field public static final java.lang.String ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
     field public static final java.lang.String ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED";
     field public static final java.lang.String ACTION_LOCAL_NAME_CHANGED = "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED";
+    field public static final java.lang.String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
     field public static final java.lang.String ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
     field public static final java.lang.String ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE";
     field public static final java.lang.String ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
@@ -7126,6 +7192,7 @@
     field public static final int SCAN_MODE_BALANCED = 1; // 0x1
     field public static final int SCAN_MODE_LOW_LATENCY = 2; // 0x2
     field public static final int SCAN_MODE_LOW_POWER = 0; // 0x0
+    field public static final int SCAN_MODE_OPPORTUNISTIC = -1; // 0xffffffff
     field public static final int SCAN_RESULT_TYPE_ABBREVIATED = 1; // 0x1
     field public static final int SCAN_RESULT_TYPE_FULL = 0; // 0x0
   }
@@ -7601,6 +7668,7 @@
     method public abstract int checkCallingPermission(java.lang.String);
     method public abstract int checkCallingUriPermission(android.net.Uri, int);
     method public abstract int checkPermission(java.lang.String, int, int);
+    method public abstract int checkSelfPermission(java.lang.String);
     method public abstract int checkUriPermission(android.net.Uri, int, int, int);
     method public abstract int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public abstract deprecated void clearWallpaper() throws java.io.IOException;
@@ -7747,6 +7815,7 @@
     field public static final deprecated int MODE_WORLD_READABLE = 1; // 0x1
     field public static final deprecated int MODE_WORLD_WRITEABLE = 2; // 0x2
     field public static final java.lang.String NETWORK_SCORE_SERVICE = "network_score";
+    field public static final java.lang.String NETWORK_STATS_SERVICE = "netstats";
     field public static final java.lang.String NFC_SERVICE = "nfc";
     field public static final java.lang.String NOTIFICATION_SERVICE = "notification";
     field public static final java.lang.String NSD_SERVICE = "servicediscovery";
@@ -7784,6 +7853,7 @@
     method public int checkCallingPermission(java.lang.String);
     method public int checkCallingUriPermission(android.net.Uri, int);
     method public int checkPermission(java.lang.String, int, int);
+    method public int checkSelfPermission(java.lang.String);
     method public int checkUriPermission(android.net.Uri, int, int, int);
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public deprecated void clearWallpaper() throws java.io.IOException;
@@ -8114,12 +8184,15 @@
     field public static final java.lang.String ACTION_INSERT = "android.intent.action.INSERT";
     field public static final java.lang.String ACTION_INSERT_OR_EDIT = "android.intent.action.INSERT_OR_EDIT";
     field public static final java.lang.String ACTION_INSTALL_PACKAGE = "android.intent.action.INSTALL_PACKAGE";
+    field public static final java.lang.String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
     field public static final java.lang.String ACTION_LOCALE_CHANGED = "android.intent.action.LOCALE_CHANGED";
     field public static final java.lang.String ACTION_MAIN = "android.intent.action.MAIN";
     field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED";
     field public static final java.lang.String ACTION_MANAGED_PROFILE_REMOVED = "android.intent.action.MANAGED_PROFILE_REMOVED";
+    field public static final java.lang.String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS";
     field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
     field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
+    field public static final java.lang.String ACTION_MANAGE_PERMISSION_APPS = "android.intent.action.MANAGE_PERMISSION_APPS";
     field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
     field public static final java.lang.String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON";
     field public static final java.lang.String ACTION_MEDIA_CHECKING = "android.intent.action.MEDIA_CHECKING";
@@ -8256,6 +8329,8 @@
     field public static final java.lang.String EXTRA_NOT_UNKNOWN_SOURCE = "android.intent.extra.NOT_UNKNOWN_SOURCE";
     field public static final java.lang.String EXTRA_ORIGINATING_URI = "android.intent.extra.ORIGINATING_URI";
     field public static final java.lang.String EXTRA_PACKAGES = "android.intent.extra.PACKAGES";
+    field public static final java.lang.String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
+    field public static final java.lang.String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
     field public static final java.lang.String EXTRA_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
     field public static final java.lang.String EXTRA_REFERRER = "android.intent.extra.REFERRER";
     field public static final java.lang.String EXTRA_REFERRER_NAME = "android.intent.extra.REFERRER_NAME";
@@ -8407,6 +8482,8 @@
     field public static final int NO_MATCH_CATEGORY = -4; // 0xfffffffc
     field public static final int NO_MATCH_DATA = -2; // 0xfffffffe
     field public static final int NO_MATCH_TYPE = -1; // 0xffffffff
+    field public static final java.lang.String SCHEME_HTTP = "http";
+    field public static final java.lang.String SCHEME_HTTPS = "https";
     field public static final int SYSTEM_HIGH_PRIORITY = 1000; // 0x3e8
     field public static final int SYSTEM_LOW_PRIORITY = -1000; // 0xfffffc18
   }
@@ -8528,6 +8605,7 @@
     ctor public RestrictionEntry(java.lang.String, boolean);
     ctor public RestrictionEntry(java.lang.String, java.lang.String[]);
     ctor public RestrictionEntry(java.lang.String, int);
+    ctor public RestrictionEntry(java.lang.String, android.content.RestrictionEntry[], boolean);
     ctor public RestrictionEntry(android.os.Parcel);
     method public int describeContents();
     method public java.lang.String[] getAllSelectedStrings();
@@ -8536,6 +8614,7 @@
     method public java.lang.String getDescription();
     method public int getIntValue();
     method public java.lang.String getKey();
+    method public android.content.RestrictionEntry[] getRestrictions();
     method public boolean getSelectedState();
     method public java.lang.String getSelectedString();
     method public java.lang.String getTitle();
@@ -8547,6 +8626,7 @@
     method public void setChoiceValues(android.content.Context, int);
     method public void setDescription(java.lang.String);
     method public void setIntValue(int);
+    method public void setRestrictions(android.content.RestrictionEntry[]);
     method public void setSelectedState(boolean);
     method public void setSelectedString(java.lang.String);
     method public void setTitle(java.lang.String);
@@ -8554,6 +8634,8 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.RestrictionEntry> CREATOR;
     field public static final int TYPE_BOOLEAN = 1; // 0x1
+    field public static final int TYPE_BUNDLE = 7; // 0x7
+    field public static final int TYPE_BUNDLE_ARRAY = 8; // 0x8
     field public static final int TYPE_CHOICE = 2; // 0x2
     field public static final int TYPE_INTEGER = 5; // 0x5
     field public static final int TYPE_MULTI_SELECT = 4; // 0x4
@@ -8562,6 +8644,7 @@
   }
 
   public class RestrictionsManager {
+    method public static android.os.Bundle convertRestrictionsToBundle(java.util.List<android.content.RestrictionEntry>);
     method public android.content.Intent createLocalApprovalIntent();
     method public android.os.Bundle getApplicationRestrictions();
     method public java.util.List<android.content.RestrictionEntry> getManifestRestrictions(java.lang.String);
@@ -8860,6 +8943,7 @@
     field public static final int FLAG_ALLOW_TASK_REPARENTING = 32; // 0x20
     field public static final int FLAG_DEBUGGABLE = 2; // 0x2
     field public static final int FLAG_EXTERNAL_STORAGE = 262144; // 0x40000
+    field public static final int FLAG_EXTRACT_NATIVE_LIBS = 268435456; // 0x10000000
     field public static final int FLAG_FACTORY_TEST = 16; // 0x10
     field public static final int FLAG_FULL_BACKUP_ONLY = 67108864; // 0x4000000
     field public static final int FLAG_HAS_CODE = 4; // 0x4
@@ -8882,6 +8966,7 @@
     field public static final int FLAG_SYSTEM = 1; // 0x1
     field public static final int FLAG_TEST_ONLY = 256; // 0x100
     field public static final int FLAG_UPDATED_SYSTEM_APP = 128; // 0x80
+    field public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 134217728; // 0x8000000
     field public static final int FLAG_VM_SAFE_MODE = 16384; // 0x4000
     field public java.lang.String backupAgentName;
     field public java.lang.String className;
@@ -9003,6 +9088,25 @@
     field public java.lang.String targetPackage;
   }
 
+  public final class IntentFilterVerificationInfo implements android.os.Parcelable {
+    ctor public IntentFilterVerificationInfo();
+    ctor public IntentFilterVerificationInfo(java.lang.String, java.lang.String[]);
+    ctor public IntentFilterVerificationInfo(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    ctor public IntentFilterVerificationInfo(android.os.Parcel);
+    method public int describeContents();
+    method public java.lang.String[] getDomains();
+    method public java.lang.String getDomainsString();
+    method public java.lang.String getPackageName();
+    method public int getStatus();
+    method public java.lang.String getStatusString();
+    method public static java.lang.String getStatusStringFromValue(int);
+    method public void readFromXml(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void setStatus(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    method public void writeToXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
+    field public static final android.os.Parcelable.Creator<android.content.pm.IntentFilterVerificationInfo> CREATOR;
+  }
+
   public class LabeledIntent extends android.content.Intent {
     ctor public LabeledIntent(android.content.Intent, java.lang.String, int, int);
     ctor public LabeledIntent(android.content.Intent, java.lang.String, java.lang.CharSequence, int);
@@ -9064,7 +9168,6 @@
     field public static final int INSTALL_LOCATION_INTERNAL_ONLY = 1; // 0x1
     field public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2; // 0x2
     field public static final int REQUESTED_PERMISSION_GRANTED = 2; // 0x2
-    field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
     field public android.content.pm.ActivityInfo[] activities;
     field public android.content.pm.ApplicationInfo applicationInfo;
     field public int baseRevisionCode;
@@ -9238,6 +9341,7 @@
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
     method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public abstract java.lang.String getNameForUid(int);
@@ -9264,6 +9368,7 @@
     method public abstract android.graphics.drawable.Drawable getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle);
     method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
+    method public abstract void grantPermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract boolean hasSystemFeature(java.lang.String);
     method public abstract boolean isSafeMode();
     method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -9279,16 +9384,20 @@
     method public abstract android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int);
     method public abstract android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int);
     method public abstract android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
+    method public abstract void revokePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract void setApplicationEnabledSetting(java.lang.String, int, int);
     method public abstract void setComponentEnabledSetting(android.content.ComponentName, int, int);
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
     method public abstract void verifyPendingInstall(int, int);
+    field public static final java.lang.String ACTION_REQUEST_PERMISSIONS = "android.content.pm.action.REQUEST_PERMISSIONS";
     field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
     field public static final int COMPONENT_ENABLED_STATE_DISABLED = 2; // 0x2
     field public static final int COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED = 4; // 0x4
     field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
     field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
     field public static final int DONT_KILL_APP = 1; // 0x1
+    field public static final java.lang.String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
+    field public static final java.lang.String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
     field public static final java.lang.String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
     field public static final java.lang.String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
     field public static final java.lang.String FEATURE_APP_WIDGETS = "android.software.app_widgets";
@@ -9324,6 +9433,7 @@
     field public static final java.lang.String FEATURE_LOCATION_NETWORK = "android.hardware.location.network";
     field public static final java.lang.String FEATURE_MANAGED_USERS = "android.software.managed_users";
     field public static final java.lang.String FEATURE_MICROPHONE = "android.hardware.microphone";
+    field public static final java.lang.String FEATURE_MIDI = "android.software.midi";
     field public static final java.lang.String FEATURE_NFC = "android.hardware.nfc";
     field public static final java.lang.String FEATURE_NFC_HOST_CARD_EMULATION = "android.hardware.nfc.hce";
     field public static final java.lang.String FEATURE_OPENGLES_EXTENSION_PACK = "android.hardware.opengles.aep";
@@ -9628,11 +9738,10 @@
 
   public class ColorStateList implements android.os.Parcelable {
     ctor public ColorStateList(int[][], int[]);
-    method public void applyTheme(android.content.res.Resources.Theme);
-    method public boolean canApplyTheme();
     method public static deprecated android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public static android.content.res.ColorStateList createFromXml(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public int describeContents();
+    method public int getChangingConfigurations();
     method public int getColorForState(int[], int);
     method public int getDefaultColor();
     method public boolean isOpaque();
@@ -11350,7 +11459,9 @@
     field public static final int JPEG = 256; // 0x100
     field public static final int NV16 = 16; // 0x10
     field public static final int NV21 = 17; // 0x11
+    field public static final int PRIVATE = 34; // 0x22
     field public static final int RAW10 = 37; // 0x25
+    field public static final int RAW12 = 38; // 0x26
     field public static final int RAW_SENSOR = 32; // 0x20
     field public static final int RGB_565 = 4; // 0x4
     field public static final int UNKNOWN = 0; // 0x0
@@ -11566,6 +11677,7 @@
     method public int getTextWidths(java.lang.String, float[]);
     method public android.graphics.Typeface getTypeface();
     method public android.graphics.Xfermode getXfermode();
+    method public boolean hasGlyph(java.lang.String);
     method public final boolean isAntiAlias();
     method public final boolean isDither();
     method public boolean isElegantTextHeight();
@@ -12146,9 +12258,12 @@
 
   public class AnimatedVectorDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Animatable {
     ctor public AnimatedVectorDrawable();
+    method public void addListener(android.animation.Animator.AnimatorListener);
     method public void draw(android.graphics.Canvas);
+    method public java.util.List<android.animation.Animator.AnimatorListener> getListeners();
     method public int getOpacity();
     method public boolean isRunning();
+    method public void removeListener(android.animation.Animator.AnimatorListener);
     method public void setAlpha(int);
     method public void setColorFilter(android.graphics.ColorFilter);
     method public void start();
@@ -14097,6 +14212,7 @@
     method public void setHdmiMhlVendorCommandListener(android.hardware.hdmi.HdmiTvClient.HdmiMhlVendorCommandListener);
     method public void setInputChangeListener(android.hardware.hdmi.HdmiTvClient.InputChangeListener);
     method public void setRecordListener(android.hardware.hdmi.HdmiRecordListener);
+    method public void setSystemAudioMode(boolean, android.hardware.hdmi.HdmiTvClient.SelectCallback);
     method public void setSystemAudioMute(boolean);
     method public void setSystemAudioVolume(int, int, int);
     method public void startOneTouchRecord(int, android.hardware.hdmi.HdmiRecordSources.RecordSource);
@@ -15737,6 +15853,16 @@
     field public static final int SUCCESS = 0; // 0x0
   }
 
+  public static class AudioRecord.Builder {
+    ctor public AudioRecord.Builder();
+    method public android.media.AudioRecord build() throws java.lang.UnsupportedOperationException;
+    method public android.media.AudioRecord.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
+    method public android.media.AudioRecord.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException;
+    method public android.media.AudioRecord.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException;
+    method public android.media.AudioRecord.Builder setCapturePreset(int) throws java.lang.IllegalArgumentException;
+    method public android.media.AudioRecord.Builder setSessionId(int) throws java.lang.IllegalArgumentException;
+  }
+
   public static abstract interface AudioRecord.OnRecordPositionUpdateListener {
     method public abstract void onMarkerReached(android.media.AudioRecord);
     method public abstract void onPeriodicNotification(android.media.AudioRecord);
@@ -16358,6 +16484,7 @@
     method public android.graphics.Bitmap getIconBitmap();
     method public android.net.Uri getIconUri();
     method public java.lang.String getMediaId();
+    method public android.net.Uri getMediaUri();
     method public java.lang.CharSequence getSubtitle();
     method public java.lang.CharSequence getTitle();
     method public void writeToParcel(android.os.Parcel, int);
@@ -16372,6 +16499,7 @@
     method public android.media.MediaDescription.Builder setIconBitmap(android.graphics.Bitmap);
     method public android.media.MediaDescription.Builder setIconUri(android.net.Uri);
     method public android.media.MediaDescription.Builder setMediaId(java.lang.String);
+    method public android.media.MediaDescription.Builder setMediaUri(android.net.Uri);
     method public android.media.MediaDescription.Builder setSubtitle(java.lang.CharSequence);
     method public android.media.MediaDescription.Builder setTitle(java.lang.CharSequence);
   }
@@ -16414,6 +16542,9 @@
     field public static final java.lang.String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
     field public static final java.lang.String PROPERTY_VENDOR = "vendor";
     field public static final java.lang.String PROPERTY_VERSION = "version";
+    field public static final int REQUEST_TYPE_INITIAL = 0; // 0x0
+    field public static final int REQUEST_TYPE_RELEASE = 2; // 0x2
+    field public static final int REQUEST_TYPE_RENEWAL = 1; // 0x1
   }
 
   public final class MediaDrm.CryptoSession {
@@ -16426,6 +16557,7 @@
   public static final class MediaDrm.KeyRequest {
     method public byte[] getData();
     method public java.lang.String getDefaultUrl();
+    method public int getRequestType();
   }
 
   public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException {
@@ -18252,6 +18384,7 @@
     method public void play();
     method public void playFromMediaId(java.lang.String, android.os.Bundle);
     method public void playFromSearch(java.lang.String, android.os.Bundle);
+    method public void playFromUri(android.net.Uri, android.os.Bundle);
     method public void rewind();
     method public void seekTo(long);
     method public void sendCustomAction(android.media.session.PlaybackState.CustomAction, android.os.Bundle);
@@ -18299,6 +18432,7 @@
     method public void onPlay();
     method public void onPlayFromMediaId(java.lang.String, android.os.Bundle);
     method public void onPlayFromSearch(java.lang.String, android.os.Bundle);
+    method public void onPlayFromUri(android.net.Uri, android.os.Bundle);
     method public void onRewind();
     method public void onSeekTo(long);
     method public void onSetRating(android.media.Rating);
@@ -18353,6 +18487,7 @@
     field public static final long ACTION_PLAY = 4L; // 0x4L
     field public static final long ACTION_PLAY_FROM_MEDIA_ID = 1024L; // 0x400L
     field public static final long ACTION_PLAY_FROM_SEARCH = 2048L; // 0x800L
+    field public static final long ACTION_PLAY_FROM_URI = 8192L; // 0x2000L
     field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L
     field public static final long ACTION_REWIND = 8L; // 0x8L
     field public static final long ACTION_SEEK_TO = 256L; // 0x100L
@@ -18537,6 +18672,10 @@
     field public static final java.lang.String COLUMN_EPISODE_NUMBER = "episode_number";
     field public static final java.lang.String COLUMN_EPISODE_TITLE = "episode_title";
     field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+    field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
     field public static final java.lang.String COLUMN_LONG_DESCRIPTION = "long_description";
     field public static final java.lang.String COLUMN_POSTER_ART_URI = "poster_art_uri";
     field public static final java.lang.String COLUMN_SEASON_NUMBER = "season_number";
@@ -20034,7 +20173,8 @@
   }
 
   public class RttManager {
-    method public android.net.wifi.RttManager.Capabilities getCapabilities();
+    method public deprecated android.net.wifi.RttManager.Capabilities getCapabilities();
+    method public android.net.wifi.RttManager.RttCapabilities getRttCapabilities();
     method public void startRanging(android.net.wifi.RttManager.RttParams[], android.net.wifi.RttManager.RttListener);
     method public void stopRanging(android.net.wifi.RttManager.RttListener);
     field public static final int BASE = 160256; // 0x27200
@@ -20044,10 +20184,19 @@
     field public static final int CMD_OP_STOP_RANGING = 160257; // 0x27201
     field public static final int CMD_OP_SUCCEEDED = 160259; // 0x27203
     field public static final java.lang.String DESCRIPTION_KEY = "android.net.wifi.RttManager.Description";
+    field public static final int PREAMBLE_HT = 2; // 0x2
+    field public static final int PREAMBLE_LEGACY = 1; // 0x1
+    field public static final int PREAMBLE_VHT = 4; // 0x4
     field public static final int REASON_INVALID_LISTENER = -3; // 0xfffffffd
     field public static final int REASON_INVALID_REQUEST = -4; // 0xfffffffc
     field public static final int REASON_NOT_AVAILABLE = -2; // 0xfffffffe
     field public static final int REASON_UNSPECIFIED = -1; // 0xffffffff
+    field public static final int RTT_BW_10_SUPPORT = 2; // 0x2
+    field public static final int RTT_BW_160_SUPPORT = 32; // 0x20
+    field public static final int RTT_BW_20_SUPPORT = 4; // 0x4
+    field public static final int RTT_BW_40_SUPPORT = 8; // 0x8
+    field public static final int RTT_BW_5_SUPPORT = 1; // 0x1
+    field public static final int RTT_BW_80_SUPPORT = 16; // 0x10
     field public static final int RTT_CHANNEL_WIDTH_10 = 6; // 0x6
     field public static final int RTT_CHANNEL_WIDTH_160 = 3; // 0x3
     field public static final int RTT_CHANNEL_WIDTH_20 = 0; // 0x0
@@ -20055,26 +20204,31 @@
     field public static final int RTT_CHANNEL_WIDTH_5 = 5; // 0x5
     field public static final int RTT_CHANNEL_WIDTH_80 = 2; // 0x2
     field public static final int RTT_CHANNEL_WIDTH_80P80 = 4; // 0x4
-    field public static final int RTT_CHANNEL_WIDTH_UNSPECIFIED = -1; // 0xffffffff
+    field public static final deprecated int RTT_CHANNEL_WIDTH_UNSPECIFIED = -1; // 0xffffffff
     field public static final int RTT_PEER_TYPE_AP = 1; // 0x1
     field public static final int RTT_PEER_TYPE_STA = 2; // 0x2
     field public static final int RTT_PEER_TYPE_UNSPECIFIED = 0; // 0x0
     field public static final int RTT_STATUS_ABORTED = 8; // 0x8
     field public static final int RTT_STATUS_FAILURE = 1; // 0x1
     field public static final int RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL = 6; // 0x6
+    field public static final int RTT_STATUS_FAIL_BUSY_TRY_LATER = 12; // 0xc
+    field public static final int RTT_STATUS_FAIL_INVALID_TS = 9; // 0x9
     field public static final int RTT_STATUS_FAIL_NOT_SCHEDULED_YET = 4; // 0x4
     field public static final int RTT_STATUS_FAIL_NO_CAPABILITY = 7; // 0x7
     field public static final int RTT_STATUS_FAIL_NO_RSP = 2; // 0x2
+    field public static final int RTT_STATUS_FAIL_PROTOCOL = 10; // 0xa
     field public static final int RTT_STATUS_FAIL_REJECTED = 3; // 0x3
+    field public static final int RTT_STATUS_FAIL_SCHEDULE = 11; // 0xb
     field public static final int RTT_STATUS_FAIL_TM_TIMEOUT = 5; // 0x5
     field public static final int RTT_STATUS_SUCCESS = 0; // 0x0
-    field public static final int RTT_TYPE_11_MC = 4; // 0x4
-    field public static final int RTT_TYPE_11_V = 2; // 0x2
+    field public static final deprecated int RTT_TYPE_11_MC = 4; // 0x4
+    field public static final deprecated int RTT_TYPE_11_V = 2; // 0x2
     field public static final int RTT_TYPE_ONE_SIDED = 1; // 0x1
-    field public static final int RTT_TYPE_UNSPECIFIED = 0; // 0x0
+    field public static final int RTT_TYPE_TWO_SIDED = 4; // 0x4
+    field public static final deprecated int RTT_TYPE_UNSPECIFIED = 0; // 0x0
   }
 
-  public class RttManager.Capabilities {
+  public deprecated class RttManager.Capabilities {
     ctor public RttManager.Capabilities();
     field public int supportedPeerType;
     field public int supportedType;
@@ -20093,6 +20247,20 @@
     field public android.net.wifi.RttManager.RttResult[] mResults;
   }
 
+  public static class RttManager.RttCapabilities implements android.os.Parcelable {
+    ctor public RttManager.RttCapabilities();
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public int bwSupported;
+    field public boolean lciSupported;
+    field public boolean lcrSupported;
+    field public boolean oneSidedRttSupported;
+    field public int preambleSupported;
+    field public deprecated boolean supportedPeerType;
+    field public deprecated boolean supportedType;
+    field public boolean twoSided11McRttSupported;
+  }
+
   public static abstract interface RttManager.RttListener {
     method public abstract void onAborted();
     method public abstract void onFailure(int, java.lang.String);
@@ -20101,30 +20269,64 @@
 
   public static class RttManager.RttParams {
     ctor public RttManager.RttParams();
+    field public boolean LCIRequest;
+    field public boolean LCRRequest;
+    field public int bandwidth;
     field public java.lang.String bssid;
+    field public int burstTimeout;
+    field public int centerFreq0;
+    field public int centerFreq1;
     field public int channelWidth;
     field public int deviceType;
     field public int frequency;
-    field public int num_retries;
-    field public int num_samples;
+    field public int interval;
+    field public int numRetriesPerFTMR;
+    field public int numRetriesPerMeasurementFrame;
+    field public int numSamplesPerBurst;
+    field public deprecated int num_retries;
+    field public deprecated int num_samples;
+    field public int numberBurst;
+    field public int preamble;
     field public int requestType;
   }
 
   public static class RttManager.RttResult {
     ctor public RttManager.RttResult();
     field public java.lang.String bssid;
-    field public int distance_cm;
-    field public int distance_sd_cm;
-    field public int distance_spread_cm;
-    field public int requestType;
+    field public int burstDuration;
+    field public int burstNumber;
+    field public int distance;
+    field public int distanceSpread;
+    field public int distanceStandardDeviation;
+    field public deprecated int distance_cm;
+    field public deprecated int distance_sd_cm;
+    field public deprecated int distance_spread_cm;
+    field public int frameNumberPerBurstPeer;
+    field public int measurementFrameNumber;
+    field public int measurementType;
+    field public deprecated int requestType;
+    field public int retryAfterDuration;
     field public int rssi;
-    field public int rssi_spread;
-    field public long rtt_ns;
-    field public long rtt_sd_ns;
-    field public long rtt_spread_ns;
+    field public int rssiSpread;
+    field public deprecated int rssi_spread;
+    field public long rtt;
+    field public long rttSpread;
+    field public long rttStandardDeviation;
+    field public deprecated long rtt_ns;
+    field public deprecated long rtt_sd_ns;
+    field public deprecated long rtt_spread_ns;
+    field public int rxRate;
     field public int status;
+    field public int successMeasurementFrameNumber;
     field public long ts;
-    field public int tx_rate;
+    field public int txRate;
+    field public deprecated int tx_rate;
+  }
+
+  public class RttManager.wifiInformationElement {
+    ctor public RttManager.wifiInformationElement();
+    field public java.lang.String data;
+    field public int id;
   }
 
   public class ScanResult implements android.os.Parcelable {
@@ -20144,7 +20346,10 @@
     field public int frequency;
     field public boolean is80211McRTTResponder;
     field public int level;
+    field public java.lang.String operatorFriendlyName;
+    field public boolean passpointNetwork;
     field public long timestamp;
+    field public java.lang.String venueName;
   }
 
   public final class SupplicantState extends java.lang.Enum implements android.os.Parcelable {
@@ -20171,6 +20376,7 @@
   public class WifiConfiguration implements android.os.Parcelable {
     ctor public WifiConfiguration();
     method public int describeContents();
+    method public boolean isPasspoint();
     method public void writeToParcel(android.os.Parcel, int);
     field public java.lang.String BSSID;
     field public java.lang.String FQDN;
@@ -20296,6 +20502,7 @@
 
   public static final class WifiEnterpriseConfig.Eap {
     field public static final int AKA = 5; // 0x5
+    field public static final int AKA_PRIME = 6; // 0x6
     field public static final int NONE = -1; // 0xffffffff
     field public static final int PEAP = 0; // 0x0
     field public static final int PWD = 3; // 0x3
@@ -24207,6 +24414,7 @@
     field public static java.lang.String DIRECTORY_RINGTONES;
     field public static final java.lang.String MEDIA_BAD_REMOVAL = "bad_removal";
     field public static final java.lang.String MEDIA_CHECKING = "checking";
+    field public static final java.lang.String MEDIA_EJECTING = "ejecting";
     field public static final java.lang.String MEDIA_MOUNTED = "mounted";
     field public static final java.lang.String MEDIA_MOUNTED_READ_ONLY = "mounted_ro";
     field public static final java.lang.String MEDIA_NOFS = "nofs";
@@ -24638,15 +24846,19 @@
   }
 
   public final class PowerManager {
+    method public boolean isDeviceIdleMode();
     method public boolean isInteractive();
     method public boolean isPowerSaveMode();
+    method public boolean isScreenBrightnessBoosted();
     method public deprecated boolean isScreenOn();
     method public boolean isWakeLockLevelSupported(int);
     method public android.os.PowerManager.WakeLock newWakeLock(int, java.lang.String);
     method public void reboot(java.lang.String);
     method public void userActivity(long, int, int);
     field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000
+    field public static final java.lang.String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
     field public static final java.lang.String ACTION_POWER_SAVE_MODE_CHANGED = "android.os.action.POWER_SAVE_MODE_CHANGED";
+    field public static final java.lang.String ACTION_SCREEN_BRIGHTNESS_BOOST_CHANGED = "android.os.action.SCREEN_BRIGHTNESS_BOOST_CHANGED";
     field public static final deprecated int FULL_WAKE_LOCK = 26; // 0x1a
     field public static final int ON_AFTER_RELEASE = 536870912; // 0x20000000
     field public static final int PARTIAL_WAKE_LOCK = 1; // 0x1
@@ -27719,6 +27931,108 @@
     field public static final java.lang.String SIZE = "_size";
   }
 
+  public abstract class SearchIndexableData {
+    ctor public SearchIndexableData();
+    ctor public SearchIndexableData(android.content.Context);
+    field public java.lang.String className;
+    field public android.content.Context context;
+    field public boolean enabled;
+    field public int iconResId;
+    field public java.lang.String intentAction;
+    field public java.lang.String intentTargetClass;
+    field public java.lang.String intentTargetPackage;
+    field public java.lang.String key;
+    field public java.util.Locale locale;
+    field public java.lang.String packageName;
+    field public int rank;
+    field public int userId;
+  }
+
+  public class SearchIndexableResource extends android.provider.SearchIndexableData {
+    ctor public SearchIndexableResource(int, int, java.lang.String, int);
+    ctor public SearchIndexableResource(android.content.Context);
+    field public int xmlResId;
+  }
+
+  public class SearchIndexablesContract {
+    ctor public SearchIndexablesContract();
+    field public static final int COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE = 0; // 0x0
+    field public static final int COLUMN_INDEX_RAW_CLASS_NAME = 7; // 0x7
+    field public static final int COLUMN_INDEX_RAW_ENTRIES = 4; // 0x4
+    field public static final int COLUMN_INDEX_RAW_ICON_RESID = 8; // 0x8
+    field public static final int COLUMN_INDEX_RAW_INTENT_ACTION = 9; // 0x9
+    field public static final int COLUMN_INDEX_RAW_INTENT_TARGET_CLASS = 11; // 0xb
+    field public static final int COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE = 10; // 0xa
+    field public static final int COLUMN_INDEX_RAW_KEY = 12; // 0xc
+    field public static final int COLUMN_INDEX_RAW_KEYWORDS = 5; // 0x5
+    field public static final int COLUMN_INDEX_RAW_RANK = 0; // 0x0
+    field public static final int COLUMN_INDEX_RAW_SCREEN_TITLE = 6; // 0x6
+    field public static final int COLUMN_INDEX_RAW_SUMMARY_OFF = 3; // 0x3
+    field public static final int COLUMN_INDEX_RAW_SUMMARY_ON = 2; // 0x2
+    field public static final int COLUMN_INDEX_RAW_TITLE = 1; // 0x1
+    field public static final int COLUMN_INDEX_RAW_USER_ID = 13; // 0xd
+    field public static final int COLUMN_INDEX_XML_RES_CLASS_NAME = 2; // 0x2
+    field public static final int COLUMN_INDEX_XML_RES_ICON_RESID = 3; // 0x3
+    field public static final int COLUMN_INDEX_XML_RES_INTENT_ACTION = 4; // 0x4
+    field public static final int COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS = 6; // 0x6
+    field public static final int COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE = 5; // 0x5
+    field public static final int COLUMN_INDEX_XML_RES_RANK = 0; // 0x0
+    field public static final int COLUMN_INDEX_XML_RES_RESID = 1; // 0x1
+    field public static final java.lang.String INDEXABLES_RAW = "indexables_raw";
+    field public static final java.lang.String[] INDEXABLES_RAW_COLUMNS;
+    field public static final java.lang.String INDEXABLES_RAW_PATH = "settings/indexables_raw";
+    field public static final java.lang.String INDEXABLES_XML_RES = "indexables_xml_res";
+    field public static final java.lang.String[] INDEXABLES_XML_RES_COLUMNS;
+    field public static final java.lang.String INDEXABLES_XML_RES_PATH = "settings/indexables_xml_res";
+    field public static final java.lang.String NON_INDEXABLES_KEYS = "non_indexables_key";
+    field public static final java.lang.String[] NON_INDEXABLES_KEYS_COLUMNS;
+    field public static final java.lang.String NON_INDEXABLES_KEYS_PATH = "settings/non_indexables_key";
+    field public static final java.lang.String PROVIDER_INTERFACE = "android.content.action.SEARCH_INDEXABLES_PROVIDER";
+  }
+
+  public static class SearchIndexablesContract.BaseColumns {
+    field public static final java.lang.String COLUMN_CLASS_NAME = "className";
+    field public static final java.lang.String COLUMN_ICON_RESID = "iconResId";
+    field public static final java.lang.String COLUMN_INTENT_ACTION = "intentAction";
+    field public static final java.lang.String COLUMN_INTENT_TARGET_CLASS = "intentTargetClass";
+    field public static final java.lang.String COLUMN_INTENT_TARGET_PACKAGE = "intentTargetPackage";
+    field public static final java.lang.String COLUMN_RANK = "rank";
+  }
+
+  public static final class SearchIndexablesContract.NonIndexableKey extends android.provider.SearchIndexablesContract.BaseColumns {
+    field public static final java.lang.String COLUMN_KEY_VALUE = "key";
+    field public static final java.lang.String MIME_TYPE = "vnd.android.cursor.dir/non_indexables_key";
+  }
+
+  public static final class SearchIndexablesContract.RawData extends android.provider.SearchIndexablesContract.BaseColumns {
+    field public static final java.lang.String COLUMN_ENTRIES = "entries";
+    field public static final java.lang.String COLUMN_KEY = "key";
+    field public static final java.lang.String COLUMN_KEYWORDS = "keywords";
+    field public static final java.lang.String COLUMN_SCREEN_TITLE = "screenTitle";
+    field public static final java.lang.String COLUMN_SUMMARY_OFF = "summaryOff";
+    field public static final java.lang.String COLUMN_SUMMARY_ON = "summaryOn";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_USER_ID = "user_id";
+    field public static final java.lang.String MIME_TYPE = "vnd.android.cursor.dir/indexables_raw";
+  }
+
+  public static final class SearchIndexablesContract.XmlResource extends android.provider.SearchIndexablesContract.BaseColumns {
+    field public static final java.lang.String COLUMN_XML_RESID = "xmlResId";
+    field public static final java.lang.String MIME_TYPE = "vnd.android.cursor.dir/indexables_xml_res";
+  }
+
+  public abstract class SearchIndexablesProvider extends android.content.ContentProvider {
+    ctor public SearchIndexablesProvider();
+    method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
+    method public java.lang.String getType(android.net.Uri);
+    method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
+    method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
+    method public abstract android.database.Cursor queryNonIndexableKeys(java.lang.String[]);
+    method public abstract android.database.Cursor queryRawData(java.lang.String[]);
+    method public abstract android.database.Cursor queryXmlResources(java.lang.String[]);
+    method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
+  }
+
   public class SearchRecentSuggestions {
     ctor public SearchRecentSuggestions(android.content.Context, java.lang.String, int);
     method public void clearHistory();
@@ -28462,6 +28776,16 @@
     method public void copy1DRangeFromUnchecked(int, int, short[]);
     method public void copy1DRangeFromUnchecked(int, int, byte[]);
     method public void copy1DRangeFromUnchecked(int, int, float[]);
+    method public void copy1DRangeTo(int, int, java.lang.Object);
+    method public void copy1DRangeTo(int, int, int[]);
+    method public void copy1DRangeTo(int, int, short[]);
+    method public void copy1DRangeTo(int, int, byte[]);
+    method public void copy1DRangeTo(int, int, float[]);
+    method public void copy1DRangeToUnchecked(int, int, java.lang.Object);
+    method public void copy1DRangeToUnchecked(int, int, int[]);
+    method public void copy1DRangeToUnchecked(int, int, short[]);
+    method public void copy1DRangeToUnchecked(int, int, byte[]);
+    method public void copy1DRangeToUnchecked(int, int, float[]);
     method public void copy2DRangeFrom(int, int, int, int, java.lang.Object);
     method public void copy2DRangeFrom(int, int, int, int, byte[]);
     method public void copy2DRangeFrom(int, int, int, int, short[]);
@@ -28469,6 +28793,14 @@
     method public void copy2DRangeFrom(int, int, int, int, float[]);
     method public void copy2DRangeFrom(int, int, int, int, android.renderscript.Allocation, int, int);
     method public void copy2DRangeFrom(int, int, android.graphics.Bitmap);
+    method public void copy2DRangeTo(int, int, int, int, java.lang.Object);
+    method public void copy2DRangeTo(int, int, int, int, byte[]);
+    method public void copy2DRangeTo(int, int, int, int, short[]);
+    method public void copy2DRangeTo(int, int, int, int, int[]);
+    method public void copy2DRangeTo(int, int, int, int, float[]);
+    method public void copy3DRangeFrom(int, int, int, int, int, int, java.lang.Object);
+    method public void copy3DRangeFrom(int, int, int, int, int, int, android.renderscript.Allocation, int, int, int);
+    method public void copy3DRangeTo(int, int, int, int, int, int, java.lang.Object);
     method public void copyFrom(android.renderscript.BaseObj[]);
     method public void copyFrom(java.lang.Object);
     method public void copyFrom(int[]);
@@ -28488,6 +28820,7 @@
     method public void copyTo(short[]);
     method public void copyTo(int[]);
     method public void copyTo(float[]);
+    method public void copyToFieldPacker(int, int, int, int, android.renderscript.FieldPacker);
     method public static android.renderscript.Allocation createCubemapFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap, android.renderscript.Allocation.MipmapControl, int);
     method public static android.renderscript.Allocation createCubemapFromBitmap(android.renderscript.RenderScript, android.graphics.Bitmap);
     method public static android.renderscript.Allocation createCubemapFromCubeFaces(android.renderscript.RenderScript, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.graphics.Bitmap, android.renderscript.Allocation.MipmapControl, int);
@@ -28511,8 +28844,10 @@
     method public void ioReceive();
     method public void ioSend();
     method public deprecated synchronized void resize(int);
+    method public void setAutoPadding(boolean);
     method public void setFromFieldPacker(int, android.renderscript.FieldPacker);
     method public void setFromFieldPacker(int, int, android.renderscript.FieldPacker);
+    method public void setFromFieldPacker(int, int, int, int, android.renderscript.FieldPacker);
     method public void setOnBufferAvailableListener(android.renderscript.Allocation.OnBufferAvailableListener);
     method public void setSurface(android.view.Surface);
     method public void syncAll(int);
@@ -28609,6 +28944,10 @@
     method public static android.renderscript.Element A_8(android.renderscript.RenderScript);
     method public static android.renderscript.Element BOOLEAN(android.renderscript.RenderScript);
     method public static android.renderscript.Element ELEMENT(android.renderscript.RenderScript);
+    method public static android.renderscript.Element F16(android.renderscript.RenderScript);
+    method public static android.renderscript.Element F16_2(android.renderscript.RenderScript);
+    method public static android.renderscript.Element F16_3(android.renderscript.RenderScript);
+    method public static android.renderscript.Element F16_4(android.renderscript.RenderScript);
     method public static android.renderscript.Element F32(android.renderscript.RenderScript);
     method public static android.renderscript.Element F32_2(android.renderscript.RenderScript);
     method public static android.renderscript.Element F32_3(android.renderscript.RenderScript);
@@ -28707,6 +29046,7 @@
     method public static android.renderscript.Element.DataType valueOf(java.lang.String);
     method public static final android.renderscript.Element.DataType[] values();
     enum_constant public static final android.renderscript.Element.DataType BOOLEAN;
+    enum_constant public static final android.renderscript.Element.DataType FLOAT_16;
     enum_constant public static final android.renderscript.Element.DataType FLOAT_32;
     enum_constant public static final android.renderscript.Element.DataType FLOAT_64;
     enum_constant public static final android.renderscript.Element.DataType MATRIX_2X2;
@@ -28980,11 +29320,14 @@
     method public static android.renderscript.RenderScript create(android.content.Context);
     method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType);
     method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType, int);
+    method public static android.renderscript.RenderScript createMultiContext(android.content.Context, android.renderscript.RenderScript.ContextType, int, int);
     method public void destroy();
     method public void finish();
     method public final android.content.Context getApplicationContext();
     method public android.renderscript.RenderScript.RSErrorHandler getErrorHandler();
     method public android.renderscript.RenderScript.RSMessageHandler getMessageHandler();
+    method public static long getMinorID();
+    method public static void releaseAllContexts();
     method public void sendMessage(int, int[]);
     method public void setErrorHandler(android.renderscript.RenderScript.RSErrorHandler);
     method public void setMessageHandler(android.renderscript.RenderScript.RSMessageHandler);
@@ -29066,9 +29409,12 @@
   public class Script extends android.renderscript.BaseObj {
     method public void bindAllocation(android.renderscript.Allocation, int);
     method protected android.renderscript.Script.FieldID createFieldID(int, android.renderscript.Element);
+    method protected android.renderscript.Script.InvokeID createInvokeID(int);
     method protected android.renderscript.Script.KernelID createKernelID(int, int, android.renderscript.Element, android.renderscript.Element);
     method protected void forEach(int, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.FieldPacker);
     method protected void forEach(int, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.FieldPacker, android.renderscript.Script.LaunchOptions);
+    method protected void forEach(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.FieldPacker);
+    method protected void forEach(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.FieldPacker, android.renderscript.Script.LaunchOptions);
     method public boolean getVarB(int);
     method public double getVarD(int);
     method public float getVarF(int);
@@ -29106,6 +29452,9 @@
   public static final class Script.FieldID extends android.renderscript.BaseObj {
   }
 
+  public static final class Script.InvokeID extends android.renderscript.BaseObj {
+  }
+
   public static final class Script.KernelID extends android.renderscript.BaseObj {
   }
 
@@ -29143,6 +29492,41 @@
     method public android.renderscript.ScriptGroup create();
   }
 
+  public class ScriptGroup2 extends android.renderscript.BaseObj {
+    ctor public ScriptGroup2(long, android.renderscript.RenderScript);
+    method public java.lang.Object[] execute(java.lang.Object...);
+  }
+
+  public static final class ScriptGroup2.Binding {
+    ctor public ScriptGroup2.Binding(android.renderscript.Script.FieldID, java.lang.Object);
+    method public android.renderscript.Script.FieldID getField();
+    method public java.lang.Object getValue();
+  }
+
+  public static final class ScriptGroup2.Builder {
+    ctor public ScriptGroup2.Builder(android.renderscript.RenderScript);
+    method public android.renderscript.ScriptGroup2.UnboundValue addInput();
+    method public android.renderscript.ScriptGroup2.Closure addInvoke(android.renderscript.Script.InvokeID, java.lang.Object[], java.util.Map<android.renderscript.Script.FieldID, java.lang.Object>);
+    method public android.renderscript.ScriptGroup2.Closure addInvoke(android.renderscript.Script.InvokeID, java.lang.Object...);
+    method public android.renderscript.ScriptGroup2.Closure addKernel(android.renderscript.Script.KernelID, android.renderscript.Type, java.lang.Object[], java.util.Map<android.renderscript.Script.FieldID, java.lang.Object>);
+    method public android.renderscript.ScriptGroup2.Closure addKernel(android.renderscript.Script.KernelID, android.renderscript.Type, java.lang.Object...);
+    method public android.renderscript.ScriptGroup2 create(android.renderscript.ScriptGroup2.Future...);
+  }
+
+  public static class ScriptGroup2.Closure extends android.renderscript.BaseObj {
+    ctor public ScriptGroup2.Closure(long, android.renderscript.RenderScript);
+    ctor public ScriptGroup2.Closure(android.renderscript.RenderScript, android.renderscript.Script.KernelID, android.renderscript.Type, java.lang.Object[], java.util.Map<android.renderscript.Script.FieldID, java.lang.Object>);
+    ctor public ScriptGroup2.Closure(android.renderscript.RenderScript, android.renderscript.Script.InvokeID, java.lang.Object[], java.util.Map<android.renderscript.Script.FieldID, java.lang.Object>);
+    method public android.renderscript.ScriptGroup2.Future getGlobal(android.renderscript.Script.FieldID);
+    method public android.renderscript.ScriptGroup2.Future getReturn();
+  }
+
+  public static class ScriptGroup2.Future {
+  }
+
+  public static class ScriptGroup2.UnboundValue {
+  }
+
   public abstract class ScriptIntrinsic extends android.renderscript.Script {
   }
 
@@ -29316,6 +29700,8 @@
     method public static android.renderscript.Type createX(android.renderscript.RenderScript, android.renderscript.Element, int);
     method public static android.renderscript.Type createXY(android.renderscript.RenderScript, android.renderscript.Element, int, int);
     method public static android.renderscript.Type createXYZ(android.renderscript.RenderScript, android.renderscript.Element, int, int, int);
+    method public int getArray(int);
+    method public int getArrayCount();
     method public int getCount();
     method public android.renderscript.Element getElement();
     method public int getX();
@@ -29329,6 +29715,7 @@
   public static class Type.Builder {
     ctor public Type.Builder(android.renderscript.RenderScript, android.renderscript.Element);
     method public android.renderscript.Type create();
+    method public android.renderscript.Type.Builder setArray(int, int);
     method public android.renderscript.Type.Builder setFaces(boolean);
     method public android.renderscript.Type.Builder setMipmaps(boolean);
     method public android.renderscript.Type.Builder setX(int);
@@ -29458,6 +29845,11 @@
     method public android.security.KeyStoreParameter.Builder setEncryptionRequired(boolean);
   }
 
+  public class NetworkSecurityPolicy {
+    method public static android.security.NetworkSecurityPolicy getInstance();
+    method public boolean isCleartextTrafficPermitted();
+  }
+
 }
 
 package android.service.carrier {
@@ -32037,6 +32429,7 @@
     method public int[] supplyPukReportResult(java.lang.String, java.lang.String);
     method public void toggleRadioOnOff();
     method public void updateServiceLocation();
+    field public static final java.lang.String ACTION_EMERGENCY_ASSISTANCE = "android.telephony.action.EMERGENCY_ASSISTANCE";
     field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
     field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
     field public static final int CALL_STATE_IDLE = 0; // 0x0
@@ -32533,6 +32926,7 @@
     method public int checkCallingPermission(java.lang.String);
     method public int checkCallingUriPermission(android.net.Uri, int);
     method public int checkPermission(java.lang.String, int, int);
+    method public int checkSelfPermission(java.lang.String);
     method public int checkUriPermission(android.net.Uri, int, int, int);
     method public int checkUriPermission(android.net.Uri, java.lang.String, java.lang.String, int, int, int);
     method public void clearWallpaper();
@@ -32707,6 +33101,7 @@
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
     method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public java.lang.String getNameForUid(int);
@@ -32732,6 +33127,7 @@
     method public android.graphics.drawable.Drawable getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle);
     method public java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
+    method public void grantPermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public boolean hasSystemFeature(java.lang.String);
     method public boolean isSafeMode();
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int);
@@ -32747,6 +33143,7 @@
     method public android.content.pm.ResolveInfo resolveActivity(android.content.Intent, int);
     method public android.content.pm.ProviderInfo resolveContentProvider(java.lang.String, int);
     method public android.content.pm.ResolveInfo resolveService(android.content.Intent, int);
+    method public void revokePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public void setApplicationEnabledSetting(java.lang.String, int, int);
     method public void setComponentEnabledSetting(android.content.ComponentName, int, int);
     method public void setInstallerPackageName(java.lang.String, java.lang.String);
@@ -33071,6 +33468,9 @@
     method public final void increaseWidthTo(int);
     method public boolean isRtlCharAt(int);
     method protected final boolean isSpanned();
+    field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
+    field public static final int BREAK_STRATEGY_HIGH_QUALITY = 1; // 0x1
+    field public static final int BREAK_STRATEGY_SIMPLE = 0; // 0x0
     field public static final int DIR_LEFT_TO_RIGHT = 1; // 0x1
     field public static final int DIR_RIGHT_TO_LEFT = -1; // 0xffffffff
   }
@@ -36485,8 +36885,10 @@
     method public long getTimeDelta();
     method public boolean isInProgress();
     method public boolean isQuickScaleEnabled();
+    method public boolean isSecondaryButtonScaleEnabled();
     method public boolean onTouchEvent(android.view.MotionEvent);
     method public void setQuickScaleEnabled(boolean);
+    method public void setSecondaryButtonScaleEnabled(boolean);
   }
 
   public static abstract interface ScaleGestureDetector.OnScaleGestureListener {
@@ -36707,6 +37109,7 @@
     method public boolean dispatchNestedPreScroll(int, int, int[], int[]);
     method public boolean dispatchNestedScroll(int, int, int, int, int[]);
     method public boolean dispatchPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+    method public void dispatchProvideAssistStructure(android.view.ViewAssistStructure);
     method protected void dispatchRestoreInstanceState(android.util.SparseArray<android.os.Parcelable>);
     method protected void dispatchSaveInstanceState(android.util.SparseArray<android.os.Parcelable>);
     method protected void dispatchSetActivated(boolean);
@@ -36767,6 +37170,10 @@
     method public boolean getFitsSystemWindows();
     method public java.util.ArrayList<android.view.View> getFocusables(int);
     method public void getFocusedRect(android.graphics.Rect);
+    method public android.graphics.drawable.Drawable getForeground();
+    method public int getForegroundGravity();
+    method public android.content.res.ColorStateList getForegroundTintList();
+    method public android.graphics.PorterDuff.Mode getForegroundTintMode();
     method public boolean getGlobalVisibleRect(android.graphics.Rect, android.graphics.Point);
     method public final boolean getGlobalVisibleRect(android.graphics.Rect);
     method public android.os.Handler getHandler();
@@ -36938,6 +37345,7 @@
     method protected void onDisplayHint(int);
     method public boolean onDragEvent(android.view.DragEvent);
     method protected void onDraw(android.graphics.Canvas);
+    method public void onDrawForeground(android.graphics.Canvas);
     method protected final void onDrawScrollBars(android.graphics.Canvas);
     method public boolean onFilterTouchEventForSecurity(android.view.MotionEvent);
     method protected void onFinishInflate();
@@ -36958,7 +37366,8 @@
     method protected void onMeasure(int, int);
     method protected void onOverScrolled(int, int, boolean, boolean);
     method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
-    method public void onProvideAssistStructure(android.view.ViewAssistStructure, android.os.Bundle);
+    method public void onProvideAssistStructure(android.view.ViewAssistStructure);
+    method public void onProvideVirtualAssistStructure(android.view.ViewAssistStructure);
     method protected void onRestoreInstanceState(android.os.Parcelable);
     method public void onRtlPropertiesChanged(int);
     method protected android.os.Parcelable onSaveInstanceState();
@@ -37043,6 +37452,10 @@
     method public void setFitsSystemWindows(boolean);
     method public void setFocusable(boolean);
     method public void setFocusableInTouchMode(boolean);
+    method public void setForeground(android.graphics.drawable.Drawable);
+    method public void setForegroundGravity(int);
+    method public void setForegroundTintList(android.content.res.ColorStateList);
+    method public void setForegroundTintMode(android.graphics.PorterDuff.Mode);
     method public void setHapticFeedbackEnabled(boolean);
     method public void setHasTransientState(boolean);
     method public void setHorizontalFadingEdgeEnabled(boolean);
@@ -37364,14 +37777,34 @@
 
   public abstract class ViewAssistStructure {
     ctor public ViewAssistStructure();
+    method public abstract void clearExtras();
+    method public abstract android.os.Bundle editExtras();
+    method public abstract int getChildCount();
     method public abstract java.lang.CharSequence getHint();
     method public abstract java.lang.CharSequence getText();
     method public abstract int getTextSelectionEnd();
     method public abstract int getTextSelectionStart();
+    method public abstract android.view.ViewAssistStructure newChild(int);
+    method public abstract void setAccessibilityFocused(boolean);
+    method public abstract void setActivated(boolean);
+    method public abstract void setCheckable(boolean);
+    method public abstract void setChecked(boolean);
+    method public abstract void setChildCount(int);
+    method public abstract void setClassName(java.lang.String);
+    method public abstract void setClickable(boolean);
+    method public abstract void setContentDescription(java.lang.CharSequence);
+    method public abstract void setDimens(int, int, int, int, int, int);
+    method public abstract void setEnabled(boolean);
+    method public abstract void setFocusable(boolean);
+    method public abstract void setFocused(boolean);
     method public abstract void setHint(java.lang.CharSequence);
+    method public abstract void setId(int, java.lang.String, java.lang.String, java.lang.String);
+    method public abstract void setLongClickable(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);
     method public abstract void setTextPaint(android.text.TextPaint);
+    method public abstract void setVisibility(int);
   }
 
   public class ViewConfiguration {
@@ -38467,6 +38900,7 @@
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SELECT;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_SELECTION;
     field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SET_TEXT;
+    field public static final android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction ACTION_SHOW_ON_SCREEN;
   }
 
   public static final class AccessibilityNodeInfo.CollectionInfo {
@@ -40111,7 +40545,6 @@
     method public boolean shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String);
     field public static final int ERROR_AUTHENTICATION = -4; // 0xfffffffc
     field public static final int ERROR_BAD_URL = -12; // 0xfffffff4
-    field public static final int ERROR_BLOCKED = -16; // 0xfffffff0
     field public static final int ERROR_CONNECT = -6; // 0xfffffffa
     field public static final int ERROR_FAILED_SSL_HANDSHAKE = -11; // 0xfffffff5
     field public static final int ERROR_FILE = -13; // 0xfffffff3
@@ -40669,7 +41102,7 @@
     field protected android.database.Cursor mDataCursor;
   }
 
-  public class AnalogClock extends android.view.View {
+  public deprecated class AnalogClock extends android.view.View {
     ctor public AnalogClock(android.content.Context);
     ctor public AnalogClock(android.content.Context, android.util.AttributeSet);
     ctor public AnalogClock(android.content.Context, android.util.AttributeSet, int);
@@ -40812,34 +41245,34 @@
     method public long getDate();
     method public int getDateTextAppearance();
     method public int getFirstDayOfWeek();
-    method public int getFocusedMonthDateColor();
+    method public deprecated int getFocusedMonthDateColor();
     method public long getMaxDate();
     method public long getMinDate();
-    method public android.graphics.drawable.Drawable getSelectedDateVerticalBar();
-    method public int getSelectedWeekBackgroundColor();
+    method public deprecated android.graphics.drawable.Drawable getSelectedDateVerticalBar();
+    method public deprecated int getSelectedWeekBackgroundColor();
     method public boolean getShowWeekNumber();
-    method public int getShownWeekCount();
-    method public int getUnfocusedMonthDateColor();
+    method public deprecated int getShownWeekCount();
+    method public deprecated int getUnfocusedMonthDateColor();
     method public int getWeekDayTextAppearance();
-    method public int getWeekNumberColor();
-    method public int getWeekSeparatorLineColor();
+    method public deprecated int getWeekNumberColor();
+    method public deprecated int getWeekSeparatorLineColor();
     method public void setDate(long);
     method public void setDate(long, boolean, boolean);
     method public void setDateTextAppearance(int);
     method public void setFirstDayOfWeek(int);
-    method public void setFocusedMonthDateColor(int);
+    method public deprecated void setFocusedMonthDateColor(int);
     method public void setMaxDate(long);
     method public void setMinDate(long);
     method public void setOnDateChangeListener(android.widget.CalendarView.OnDateChangeListener);
-    method public void setSelectedDateVerticalBar(int);
-    method public void setSelectedDateVerticalBar(android.graphics.drawable.Drawable);
-    method public void setSelectedWeekBackgroundColor(int);
+    method public deprecated void setSelectedDateVerticalBar(int);
+    method public deprecated void setSelectedDateVerticalBar(android.graphics.drawable.Drawable);
+    method public deprecated void setSelectedWeekBackgroundColor(int);
     method public void setShowWeekNumber(boolean);
-    method public void setShownWeekCount(int);
-    method public void setUnfocusedMonthDateColor(int);
+    method public deprecated void setShownWeekCount(int);
+    method public deprecated void setUnfocusedMonthDateColor(int);
     method public void setWeekDayTextAppearance(int);
-    method public void setWeekNumberColor(int);
-    method public void setWeekSeparatorLineColor(int);
+    method public deprecated void setWeekNumberColor(int);
+    method public deprecated void setWeekSeparatorLineColor(int);
   }
 
   public static abstract interface CalendarView.OnDateChangeListener {
@@ -41172,16 +41605,8 @@
     ctor public FrameLayout(android.content.Context, android.util.AttributeSet, int);
     ctor public FrameLayout(android.content.Context, android.util.AttributeSet, int, int);
     method public deprecated boolean getConsiderGoneChildrenWhenMeasuring();
-    method public android.graphics.drawable.Drawable getForeground();
-    method public int getForegroundGravity();
-    method public android.content.res.ColorStateList getForegroundTintList();
-    method public android.graphics.PorterDuff.Mode getForegroundTintMode();
     method public boolean getMeasureAllChildren();
     method protected void onLayout(boolean, int, int, int, int);
-    method public void setForeground(android.graphics.drawable.Drawable);
-    method public void setForegroundGravity(int);
-    method public void setForegroundTintList(android.content.res.ColorStateList);
-    method public void setForegroundTintMode(android.graphics.PorterDuff.Mode);
     method public void setMeasureAllChildren(boolean);
   }
 
@@ -41772,7 +42197,7 @@
     method public void setTouchInterceptor(android.view.View.OnTouchListener);
     method public void setTouchable(boolean);
     method public void setWidth(int);
-    method public void setWindowLayoutMode(int, int);
+    method public deprecated void setWindowLayoutMode(int, int);
     method public void showAsDropDown(android.view.View);
     method public void showAsDropDown(android.view.View, int, int);
     method public void showAsDropDown(android.view.View, int, int, int);
@@ -42531,6 +42956,7 @@
     method public void endBatchEdit();
     method public boolean extractText(android.view.inputmethod.ExtractedTextRequest, android.view.inputmethod.ExtractedText);
     method public final int getAutoLinkMask();
+    method public int getBreakStrategy();
     method public int getCompoundDrawablePadding();
     method public android.content.res.ColorStateList getCompoundDrawableTintList();
     method public android.graphics.PorterDuff.Mode getCompoundDrawableTintMode();
@@ -42632,6 +43058,7 @@
     method public void removeTextChangedListener(android.text.TextWatcher);
     method public void setAllCaps(boolean);
     method public final void setAutoLinkMask(int);
+    method public void setBreakStrategy(int);
     method public void setCompoundDrawablePadding(int);
     method public void setCompoundDrawableTintList(android.content.res.ColorStateList);
     method public void setCompoundDrawableTintMode(android.graphics.PorterDuff.Mode);
@@ -42738,12 +43165,16 @@
     ctor public TimePicker(android.content.Context, android.util.AttributeSet);
     ctor public TimePicker(android.content.Context, android.util.AttributeSet, int);
     ctor public TimePicker(android.content.Context, android.util.AttributeSet, int, int);
-    method public java.lang.Integer getCurrentHour();
-    method public java.lang.Integer getCurrentMinute();
+    method public deprecated java.lang.Integer getCurrentHour();
+    method public deprecated java.lang.Integer getCurrentMinute();
+    method public int getHour();
+    method public int getMinute();
     method public boolean is24HourView();
-    method public void setCurrentHour(java.lang.Integer);
-    method public void setCurrentMinute(java.lang.Integer);
+    method public deprecated void setCurrentHour(java.lang.Integer);
+    method public deprecated void setCurrentMinute(java.lang.Integer);
+    method public void setHour(int);
     method public void setIs24HourView(java.lang.Boolean);
+    method public void setMinute(int);
     method public void setOnTimeChangedListener(android.widget.TimePicker.OnTimeChangedListener);
   }
 
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 1b209a9..c2b9d3e 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -1,3 +1,11 @@
+package android.content.pm {
+
+  public class PackageInfo implements android.os.Parcelable {
+    field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
+  }
+
+}
+
 package android.media {
 
   public class AudioFormat {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 29ba1d7..0a53371 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -112,7 +112,7 @@
                 "       am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" +
                 "               [--user <USER_ID> | current]\n" +
                 "               [--no-window-animation] [--abi <ABI>] <COMPONENT>\n" +
-                "       am profile start [--user <USER_ID> current] <PROCESS> <FILE>\n" +
+                "       am profile start [--user <USER_ID> current] [--sampling INTERVAL] <PROCESS> <FILE>\n" +
                 "       am profile stop [--user <USER_ID> current] [<PROCESS>]\n" +
                 "       am dumpheap [--user <USER_ID> current] [-n] <PROCESS> <FILE>\n" +
                 "       am set-debug-app [-w] [--persistent] <PACKAGE>\n" +
@@ -1031,6 +1031,7 @@
         boolean wall = false;
         int userId = UserHandle.USER_CURRENT;
         int profileType = 0;
+        mSamplingInterval = 0;
 
         String process = null;
 
@@ -1044,6 +1045,8 @@
                     userId = parseUserArg(nextArgRequired());
                 } else if (opt.equals("--wall")) {
                     wall = true;
+                } else if (opt.equals("--sampling")) {
+                    mSamplingInterval = Integer.parseInt(nextArgRequired());
                 } else {
                     System.err.println("Error: Unknown option: " + opt);
                     return;
@@ -1093,7 +1096,7 @@
                 System.err.println("Consider using a file under /data/local/tmp/");
                 return;
             }
-            profilerInfo = new ProfilerInfo(profileFile, fd, 0, false);
+            profilerInfo = new ProfilerInfo(profileFile, fd, mSamplingInterval, false);
         }
 
         try {
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index c86fd53..c5af992 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -7,6 +7,12 @@
 
 #define LOG_TAG "appproc"
 
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
 #include <binder/IPCThreadState.h>
 #include <binder/ProcessState.h>
 #include <utils/Log.h>
@@ -17,11 +23,6 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <private/android_filesystem_config.h>  // for AID_SYSTEM
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/prctl.h>
-
 namespace android {
 
 static void app_usage()
diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp
index 197e36b..84158d3 100644
--- a/cmds/idmap/scan.cpp
+++ b/cmds/idmap/scan.cpp
@@ -1,3 +1,6 @@
+#include <dirent.h>
+#include <sys/stat.h>
+
 #include "idmap.h"
 
 #include <UniquePtr.h>
@@ -9,8 +12,6 @@
 #include <utils/String16.h>
 #include <utils/String8.h>
 
-#include <dirent.h>
-
 #define NO_OVERLAY_TAG (-1000)
 
 using namespace android;
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index c48a618..89dd079 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -892,6 +892,8 @@
                 installFlags |= PackageManager.INSTALL_INTERNAL;
             } else if (opt.equals("-d")) {
                 installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
+            } else if (opt.equals("-g")) {
+                installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
             } else if (opt.equals("--originating-uri")) {
                 originatingUriString = nextOptionData();
                 if (originatingUriString == null) {
@@ -1517,6 +1519,15 @@
     }
 
     private int runGrantRevokePermission(boolean grant) {
+        int userId = UserHandle.USER_CURRENT;
+
+        String opt = null;
+        while ((opt = nextOption()) != null) {
+            if (opt.equals("--user")) {
+                userId = Integer.parseInt(nextArg());
+            }
+        }
+
         String pkg = nextArg();
         if (pkg == null) {
             System.err.println("Error: no package specified");
@@ -1529,11 +1540,12 @@
             showUsage();
             return 1;
         }
+
         try {
             if (grant) {
-                mPm.grantPermission(pkg, perm);
+                mPm.grantPermission(pkg, perm, userId);
             } else {
-                mPm.revokePermission(pkg, perm);
+                mPm.revokePermission(pkg, perm, userId);
             }
             return 0;
         } catch (RemoteException e) {
@@ -1815,8 +1827,8 @@
         System.err.println("       pm disable-until-used [--user USER_ID] PACKAGE_OR_COMPONENT");
         System.err.println("       pm hide [--user USER_ID] PACKAGE_OR_COMPONENT");
         System.err.println("       pm unhide [--user USER_ID] PACKAGE_OR_COMPONENT");
-        System.err.println("       pm grant PACKAGE PERMISSION");
-        System.err.println("       pm revoke PACKAGE PERMISSION");
+        System.err.println("       pm grant [--user USER_ID] PACKAGE PERMISSION");
+        System.err.println("       pm revoke [--user USER_ID] PACKAGE PERMISSION");
         System.err.println("       pm set-install-location [0/auto] [1/internal] [2/external]");
         System.err.println("       pm get-install-location");
         System.err.println("       pm set-permission-enforced PERMISSION [true|false]");
@@ -1868,6 +1880,7 @@
         System.err.println("    -f: install application on internal flash");
         System.err.println("    -d: allow version code downgrade");
         System.err.println("    -p: partial application install");
+        System.err.println("    -g: grant all runtime permissions");
         System.err.println("    -S: size in bytes of entire session");
         System.err.println("");
         System.err.println("pm install-write: write a package into existing session; path may");
@@ -1889,8 +1902,9 @@
         System.err.println("  as \"package/class\").");
         System.err.println("");
         System.err.println("pm grant, revoke: these commands either grant or revoke permissions");
-        System.err.println("  to applications.  Only optional permissions the application has");
-        System.err.println("  declared can be granted or revoked.");
+        System.err.println("    to apps. The permissions must be declared as used in the app's");
+        System.err.println("    manifest, be runtime permissions (protection level dangerous),");
+        System.err.println("    and the app targeting SDK greater than Lollipop MR1.");
         System.err.println("");
         System.err.println("pm get-install-location: returns the current install location.");
         System.err.println("    0 [auto]: Let system decide the best location");
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 6957435c..480d171 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -203,6 +203,14 @@
     public static final String KEY_USERDATA = "userdata";
 
     /**
+     * Bundle key used to supply the last time the credentials of the account
+     * were authenticated successfully. Time is specified in milliseconds since
+     * epoch.
+     */
+    public static final String KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH =
+            "lastAuthenticatedTimeMillisEpoch";
+
+    /**
      * Authenticators using 'customTokens' option will also get the UID of the
      * caller
      */
@@ -663,6 +671,31 @@
     }
 
     /**
+     * Informs the system that the account has been authenticated recently. This
+     * recency may be used by other applications to verify the account. This
+     * should be called only when the user has entered correct credentials for
+     * the account.
+     * <p>
+     * It is not safe to call this method from the main thread. As such, call it
+     * from another thread.
+     * <p>
+     * This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#AUTHENTICATE_ACCOUNTS} and should be
+     * called from the account's authenticator.
+     *
+     * @param account The {@link Account} to be updated.
+     */
+    public boolean accountAuthenticated(Account account) {
+        if (account == null)
+            throw new IllegalArgumentException("account is null");
+        try {
+            return mService.accountAuthenticated(account);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
      * Rename the specified {@link Account}.  This is equivalent to removing
      * the existing account and adding a new renamed account with the old
      * account's user data.
@@ -1544,15 +1577,20 @@
      *     with these fields if activity or password was supplied and
      *     the account was successfully verified:
      * <ul>
-     * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created
+     * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account verified
      * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
      * <li> {@link #KEY_BOOLEAN_RESULT} - true to indicate success
      * </ul>
      *
      * If no activity or password was specified, the returned Bundle contains
-     * only {@link #KEY_INTENT} with the {@link Intent} needed to launch the
-     * password prompt.  If an error occurred,
-     * {@link AccountManagerFuture#getResult()} throws:
+     * {@link #KEY_INTENT} with the {@link Intent} needed to launch the
+     * password prompt.
+     * 
+     * <p>Also the returning Bundle may contain {@link
+     * #KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH} indicating the last time the
+     * credential was validated/created.
+     * 
+     * If an error occurred,{@link AccountManagerFuture#getResult()} throws:
      * <ul>
      * <li> {@link AuthenticatorException} if the authenticator failed to respond
      * <li> {@link OperationCanceledException} if the operation was canceled for
@@ -1625,9 +1663,9 @@
      * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
      * </ul>
      *
-     * If no activity was specified, the returned Bundle contains only
+     * If no activity was specified, the returned Bundle contains
      * {@link #KEY_INTENT} with the {@link Intent} needed to launch the
-     * password prompt.  If an error occurred,
+     * password prompt. If an error occurred,
      * {@link AccountManagerFuture#getResult()} throws:
      * <ul>
      * <li> {@link AuthenticatorException} if the authenticator failed to respond
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index aa41161..04b3c88 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -67,6 +67,7 @@
         boolean expectActivityLaunch);
     void confirmCredentialsAsUser(in IAccountManagerResponse response, in Account account,
         in Bundle options, boolean expectActivityLaunch, int userId);
+    boolean accountAuthenticated(in Account account);
     void getAuthTokenLabel(in IAccountManagerResponse response, String accountType,
         String authTokenType);
 
diff --git a/core/java/android/animation/FloatKeyframeSet.java b/core/java/android/animation/FloatKeyframeSet.java
index 56da940..0173079 100644
--- a/core/java/android/animation/FloatKeyframeSet.java
+++ b/core/java/android/animation/FloatKeyframeSet.java
@@ -118,13 +118,14 @@
             FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i);
             if (fraction < nextKeyframe.getFraction()) {
                 final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
-                if (interpolator != null) {
-                    fraction = interpolator.getInterpolation(fraction);
-                }
                 float intervalFraction = (fraction - prevKeyframe.getFraction()) /
                     (nextKeyframe.getFraction() - prevKeyframe.getFraction());
                 float prevValue = prevKeyframe.getFloatValue();
                 float nextValue = nextKeyframe.getFloatValue();
+                // Apply interpolator on the proportional duration.
+                if (interpolator != null) {
+                    intervalFraction = interpolator.getInterpolation(intervalFraction);
+                }
                 return mEvaluator == null ?
                         prevValue + intervalFraction * (nextValue - prevValue) :
                         ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
diff --git a/core/java/android/animation/IntKeyframeSet.java b/core/java/android/animation/IntKeyframeSet.java
index 12a4bf9..73f9af1 100644
--- a/core/java/android/animation/IntKeyframeSet.java
+++ b/core/java/android/animation/IntKeyframeSet.java
@@ -117,13 +117,14 @@
             IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(i);
             if (fraction < nextKeyframe.getFraction()) {
                 final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
-                if (interpolator != null) {
-                    fraction = interpolator.getInterpolation(fraction);
-                }
                 float intervalFraction = (fraction - prevKeyframe.getFraction()) /
                     (nextKeyframe.getFraction() - prevKeyframe.getFraction());
                 int prevValue = prevKeyframe.getIntValue();
                 int nextValue = nextKeyframe.getIntValue();
+                // Apply interpolator on the proportional duration.
+                if (interpolator != null) {
+                    intervalFraction = interpolator.getInterpolation(intervalFraction);
+                }
                 return mEvaluator == null ?
                         prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
                         ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
diff --git a/core/java/android/animation/KeyframeSet.java b/core/java/android/animation/KeyframeSet.java
index c80e162..32edd4d 100644
--- a/core/java/android/animation/KeyframeSet.java
+++ b/core/java/android/animation/KeyframeSet.java
@@ -201,7 +201,6 @@
      * @return The animated value.
      */
     public Object getValue(float fraction) {
-
         // Special-case optimization for the common case of only two keyframes
         if (mNumKeyframes == 2) {
             if (mInterpolator != null) {
@@ -238,12 +237,13 @@
             Keyframe nextKeyframe = mKeyframes.get(i);
             if (fraction < nextKeyframe.getFraction()) {
                 final TimeInterpolator interpolator = nextKeyframe.getInterpolator();
-                if (interpolator != null) {
-                    fraction = interpolator.getInterpolation(fraction);
-                }
                 final float prevFraction = prevKeyframe.getFraction();
                 float intervalFraction = (fraction - prevFraction) /
                     (nextKeyframe.getFraction() - prevFraction);
+                // Apply interpolator on the proportional duration.
+                if (interpolator != null) {
+                    intervalFraction = interpolator.getInterpolation(intervalFraction);
+                }
                 return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
                         nextKeyframe.getValue());
             }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 7fcbe35..b5817df5 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3726,6 +3726,95 @@
     }
 
     /**
+     * Requests permissions to be granted to this application. These permissions
+     * must be requested in your manifest, they should not be granted to your app,
+     * and they should have protection level {@link android.content.pm.PermissionInfo
+     * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by
+     * the platform or a third-party app.
+     * <p>
+     * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
+     * are granted at install time if requested in the manifest. Signature permissions
+     * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at
+     * install time if requested in the manifest and the signature of your app matches
+     * the signature of the app declaring the permissions.
+     * </p>
+     * <p>
+     * If your app does not have the requested permissions the user will be presented
+     * with UI for accepting them. After the user has accepted or rejected the
+     * requested permissions you will receive a callback on {@link
+     * #onRequestPermissionsResult(int, String[], int[])} reporting whether the
+     * permissions were granted or not.
+     * </p>
+     * <p>
+     * Note that requesting a permission does not guarantee it will be granted and
+     * your app should be able to run without having this permission.
+     * </p>
+     * <p>
+     * This method may start an activity allowing the user to choose which permissions
+     * to grant and which to reject. Hence, you should be prepared that your activity
+     * may be paused and resumed. Further, granting some permissions may require
+     * a restart of you application. In such a case, the system will recreate the
+     * activity stack before delivering the result to {@link
+     * #onRequestPermissionsResult(int, String[], int[])}.
+     * </p>
+     * <p>
+     * When checking whether you have a permission you should use {@link
+     * #checkSelfPermission(String)}.
+     * </p>
+     * <p>
+     * A sample permissions request looks like this:
+     * </p>
+     * <code><pre><p>
+     * private void showContacts() {
+     *     if (checkSelfPermission(Manifest.permission.READ_CONTACTS)
+     *             != PackageManager.PERMISSION_GRANTED) {
+     *         requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
+     *                 PERMISSIONS_REQUEST_READ_CONTACTS);
+     *     } else {
+     *         doShowContacts();
+     *     }
+     * }
+     *
+     * {@literal @}Override
+     * public void onRequestPermissionsResult(int requestCode, String[] permissions,
+     *         int[] grantResults) {
+     *     if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS
+     *             && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+     *         showContacts();
+     *     }
+     * }
+     * </code></pre></p>
+     *
+     * @param permissions The requested permissions.
+     * @param requestCode Application specific request code to match with a result
+     *    reported to {@link #onRequestPermissionsResult(int, String[], int[])}.
+     *
+     * @see #onRequestPermissionsResult(int, String[], int[])
+     * @see #checkSelfPermission(String)
+     */
+    public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
+        Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
+        startActivityForResult(intent, requestCode);
+    }
+
+    /**
+     * Callback for the result from requesting permissions. This method
+     * is invoked for every call on {@link #requestPermissions(String[], int)}.
+     *
+     * @param requestCode The request code passed in {@link #requestPermissions(String[], int)}.
+     * @param permissions The requested permissions. Never null.
+     * @param grantResults The grant results for the corresponding permissions
+     *     which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
+     *     or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
+     *
+     * @see #requestPermissions(String[], int)
+     */
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+            @NonNull int[] grantResults) {
+        /* callback - no nothing */
+    }
+
+    /**
      * Same as calling {@link #startActivityForResult(Intent, int, Bundle)}
      * with no options.
      *
@@ -6269,11 +6358,19 @@
             + ", resCode=" + resultCode + ", data=" + data);
         mFragments.noteStateNotSaved();
         if (who == null) {
-            onActivityResult(requestCode, resultCode, data);
+            if (isRequestPermissionResult(data)) {
+                dispatchRequestPermissionsResult(requestCode, data);
+            } else {
+                onActivityResult(requestCode, resultCode, data);
+            }
         } else {
             Fragment frag = mFragments.findFragmentByWho(who);
             if (frag != null) {
-                frag.onActivityResult(requestCode, resultCode, data);
+                if (isRequestPermissionResult(data)) {
+                    dispatchRequestPermissionsResultToFragment(requestCode, data, frag);
+                } else {
+                    frag.onActivityResult(requestCode, resultCode, data);
+                }
             }
         }
     }
@@ -6343,4 +6440,26 @@
          */
         public void onTranslucentConversionComplete(boolean drawComplete);
     }
+
+    private void dispatchRequestPermissionsResult(int requestCode, Intent data) {
+        String[] permissions = data.getStringArrayExtra(
+                PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
+        final int[] grantResults = data.getIntArrayExtra(
+                PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS);
+        onRequestPermissionsResult(requestCode, permissions, grantResults);
+    }
+
+    private void dispatchRequestPermissionsResultToFragment(int requestCode, Intent data,
+            Fragment fragement) {
+        String[] permissions = data.getStringArrayExtra(
+                PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
+        final int[] grantResults = data.getIntArrayExtra(
+                PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS);
+        fragement.onRequestPermissionsResult(requestCode, permissions, grantResults);
+    }
+
+    private static boolean isRequestPermissionResult(Intent intent) {
+        return intent != null
+                && PackageManager.ACTION_REQUEST_PERMISSIONS.equals(intent.getAction());
+    }
 }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 29b024ac..d143f8b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2493,7 +2493,8 @@
     public static int checkComponentPermission(String permission, int uid,
             int owningUid, boolean exported) {
         // Root, system server get to do everything.
-        if (uid == 0 || uid == Process.SYSTEM_UID) {
+        final int appId = UserHandle.getAppId(uid);
+        if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
             return PackageManager.PERMISSION_GRANTED;
         }
         // Isolated processes don't get any permissions.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4880db1..81b1583 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -254,18 +254,21 @@
         }
     }
 
+    static final class AcquiringProviderRecord {
+        IActivityManager.ContentProviderHolder holder;
+        boolean acquiring = true;
+        int requests = 1;
+    }
+
     // The lock of mProviderMap protects the following variables.
-    final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
-        = new ArrayMap<ProviderKey, ProviderClientRecord>();
-    final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap
-        = new ArrayMap<IBinder, ProviderRefCount>();
-    final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders
-        = new ArrayMap<IBinder, ProviderClientRecord>();
-    final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
-            = new ArrayMap<ComponentName, ProviderClientRecord>();
+    final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<>();
+    final ArrayMap<ProviderKey, AcquiringProviderRecord> mAcquiringProviderMap = new ArrayMap<>();
+    final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap = new ArrayMap<>();
+    final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders = new ArrayMap<>();
+    final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName = new ArrayMap<>();
 
     final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
-        = new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>();
+            = new ArrayMap<>();
 
     final GcIdler mGcIdler = new GcIdler();
     boolean mGcIdlerScheduled = false;
@@ -345,7 +348,7 @@
         }
     }
 
-    final class ProviderClientRecord {
+    static final class ProviderClientRecord {
         final String[] mNames;
         final IContentProvider mProvider;
         final ContentProvider mLocalProvider;
@@ -4693,22 +4696,57 @@
 
     public final IContentProvider acquireProvider(
             Context c, String auth, int userId, boolean stable) {
-        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
+        final ProviderKey key = new ProviderKey(auth, userId);
+        final IContentProvider provider = acquireExistingProvider(c, key, stable);
         if (provider != null) {
             return provider;
         }
+        AcquiringProviderRecord r;
+        boolean first = false;
+        synchronized (mAcquiringProviderMap) {
+            r = mAcquiringProviderMap.get(key);
+            if (r == null) {
+                r = new AcquiringProviderRecord();
+                mAcquiringProviderMap.put(key, r);
+                first = true;
+            } else {
+                r.requests++;
+            }
+        }
 
-        // There is a possible race here.  Another thread may try to acquire
-        // the same provider at the same time.  When this happens, we want to ensure
-        // that the first one wins.
-        // Note that we cannot hold the lock while acquiring and installing the
-        // provider since it might take a long time to run and it could also potentially
-        // be re-entrant in the case where the provider is in the same process.
         IActivityManager.ContentProviderHolder holder = null;
-        try {
-            holder = ActivityManagerNative.getDefault().getContentProvider(
-                    getApplicationThread(), auth, userId, stable);
-        } catch (RemoteException ex) {
+        if (first) {
+            // Multiple threads may try to acquire the same provider at the same time.
+            // When this happens, we only let the first one really gets provider.
+            // Other threads just wait for its result.
+            // Note that we cannot hold the lock while acquiring and installing the
+            // provider since it might take a long time to run and it could also potentially
+            // be re-entrant in the case where the provider is in the same process.
+            try {
+                holder = ActivityManagerNative.getDefault().getContentProvider(
+                        getApplicationThread(), auth, userId, stable);
+            } catch (RemoteException ex) {
+            }
+            synchronized (r) {
+                r.holder = holder;
+                r.acquiring = false;
+                r.notifyAll();
+            }
+        } else {
+            synchronized (r) {
+                while (r.acquiring) {
+                    try {
+                        r.wait();
+                    } catch (InterruptedException e) {
+                    }
+                }
+                holder = r.holder;
+            }
+        }
+        synchronized (mAcquiringProviderMap) {
+            if (--r.requests == 0) {
+                mAcquiringProviderMap.remove(key);
+            }
         }
         if (holder == null) {
             Slog.e(TAG, "Failed to find provider info for " + auth);
@@ -4792,8 +4830,12 @@
 
     public final IContentProvider acquireExistingProvider(
             Context c, String auth, int userId, boolean stable) {
+        return acquireExistingProvider(c, new ProviderKey(auth, userId), stable);
+    }
+
+    final IContentProvider acquireExistingProvider(
+            Context c, ProviderKey key, boolean stable) {
         synchronized (mProviderMap) {
-            final ProviderKey key = new ProviderKey(auth, userId);
             final ProviderClientRecord pr = mProviderMap.get(key);
             if (pr == null) {
                 return null;
@@ -4804,7 +4846,7 @@
             if (!jBinder.isBinderAlive()) {
                 // The hosting process of the provider has died; we can't
                 // use this one.
-                Log.i(TAG, "Acquiring provider " + auth + " for user " + userId
+                Log.i(TAG, "Acquiring provider " + key.authority + " for user " +  key.userId
                         + ": existing object's process dead");
                 handleUnstableProviderDiedLocked(jBinder, true);
                 return null;
@@ -5126,18 +5168,12 @@
                     if (DEBUG_PROVIDER) {
                         Slog.v(TAG, "installProvider: lost the race, updating ref count");
                     }
-                    // We need to transfer our new reference to the existing
-                    // ref count, releasing the old one...  but only if
-                    // release is needed (that is, it is not running in the
-                    // system process).
+                    // The provider has already been installed, so we need
+                    // to increase reference count to the existing one, but
+                    // only if release is needed (that is, it is not running
+                    // in the system process or local to the process).
                     if (!noReleaseNeeded) {
                         incProviderRefLocked(prc, stable);
-                        try {
-                            ActivityManagerNative.getDefault().removeContentProvider(
-                                    holder.connection, stable);
-                        } catch (RemoteException e) {
-                            //do nothing content provider object is dead any way
-                        }
                     }
                 } else {
                     ProviderClientRecord client = installProviderAuthoritiesLocked(
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index 2cb27b0..eafcdb2 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -350,10 +350,15 @@
             if (activityView != null) {
                 final ActivityViewCallback callback = activityView.mActivityViewCallback;
                 if (callback != null) {
+                    final WeakReference<ActivityViewCallback> callbackRef =
+                            new WeakReference<>(callback);
                     activityView.post(new Runnable() {
                         @Override
                         public void run() {
-                            callback.onAllActivitiesComplete(activityView);
+                            ActivityViewCallback callback = callbackRef.get();
+                            if (callback != null) {
+                                callback.onAllActivitiesComplete(activityView);
+                            }
                         }
                     });
                 }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 9f81670..6ec5457 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -36,6 +36,7 @@
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
+import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.KeySet;
 import android.content.pm.ManifestDigest;
 import android.content.pm.PackageInfo;
@@ -81,7 +82,6 @@
 /*package*/
 final class ApplicationPackageManager extends PackageManager {
     private static final String TAG = "ApplicationPackageManager";
-    private final static boolean DEBUG = false;
     private final static boolean DEBUG_ICONS = false;
 
     // Default flags to use with PackageManager when no flags are given.
@@ -186,8 +186,8 @@
     public int[] getPackageGids(String packageName)
             throws NameNotFoundException {
         try {
-            int[] gids = mPM.getPackageGids(packageName);
-            if (gids == null || gids.length > 0) {
+            int[] gids = mPM.getPackageGids(packageName, mContext.getUserId());
+            if (gids != null) {
                 return gids;
             }
         } catch (RemoteException e) {
@@ -398,7 +398,7 @@
     @Override
     public int checkPermission(String permName, String pkgName) {
         try {
-            return mPM.checkPermission(permName, pkgName);
+            return mPM.checkPermission(permName, pkgName, mContext.getUserId());
         } catch (RemoteException e) {
             throw new RuntimeException("Package manager has died", e);
         }
@@ -432,18 +432,18 @@
     }
 
     @Override
-    public void grantPermission(String packageName, String permissionName) {
+    public void grantPermission(String packageName, String permissionName, UserHandle user) {
         try {
-            mPM.grantPermission(packageName, permissionName);
+            mPM.grantPermission(packageName, permissionName, user.getIdentifier());
         } catch (RemoteException e) {
             throw new RuntimeException("Package manager has died", e);
         }
     }
 
     @Override
-    public void revokePermission(String packageName, String permissionName) {
+    public void revokePermission(String packageName, String permissionName, UserHandle user) {
         try {
-            mPM.revokePermission(packageName, permissionName);
+            mPM.revokePermission(packageName, permissionName, user.getIdentifier());
         } catch (RemoteException e) {
             throw new RuntimeException("Package manager has died", e);
         }
@@ -1310,6 +1310,45 @@
     }
 
     @Override
+    public void verifyIntentFilter(int id, int verificationCode, List<String> outFailedDomains) {
+        try {
+            mPM.verifyIntentFilter(id, verificationCode, outFailedDomains);
+        } catch (RemoteException e) {
+            // Should never happen!
+        }
+    }
+
+    @Override
+    public int getIntentVerificationStatus(String packageName, int userId) {
+        try {
+            return mPM.getIntentVerificationStatus(packageName, userId);
+        } catch (RemoteException e) {
+            // Should never happen!
+            return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+        }
+    }
+
+    @Override
+    public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
+        try {
+            return mPM.updateIntentVerificationStatus(packageName, status, userId);
+        } catch (RemoteException e) {
+            // Should never happen!
+            return false;
+        }
+    }
+
+    @Override
+    public List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) {
+        try {
+            return mPM.getIntentFilterVerifications(packageName);
+        } catch (RemoteException e) {
+            // Should never happen!
+            return null;
+        }
+    }
+
+    @Override
     public void setInstallerPackageName(String targetPackage,
             String installerPackageName) {
         try {
diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java
index 25153fc5..e31c821 100644
--- a/core/java/android/app/AssistStructure.java
+++ b/core/java/android/app/AssistStructure.java
@@ -30,10 +30,8 @@
 import android.util.Log;
 import android.view.View;
 import android.view.ViewAssistStructure;
-import android.view.ViewGroup;
 import android.view.ViewRootImpl;
 import android.view.WindowManagerGlobal;
-import android.widget.Checkable;
 
 import java.util.ArrayList;
 
@@ -53,109 +51,24 @@
 
     final ComponentName mActivityComponent;
 
-    final ArrayList<ViewNodeImpl> mRootViews = new ArrayList<>();
+    final ArrayList<WindowNode> mWindowNodes = new ArrayList<>();
 
-    ViewAssistStructureImpl mTmpViewAssistStructureImpl = new ViewAssistStructureImpl();
-    Bundle mTmpExtras = new Bundle();
+    Rect mTmpRect = new Rect();
 
-    final static class ViewAssistStructureImpl extends ViewAssistStructure {
+    final static class ViewNodeText {
         CharSequence mText;
-        int mTextSelectionStart = -1;
-        int mTextSelectionEnd = -1;
-        int mTextColor = ViewNode.TEXT_COLOR_UNDEFINED;
-        int mTextBackgroundColor = ViewNode.TEXT_COLOR_UNDEFINED;
-        float mTextSize = 0;
-        int mTextStyle = 0;
-        CharSequence mHint;
-
-        @Override
-        public void setText(CharSequence text) {
-            mText = text;
-            mTextSelectionStart = mTextSelectionEnd = -1;
-        }
-
-        @Override
-        public void setText(CharSequence text, int selectionStart, int selectionEnd) {
-            mText = text;
-            mTextSelectionStart = selectionStart;
-            mTextSelectionEnd = selectionEnd;
-        }
-
-        @Override
-        public void setTextPaint(TextPaint paint) {
-            mTextColor = paint.getColor();
-            mTextBackgroundColor = paint.bgColor;
-            mTextSize = paint.getTextSize();
-            mTextStyle = 0;
-            Typeface tf = paint.getTypeface();
-            if (tf != null) {
-                if (tf.isBold()) {
-                    mTextStyle |= ViewNode.TEXT_STYLE_BOLD;
-                }
-                if (tf.isItalic()) {
-                    mTextStyle |= ViewNode.TEXT_STYLE_ITALIC;
-                }
-            }
-            int pflags = paint.getFlags();
-            if ((pflags& Paint.FAKE_BOLD_TEXT_FLAG) != 0) {
-                mTextStyle |= ViewNode.TEXT_STYLE_BOLD;
-            }
-            if ((pflags& Paint.UNDERLINE_TEXT_FLAG) != 0) {
-                mTextStyle |= ViewNode.TEXT_STYLE_UNDERLINE;
-            }
-            if ((pflags& Paint.STRIKE_THRU_TEXT_FLAG) != 0) {
-                mTextStyle |= ViewNode.TEXT_STYLE_STRIKE_THRU;
-            }
-        }
-
-        @Override
-        public void setHint(CharSequence hint) {
-            mHint = hint;
-        }
-
-        @Override
-        public CharSequence getText() {
-            return mText;
-        }
-
-        @Override
-        public int getTextSelectionStart() {
-            return mTextSelectionStart;
-        }
-
-        @Override
-        public int getTextSelectionEnd() {
-            return mTextSelectionEnd;
-        }
-
-        @Override
-        public CharSequence getHint() {
-            return mHint;
-        }
-    }
-
-    final static class ViewNodeTextImpl {
-        final CharSequence mText;
-        final int mTextSelectionStart;
-        final int mTextSelectionEnd;
+        int mTextSelectionStart;
+        int mTextSelectionEnd;
         int mTextColor;
         int mTextBackgroundColor;
         float mTextSize;
         int mTextStyle;
-        final String mHint;
+        String mHint;
 
-        ViewNodeTextImpl(ViewAssistStructureImpl data) {
-            mText = data.mText;
-            mTextSelectionStart = data.mTextSelectionStart;
-            mTextSelectionEnd = data.mTextSelectionEnd;
-            mTextColor = data.mTextColor;
-            mTextBackgroundColor = data.mTextBackgroundColor;
-            mTextSize = data.mTextSize;
-            mTextStyle = data.mTextStyle;
-            mHint = data.mHint != null ? data.mHint.toString() : null;
+        ViewNodeText() {
         }
 
-        ViewNodeTextImpl(Parcel in) {
+        ViewNodeText(Parcel in) {
             mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
             mTextSelectionStart = in.readInt();
             mTextSelectionEnd = in.readInt();
@@ -178,13 +91,99 @@
         }
     }
 
-    final static class ViewNodeImpl {
+    /**
+     * Describes a window in the assist data.
+     */
+    static public class WindowNode {
         final int mX;
         final int mY;
-        final int mScrollX;
-        final int mScrollY;
         final int mWidth;
         final int mHeight;
+        final CharSequence mTitle;
+        final ViewNode mRoot;
+
+        WindowNode(AssistStructure assist, ViewRootImpl root) {
+            View view = root.getView();
+            Rect rect = new Rect();
+            view.getBoundsOnScreen(rect);
+            mX = rect.left - view.getLeft();
+            mY = rect.top - view.getTop();
+            mWidth = rect.width();
+            mHeight = rect.height();
+            mTitle = root.getTitle();
+            mRoot = new ViewNode();
+            ViewNodeBuilder builder = new ViewNodeBuilder(assist, mRoot);
+            view.dispatchProvideAssistStructure(builder);
+        }
+
+        WindowNode(Parcel in, PooledStringReader preader) {
+            mX = in.readInt();
+            mY = in.readInt();
+            mWidth = in.readInt();
+            mHeight = in.readInt();
+            mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            mRoot = new ViewNode(in, preader);
+        }
+
+        void writeToParcel(Parcel out, PooledStringWriter pwriter) {
+            out.writeInt(mX);
+            out.writeInt(mY);
+            out.writeInt(mWidth);
+            out.writeInt(mHeight);
+            TextUtils.writeToParcel(mTitle, out, 0);
+            mRoot.writeToParcel(out, pwriter);
+        }
+
+        public int getLeft() {
+            return mX;
+        }
+
+        public int getTop() {
+            return mY;
+        }
+
+        public int getWidth() {
+            return mWidth;
+        }
+
+        public int getHeight() {
+            return mHeight;
+        }
+
+        public CharSequence getTitle() {
+            return mTitle;
+        }
+
+        public ViewNode getRootViewNode() {
+            return mRoot;
+        }
+    }
+
+    /**
+     * Describes a single view in the assist data.
+     */
+    static public class ViewNode {
+        /**
+         * Magic value for text color that has not been defined, which is very unlikely
+         * to be confused with a real text color.
+         */
+        public static final int TEXT_COLOR_UNDEFINED = 1;
+
+        public static final int TEXT_STYLE_BOLD = 1<<0;
+        public static final int TEXT_STYLE_ITALIC = 1<<1;
+        public static final int TEXT_STYLE_UNDERLINE = 1<<2;
+        public static final int TEXT_STYLE_STRIKE_THRU = 1<<3;
+
+        int mId;
+        String mIdPackage;
+        String mIdType;
+        String mIdEntry;
+        int mX;
+        int mY;
+        int mScrollX;
+        int mScrollY;
+        int mWidth;
+        int mHeight;
 
         static final int FLAGS_DISABLED = 0x00000001;
         static final int FLAGS_VISIBILITY_MASK = View.VISIBLE|View.INVISIBLE|View.GONE;
@@ -198,94 +197,32 @@
         static final int FLAGS_CLICKABLE = 0x00004000;
         static final int FLAGS_LONG_CLICKABLE = 0x00200000;
 
-        final int mFlags;
+        int mFlags;
 
-        final String mClassName;
-        final String mContentDescription;
+        String mClassName;
+        CharSequence mContentDescription;
 
-        final ViewNodeTextImpl mText;
-        final Bundle mExtras;
+        ViewNodeText mText;
+        Bundle mExtras;
 
-        final ViewNodeImpl[] mChildren;
+        ViewNode[] mChildren;
 
-        ViewNodeImpl(AssistStructure assistStructure, View view, int left, int top,
-                CharSequence contentDescription) {
-            mX = left;
-            mY = top;
-            mScrollX = view.getScrollX();
-            mScrollY = view.getScrollY();
-            mWidth = view.getWidth();
-            mHeight = view.getHeight();
-            int flags = view.getVisibility();
-            if (!view.isEnabled()) {
-                flags |= FLAGS_DISABLED;
-            }
-            if (!view.isClickable()) {
-                flags |= FLAGS_CLICKABLE;
-            }
-            if (!view.isFocusable()) {
-                flags |= FLAGS_FOCUSABLE;
-            }
-            if (!view.isFocused()) {
-                flags |= FLAGS_FOCUSED;
-            }
-            if (!view.isAccessibilityFocused()) {
-                flags |= FLAGS_ACCESSIBILITY_FOCUSED;
-            }
-            if (!view.isSelected()) {
-                flags |= FLAGS_SELECTED;
-            }
-            if (!view.isActivated()) {
-                flags |= FLAGS_ACTIVATED;
-            }
-            if (!view.isLongClickable()) {
-                flags |= FLAGS_LONG_CLICKABLE;
-            }
-            if (view instanceof Checkable) {
-                flags |= FLAGS_CHECKABLE;
-                if (((Checkable)view).isChecked()) {
-                    flags |= FLAGS_CHECKED;
-                }
-            }
-            mFlags = flags;
-            mClassName = view.getAccessibilityClassName().toString();
-            mContentDescription = contentDescription != null ? contentDescription.toString() : null;
-            final ViewAssistStructureImpl viewData = assistStructure.mTmpViewAssistStructureImpl;
-            final Bundle extras = assistStructure.mTmpExtras;
-            view.onProvideAssistStructure(viewData, extras);
-            if (viewData.mText != null || viewData.mHint != null) {
-                mText = new ViewNodeTextImpl(viewData);
-                assistStructure.mTmpViewAssistStructureImpl = new ViewAssistStructureImpl();
-            } else {
-                mText = null;
-            }
-            if (!extras.isEmpty()) {
-                mExtras = extras;
-                assistStructure.mTmpExtras = new Bundle();
-            } else {
-                mExtras = null;
-            }
-            if (view instanceof ViewGroup) {
-                ViewGroup vg = (ViewGroup)view;
-                final int NCHILDREN = vg.getChildCount();
-                if (NCHILDREN > 0) {
-                    mChildren = new ViewNodeImpl[NCHILDREN];
-                    for (int i=0; i<NCHILDREN; i++) {
-                        mChildren[i] = new ViewNodeImpl(assistStructure, vg.getChildAt(i));
-                    }
+        ViewNode() {
+        }
+
+        ViewNode(Parcel in, PooledStringReader preader) {
+            mId = in.readInt();
+            if (mId != 0) {
+                mIdEntry = preader.readString();
+                if (mIdEntry != null) {
+                    mIdType = preader.readString();
+                    mIdPackage = preader.readString();
                 } else {
-                    mChildren = null;
+                    mIdPackage = mIdType = null;
                 }
             } else {
-                mChildren = null;
+                mIdPackage = mIdType = mIdEntry = null;
             }
-        }
-
-        ViewNodeImpl(AssistStructure assistStructure, View view) {
-            this(assistStructure, view, view.getLeft(), view.getTop(), view.getContentDescription());
-        }
-
-        ViewNodeImpl(Parcel in, PooledStringReader preader) {
             mX = in.readInt();
             mY = in.readInt();
             mScrollX = in.readInt();
@@ -294,18 +231,18 @@
             mHeight = in.readInt();
             mFlags = in.readInt();
             mClassName = preader.readString();
-            mContentDescription = in.readString();
+            mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
             if (in.readInt() != 0) {
-                mText = new ViewNodeTextImpl(in);
+                mText = new ViewNodeText(in);
             } else {
                 mText = null;
             }
             mExtras = in.readBundle();
             final int NCHILDREN = in.readInt();
             if (NCHILDREN > 0) {
-                mChildren = new ViewNodeImpl[NCHILDREN];
+                mChildren = new ViewNode[NCHILDREN];
                 for (int i=0; i<NCHILDREN; i++) {
-                    mChildren[i] = new ViewNodeImpl(in, preader);
+                    mChildren[i] = new ViewNode(in, preader);
                 }
             } else {
                 mChildren = null;
@@ -313,6 +250,14 @@
         }
 
         void writeToParcel(Parcel out, PooledStringWriter pwriter) {
+            out.writeInt(mId);
+            if (mId != 0) {
+                pwriter.writeString(mIdEntry);
+                if (mIdEntry != null) {
+                    pwriter.writeString(mIdType);
+                    pwriter.writeString(mIdPackage);
+                }
+            }
             out.writeInt(mX);
             out.writeInt(mY);
             out.writeInt(mScrollX);
@@ -321,7 +266,7 @@
             out.writeInt(mHeight);
             out.writeInt(mFlags);
             pwriter.writeString(mClassName);
-            out.writeString(mContentDescription);
+            TextUtils.writeToParcel(mContentDescription, out, 0);
             if (mText != null) {
                 out.writeInt(1);
                 mText.writeToParcel(out);
@@ -339,146 +284,356 @@
                 out.writeInt(0);
             }
         }
-    }
 
-    /**
-     * Provides access to information about a single view in the assist data.
-     */
-    static public class ViewNode {
-        /**
-         * Magic value for text color that has not been defined, which is very unlikely
-         * to be confused with a real text color.
-         */
-        public static final int TEXT_COLOR_UNDEFINED = 1;
+        public int getId() {
+            return mId;
+        }
 
-        public static final int TEXT_STYLE_BOLD = 1<<0;
-        public static final int TEXT_STYLE_ITALIC = 1<<1;
-        public static final int TEXT_STYLE_UNDERLINE = 1<<2;
-        public static final int TEXT_STYLE_STRIKE_THRU = 1<<3;
+        public String getIdPackage() {
+            return mIdPackage;
+        }
 
-        ViewNodeImpl mImpl;
+        public String getIdType() {
+            return mIdType;
+        }
 
-        public ViewNode() {
+        public String getIdEntry() {
+            return mIdEntry;
         }
 
         public int getLeft() {
-            return mImpl.mX;
+            return mX;
         }
 
         public int getTop() {
-            return mImpl.mY;
+            return mY;
         }
 
         public int getScrollX() {
-            return mImpl.mScrollX;
+            return mScrollX;
         }
 
         public int getScrollY() {
-            return mImpl.mScrollY;
+            return mScrollY;
         }
 
         public int getWidth() {
-            return mImpl.mWidth;
+            return mWidth;
         }
 
         public int getHeight() {
-            return mImpl.mHeight;
+            return mHeight;
         }
 
         public int getVisibility() {
-            return mImpl.mFlags&ViewNodeImpl.FLAGS_VISIBILITY_MASK;
+            return mFlags&ViewNode.FLAGS_VISIBILITY_MASK;
         }
 
         public boolean isEnabled() {
-            return (mImpl.mFlags&ViewNodeImpl.FLAGS_DISABLED) == 0;
+            return (mFlags&ViewNode.FLAGS_DISABLED) == 0;
         }
 
         public boolean isClickable() {
-            return (mImpl.mFlags&ViewNodeImpl.FLAGS_CLICKABLE) != 0;
+            return (mFlags&ViewNode.FLAGS_CLICKABLE) != 0;
         }
 
         public boolean isFocusable() {
-            return (mImpl.mFlags&ViewNodeImpl.FLAGS_FOCUSABLE) != 0;
+            return (mFlags&ViewNode.FLAGS_FOCUSABLE) != 0;
         }
 
         public boolean isFocused() {
-            return (mImpl.mFlags&ViewNodeImpl.FLAGS_FOCUSED) != 0;
+            return (mFlags&ViewNode.FLAGS_FOCUSED) != 0;
         }
 
         public boolean isAccessibilityFocused() {
-            return (mImpl.mFlags&ViewNodeImpl.FLAGS_ACCESSIBILITY_FOCUSED) != 0;
+            return (mFlags&ViewNode.FLAGS_ACCESSIBILITY_FOCUSED) != 0;
         }
 
         public boolean isCheckable() {
-            return (mImpl.mFlags&ViewNodeImpl.FLAGS_CHECKABLE) != 0;
+            return (mFlags&ViewNode.FLAGS_CHECKABLE) != 0;
         }
 
         public boolean isChecked() {
-            return (mImpl.mFlags&ViewNodeImpl.FLAGS_CHECKED) != 0;
+            return (mFlags&ViewNode.FLAGS_CHECKED) != 0;
         }
 
         public boolean isSelected() {
-            return (mImpl.mFlags&ViewNodeImpl.FLAGS_SELECTED) != 0;
+            return (mFlags&ViewNode.FLAGS_SELECTED) != 0;
         }
 
         public boolean isActivated() {
-            return (mImpl.mFlags&ViewNodeImpl.FLAGS_ACTIVATED) != 0;
+            return (mFlags&ViewNode.FLAGS_ACTIVATED) != 0;
         }
 
         public boolean isLongClickable() {
-            return (mImpl.mFlags&ViewNodeImpl.FLAGS_LONG_CLICKABLE) != 0;
+            return (mFlags&ViewNode.FLAGS_LONG_CLICKABLE) != 0;
         }
 
         public String getClassName() {
-            return mImpl.mClassName;
+            return mClassName;
         }
 
-        public String getContentDescription() {
-            return mImpl.mContentDescription;
+        public CharSequence getContentDescription() {
+            return mContentDescription;
         }
 
         public CharSequence getText() {
-            return mImpl.mText != null ? mImpl.mText.mText : null;
+            return mText != null ? mText.mText : null;
         }
 
         public int getTextSelectionStart() {
-            return mImpl.mText != null ? mImpl.mText.mTextSelectionStart : -1;
+            return mText != null ? mText.mTextSelectionStart : -1;
         }
 
         public int getTextSelectionEnd() {
-            return mImpl.mText != null ? mImpl.mText.mTextSelectionEnd : -1;
+            return mText != null ? mText.mTextSelectionEnd : -1;
         }
 
         public int getTextColor() {
-            return mImpl.mText != null ? mImpl.mText.mTextColor : TEXT_COLOR_UNDEFINED;
+            return mText != null ? mText.mTextColor : TEXT_COLOR_UNDEFINED;
         }
 
         public int getTextBackgroundColor() {
-            return mImpl.mText != null ? mImpl.mText.mTextBackgroundColor : TEXT_COLOR_UNDEFINED;
+            return mText != null ? mText.mTextBackgroundColor : TEXT_COLOR_UNDEFINED;
         }
 
         public float getTextSize() {
-            return mImpl.mText != null ? mImpl.mText.mTextSize : 0;
+            return mText != null ? mText.mTextSize : 0;
         }
 
         public int getTextStyle() {
-            return mImpl.mText != null ? mImpl.mText.mTextStyle : 0;
+            return mText != null ? mText.mTextStyle : 0;
         }
 
         public String getHint() {
-            return mImpl.mText != null ? mImpl.mText.mHint : null;
+            return mText != null ? mText.mHint : null;
         }
 
         public Bundle getExtras() {
-            return mImpl.mExtras;
+            return mExtras;
         }
 
         public int getChildCount() {
-            return mImpl.mChildren != null ? mImpl.mChildren.length : 0;
+            return mChildren != null ? mChildren.length : 0;
         }
 
-        public void getChildAt(int index, ViewNode outNode) {
-            outNode.mImpl = mImpl.mChildren[index];
+        public ViewNode getChildAt(int index) {
+            return mChildren[index];
+        }
+    }
+
+    static class ViewNodeBuilder extends ViewAssistStructure {
+        final AssistStructure mAssist;
+        final ViewNode mNode;
+
+        ViewNodeBuilder(AssistStructure assist, ViewNode node) {
+            mAssist = assist;
+            mNode = node;
+        }
+
+        @Override
+        public void setId(int id, String packageName, String typeName, String entryName) {
+            mNode.mId = id;
+            mNode.mIdPackage = packageName;
+            mNode.mIdType = typeName;
+            mNode.mIdEntry = entryName;
+        }
+
+        @Override
+        public void setDimens(int left, int top, int scrollX, int scrollY, int width, int height) {
+            mNode.mX = left;
+            mNode.mY = top;
+            mNode.mScrollX = scrollX;
+            mNode.mScrollY = scrollY;
+            mNode.mWidth = width;
+            mNode.mHeight = height;
+        }
+
+        @Override
+        public void setVisibility(int visibility) {
+            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_VISIBILITY_MASK) | visibility;
+        }
+
+        @Override
+        public void setEnabled(boolean state) {
+            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_DISABLED)
+                    | (state ? 0 : ViewNode.FLAGS_DISABLED);
+        }
+
+        @Override
+        public void setClickable(boolean state) {
+            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CLICKABLE)
+                    | (state ? ViewNode.FLAGS_CLICKABLE : 0);
+        }
+
+        @Override
+        public void setLongClickable(boolean state) {
+            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_LONG_CLICKABLE)
+                    | (state ? ViewNode.FLAGS_LONG_CLICKABLE : 0);
+        }
+
+        @Override
+        public void setFocusable(boolean state) {
+            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_FOCUSABLE)
+                    | (state ? ViewNode.FLAGS_FOCUSABLE : 0);
+        }
+
+        @Override
+        public void setFocused(boolean state) {
+            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_FOCUSED)
+                    | (state ? ViewNode.FLAGS_FOCUSED : 0);
+        }
+
+        @Override
+        public void setAccessibilityFocused(boolean state) {
+            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ACCESSIBILITY_FOCUSED)
+                    | (state ? ViewNode.FLAGS_ACCESSIBILITY_FOCUSED : 0);
+        }
+
+        @Override
+        public void setCheckable(boolean state) {
+            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CHECKABLE)
+                    | (state ? ViewNode.FLAGS_CHECKABLE : 0);
+        }
+
+        @Override
+        public void setChecked(boolean state) {
+            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_CHECKED)
+                    | (state ? ViewNode.FLAGS_CHECKED : 0);
+        }
+
+        @Override
+        public void setSelected(boolean state) {
+            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_SELECTED)
+                    | (state ? ViewNode.FLAGS_SELECTED : 0);
+        }
+
+        @Override
+        public void setActivated(boolean state) {
+            mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_ACTIVATED)
+                    | (state ? ViewNode.FLAGS_ACTIVATED : 0);
+        }
+
+        @Override
+        public void setClassName(String className) {
+            mNode.mClassName = className;
+        }
+
+        @Override
+        public void setContentDescription(CharSequence contentDescription) {
+            mNode.mContentDescription = contentDescription;
+        }
+
+        private final ViewNodeText getNodeText() {
+            if (mNode.mText != null) {
+                return mNode.mText;
+            }
+            mNode.mText = new ViewNodeText();
+            return mNode.mText;
+        }
+
+        @Override
+        public void setText(CharSequence text) {
+            ViewNodeText t = getNodeText();
+            t.mText = text;
+            t.mTextSelectionStart = t.mTextSelectionEnd = -1;
+        }
+
+        @Override
+        public void setText(CharSequence text, int selectionStart, int selectionEnd) {
+            ViewNodeText t = getNodeText();
+            t.mText = text;
+            t.mTextSelectionStart = selectionStart;
+            t.mTextSelectionEnd = selectionEnd;
+        }
+
+        @Override
+        public void setTextPaint(TextPaint paint) {
+            ViewNodeText t = getNodeText();
+            t.mTextColor = paint.getColor();
+            t.mTextBackgroundColor = paint.bgColor;
+            t.mTextSize = paint.getTextSize();
+            t.mTextStyle = 0;
+            Typeface tf = paint.getTypeface();
+            if (tf != null) {
+                if (tf.isBold()) {
+                    t.mTextStyle |= ViewNode.TEXT_STYLE_BOLD;
+                }
+                if (tf.isItalic()) {
+                    t.mTextStyle |= ViewNode.TEXT_STYLE_ITALIC;
+                }
+            }
+            int pflags = paint.getFlags();
+            if ((pflags& Paint.FAKE_BOLD_TEXT_FLAG) != 0) {
+                t.mTextStyle |= ViewNode.TEXT_STYLE_BOLD;
+            }
+            if ((pflags& Paint.UNDERLINE_TEXT_FLAG) != 0) {
+                t.mTextStyle |= ViewNode.TEXT_STYLE_UNDERLINE;
+            }
+            if ((pflags& Paint.STRIKE_THRU_TEXT_FLAG) != 0) {
+                t.mTextStyle |= ViewNode.TEXT_STYLE_STRIKE_THRU;
+            }
+        }
+
+        @Override
+        public void setHint(CharSequence hint) {
+            getNodeText().mHint = hint != null ? hint.toString() : null;
+        }
+
+        @Override
+        public CharSequence getText() {
+            return mNode.mText != null ? mNode.mText.mText : null;
+        }
+
+        @Override
+        public int getTextSelectionStart() {
+            return mNode.mText != null ? mNode.mText.mTextSelectionStart : -1;
+        }
+
+        @Override
+        public int getTextSelectionEnd() {
+            return mNode.mText != null ? mNode.mText.mTextSelectionEnd : -1;
+        }
+
+        @Override
+        public CharSequence getHint() {
+            return mNode.mText != null ? mNode.mText.mHint : null;
+        }
+
+        @Override
+        public Bundle editExtras() {
+            if (mNode.mExtras != null) {
+                return mNode.mExtras;
+            }
+            mNode.mExtras = new Bundle();
+            return mNode.mExtras;
+        }
+
+        @Override
+        public void clearExtras() {
+            mNode.mExtras = null;
+        }
+
+        @Override
+        public void setChildCount(int num) {
+            mNode.mChildren = new ViewNode[num];
+        }
+
+        @Override
+        public int getChildCount() {
+            return mNode.mChildren != null ? mNode.mChildren.length : 0;
+        }
+
+        @Override
+        public ViewAssistStructure newChild(int index) {
+            ViewNode node = new ViewNode();
+            mNode.mChildren[index] = node;
+            return new ViewNodeBuilder(mAssist, node);
+        }
+
+        @Override
+        public Rect getTempRect() {
+            return mAssist.mTmpRect;
         }
     }
 
@@ -488,12 +643,7 @@
                 activity.getActivityToken());
         for (int i=0; i<views.size(); i++) {
             ViewRootImpl root = views.get(i);
-            View view = root.getView();
-            Rect rect = new Rect();
-            view.getBoundsOnScreen(rect);
-            CharSequence title = root.getTitle();
-            mRootViews.add(new ViewNodeImpl(this, view, rect.left, rect.top,
-                    title != null ? title : view.getContentDescription()));
+            mWindowNodes.add(new WindowNode(this, root));
         }
     }
 
@@ -502,7 +652,7 @@
         mActivityComponent = ComponentName.readFromParcel(in);
         final int N = in.readInt();
         for (int i=0; i<N; i++) {
-            mRootViews.add(new ViewNodeImpl(in, preader));
+            mWindowNodes.add(new WindowNode(in, preader));
         }
         //dump();
     }
@@ -510,24 +660,37 @@
     /** @hide */
     public void dump() {
         Log.i(TAG, "Activity: " + mActivityComponent.flattenToShortString());
-        ViewNode node = new ViewNode();
-        final int N = getWindowCount();
+        final int N = getWindowNodeCount();
         for (int i=0; i<N; i++) {
-            Log.i(TAG, "Window #" + i + ":");
-            getWindowAt(i, node);
-            dump("  ", node);
+            WindowNode node = getWindowNodeAt(i);
+            Log.i(TAG, "Window #" + i + " [" + node.getLeft() + "," + node.getTop()
+                    + " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getTitle());
+            dump("  ", node.getRootViewNode());
         }
     }
 
     void dump(String prefix, ViewNode node) {
         Log.i(TAG, prefix + "View [" + node.getLeft() + "," + node.getTop()
                 + " " + node.getWidth() + "x" + node.getHeight() + "]" + " " + node.getClassName());
+        int id = node.getId();
+        if (id != 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(prefix); sb.append("  ID: #"); sb.append(Integer.toHexString(id));
+            String entry = node.getIdEntry();
+            if (entry != null) {
+                String type = node.getIdType();
+                String pkg = node.getIdPackage();
+                sb.append(" "); sb.append(pkg); sb.append(":"); sb.append(type);
+                sb.append("/"); sb.append(entry);
+            }
+            Log.i(TAG, sb.toString());
+        }
         int scrollX = node.getScrollX();
         int scrollY = node.getScrollY();
         if (scrollX != 0 || scrollY != 0) {
             Log.i(TAG, prefix + "  Scroll: " + scrollX + "," + scrollY);
         }
-        String contentDescription = node.getContentDescription();
+        CharSequence contentDescription = node.getContentDescription();
         if (contentDescription != null) {
             Log.i(TAG, prefix + "  Content description: " + contentDescription);
         }
@@ -552,9 +715,8 @@
         if (NCHILDREN > 0) {
             Log.i(TAG, prefix + "  Children:");
             String cprefix = prefix + "    ";
-            ViewNode cnode = new ViewNode();
             for (int i=0; i<NCHILDREN; i++) {
-                node.getChildAt(i, cnode);
+                ViewNode cnode = node.getChildAt(i);
                 dump(cprefix, cnode);
             }
         }
@@ -575,17 +737,16 @@
     /**
      * Return the number of window contents that have been collected in this assist data.
      */
-    public int getWindowCount() {
-        return mRootViews.size();
+    public int getWindowNodeCount() {
+        return mWindowNodes.size();
     }
 
     /**
-     * Return the root view for one of the windows in the assist data.
-     * @param index Which window to retrieve, may be 0 to {@link #getWindowCount()}-1.
-     * @param outNode Node in which to place the window's root view.
+     * Return one of the windows in the assist data.
+     * @param index Which window to retrieve, may be 0 to {@link #getWindowNodeCount()}-1.
      */
-    public void getWindowAt(int index, ViewNode outNode) {
-        outNode.mImpl = mRootViews.get(index);
+    public WindowNode getWindowNodeAt(int index) {
+        return mWindowNodes.get(index);
     }
 
     public int describeContents() {
@@ -596,10 +757,10 @@
         int start = out.dataPosition();
         PooledStringWriter pwriter = new PooledStringWriter(out);
         ComponentName.writeToParcel(mActivityComponent, out);
-        final int N = mRootViews.size();
+        final int N = mWindowNodes.size();
         out.writeInt(N);
         for (int i=0; i<N; i++) {
-            mRootViews.get(i).writeToParcel(out, pwriter);
+            mWindowNodes.get(i).writeToParcel(out, pwriter);
         }
         pwriter.finish();
         Log.i(TAG, "Flattened assist data: " + (out.dataPosition() - start) + " bytes");
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index eb27830..4ccd69f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1323,6 +1323,15 @@
                 Binder.getCallingUid());
     }
 
+    @Override
+    public int checkSelfPermission(String permission) {
+        if (permission == null) {
+            throw new IllegalArgumentException("permission is null");
+        }
+
+        return checkPermission(permission, Process.myPid(), Process.myUid());
+    }
+
     private void enforce(
             String permission, int resultOfCheck,
             boolean selfToo, int uid, String message) {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index bdcc312..4fdae7f 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.animation.Animator;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringRes;
 import android.content.ComponentCallbacks2;
@@ -1092,13 +1093,7 @@
         if (mActivity == null) {
             throw new IllegalStateException("Fragment " + this + " not attached to Activity");
         }
-        if (options != null) {
-            mActivity.startActivityFromFragment(this, intent, requestCode, options);
-        } else {
-            // Note we want to go through this call for compatibility with
-            // applications that may have overridden the method.
-            mActivity.startActivityFromFragment(this, intent, requestCode, options);
-        }
+        mActivity.startActivityFromFragment(this, intent, requestCode, options);
     }
 
     /**
@@ -1119,6 +1114,98 @@
     }
 
     /**
+     * Requests permissions to be granted to this application. These permissions
+     * must be requested in your manifest, they should not be granted to your app,
+     * and they should have protection level {@link android.content.pm.PermissionInfo
+     * #PROTECTION_DANGEROUS dangerous}, regardless whether they are declared by
+     * the platform or a third-party app.
+     * <p>
+     * Normal permissions {@link android.content.pm.PermissionInfo#PROTECTION_NORMAL}
+     * are granted at install time if requested in the manifest. Signature permissions
+     * {@link android.content.pm.PermissionInfo#PROTECTION_SIGNATURE} are granted at
+     * install time if requested in the manifest and the signature of your app matches
+     * the signature of the app declaring the permissions.
+     * </p>
+     * <p>
+     * If your app does not have the requested permissions the user will be presented
+     * with UI for accepting them. After the user has accepted or rejected the
+     * requested permissions you will receive a callback on {@link
+     * #onRequestPermissionsResult(int, String[], int[])} reporting whether the
+     * permissions were granted or not.
+     * </p>
+     * <p>
+     * Note that requesting a permission does not guarantee it will be granted and
+     * your app should be able to run without having this permission.
+     * </p>
+     * <p>
+     * This method may start an activity allowing the user to choose which permissions
+     * to grant and which to reject. Hence, you should be prepared that your activity
+     * may be paused and resumed. Further, granting some permissions may require
+     * a restart of you application. In such a case, the system will recreate the
+     * activity stack before delivering the result to {@link
+     * #onRequestPermissionsResult(int, String[], int[])}.
+     * </p>
+     * <p>
+     * When checking whether you have a permission you should use {@link
+     * android.content.Context#checkSelfPermission(String)}.
+     * </p>
+     * <p>
+     * A sample permissions request looks like this:
+     * </p>
+     * <code><pre><p>
+     * private void showContacts() {
+     *     if (getActivity().checkSelfPermission(Manifest.permission.READ_CONTACTS)
+     *             != PackageManager.PERMISSION_GRANTED) {
+     *         requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
+     *                 PERMISSIONS_REQUEST_READ_CONTACTS);
+     *     } else {
+     *         doShowContacts();
+     *     }
+     * }
+     *
+     * {@literal @}Override
+     * public void onRequestPermissionsResult(int requestCode, String[] permissions,
+     *         int[] grantResults) {
+     *     if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS
+     *             && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+     *         doShowContacts();
+     *     }
+     * }
+     * </code></pre></p>
+     *
+     * @param permissions The requested permissions.
+     * @param requestCode Application specific request code to match with a result
+     *    reported to {@link #onRequestPermissionsResult(int, String[], int[])}.
+     *
+     * @see #onRequestPermissionsResult(int, String[], int[])
+     * @see android.content.Context#checkSelfPermission(String)
+     */
+    public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
+        if (mActivity == null) {
+            throw new IllegalStateException("Fragment " + this + " not attached to Activity");
+        }
+        Intent intent = mActivity.getPackageManager().buildRequestPermissionsIntent(permissions);
+        mActivity.startActivityFromFragment(this, intent, requestCode, null);
+    }
+
+    /**
+     * Callback for the result from requesting permissions. This method
+     * is invoked for every call on {@link #requestPermissions(String[], int)}.
+     *
+     * @param requestCode The request code passed in {@link #requestPermissions(String[], int)}.
+     * @param permissions The requested permissions. Never null.
+     * @param grantResults The grant results for the corresponding permissions
+     *     which is either {@link android.content.pm.PackageManager#PERMISSION_GRANTED}
+     *     or {@link android.content.pm.PackageManager#PERMISSION_DENIED}. Never null.
+     *
+     * @see #requestPermissions(String[], int)
+     */
+    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+            @NonNull int[] grantResults) {
+        /* callback - do nothing */
+    }
+
+    /**
      * @hide Hack so that DialogFragment can make its Dialog before creating
      * its views, and the view construction can use the dialog's context for
      * inflation.  Maybe this should become a public API. Note sure.
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index 451af99..fe8e228 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -100,6 +100,11 @@
     void doFullBackup(in ParcelFileDescriptor data, int token, IBackupManager callbackBinder);
 
     /**
+     * Estimate how much data a full backup will deliver
+     */
+    void doMeasureFullBackup(int token, IBackupManager callbackBinder);
+
+    /**
      * Restore a single "file" to the application.  The file was typically obtained from
      * a full-backup dataset.  The agent reads 'size' bytes of file content
      * from the provided file descriptor.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b31ce04..e7f8f6d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -646,6 +646,11 @@
     public static final String CATEGORY_STATUS = "status";
 
     /**
+     * Notification category: user-scheduled reminder.
+     */
+    public static final String CATEGORY_REMINDER = "reminder";
+
+    /**
      * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
      * that best describes this Notification.  May be used by the system for ranking and filtering.
      */
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index fd7bae7..b3aa6be 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -27,6 +27,7 @@
 import android.app.job.JobScheduler;
 import android.app.trust.TrustManager;
 import android.app.usage.IUsageStatsManager;
+import android.app.usage.NetworkStatsManager;
 import android.app.usage.UsageStatsManager;
 import android.appwidget.AppWidgetManager;
 import android.bluetooth.BluetoothManager;
@@ -98,8 +99,8 @@
 import android.os.storage.StorageManager;
 import android.print.IPrintManager;
 import android.print.PrintManager;
-import android.service.fingerprint.FingerprintManager;
-import android.service.fingerprint.IFingerprintService;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintService;
 import android.service.persistentdata.IPersistentDataBlockService;
 import android.service.persistentdata.PersistentDataBlockManager;
 import android.telecom.TelecomManager;
@@ -401,13 +402,7 @@
                 new CachedServiceFetcher<StorageManager>() {
             @Override
             public StorageManager createService(ContextImpl ctx) {
-                try {
-                    return new StorageManager(
-                            ctx.getContentResolver(), ctx.mMainThread.getHandler().getLooper());
-                } catch (RemoteException rex) {
-                    Log.e(TAG, "Failed to create StorageManager", rex);
-                    return null;
-                }
+                return new StorageManager(ctx, ctx.mMainThread.getHandler().getLooper());
             }});
 
         registerService(Context.TELEPHONY_SERVICE, TelephonyManager.class,
@@ -639,6 +634,13 @@
                 return new UsageStatsManager(ctx.getOuterContext(), service);
             }});
 
+        registerService(Context.NETWORK_STATS_SERVICE, NetworkStatsManager.class,
+                new CachedServiceFetcher<NetworkStatsManager>() {
+            @Override
+            public NetworkStatsManager createService(ContextImpl ctx) {
+                return new NetworkStatsManager(ctx.getOuterContext());
+            }});
+
         registerService(Context.JOB_SCHEDULER_SERVICE, JobScheduler.class,
                 new StaticServiceFetcher<JobScheduler>() {
             @Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 682a468..cbb0f51 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -20,7 +20,6 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.app.Activity;
-import android.app.admin.IDevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -176,7 +175,8 @@
      *
      * <p>This component is set as device owner and active admin when device owner provisioning is
      * started by an NFC message containing an NFC record with MIME type
-     * {@link #MIME_TYPE_PROVISIONING_NFC}.
+     * {@link #MIME_TYPE_PROVISIONING_NFC_V2}. For the NFC record, the component name should be
+     * flattened to a string, via {@link ComponentName#flattenToShortString()}.
      *
      * @see DeviceAdminReceiver
      */
@@ -341,6 +341,18 @@
         = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION";
 
     /**
+     * An int extra holding a minimum required version code for the device admin package. If the
+     * device admin is already installed on the device, it will only be re-downloaded from
+     * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION} if the version of the
+     * installed package is less than this version code.
+     *
+     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
+     * provisioning via an NFC bump.
+     */
+    public static final String EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE
+        = "android.app.extra.PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE";
+
+    /**
      * A String extra holding a http cookie header which should be used in the http request to the
      * url specified in {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}.
      *
@@ -351,10 +363,10 @@
         = "android.app.extra.PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER";
 
     /**
-     * A String extra holding the SHA-1 checksum of the file at download location specified in
-     * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}. If this doesn't match
-     * the file at the download location an error will be shown to the user and the user will be
-     * asked to factory reset the device.
+     * A String extra holding the URL-safe base64 encoded SHA-1 checksum of the file at download
+     * location specified in {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}. If
+     * this doesn't match the file at the download location an error will be shown to the user and
+     * the user will be asked to factory reset the device.
      *
      * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
      * provisioning via an NFC bump.
@@ -380,21 +392,23 @@
      * A boolean extra indicating whether device encryption is required as part of Device Owner
      * provisioning.
      *
-     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
      * provisioning via an NFC bump.
      */
     public static final String EXTRA_PROVISIONING_SKIP_ENCRYPTION =
              "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
 
     /**
-     * On devices managed by a device owner app, a String representation of a Component name extra
-     * indicating the component of the application that is temporarily granted device owner
-     * privileges during device initialization and profile owner privileges during secondary user
-     * initialization.
+     * On devices managed by a device owner app, a {@link ComponentName} extra indicating the
+     * component of the application that is temporarily granted device owner privileges during
+     * device initialization and profile owner privileges during secondary user initialization.
      *
-     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
-     * provisioning via an NFC bump.
-     * @see ComponentName#unflattenFromString()
+     * <p>
+     * It can also be used in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts
+     * device owner provisioning via an NFC bump. For the NFC record, it should be flattened to a
+     * string first.
+     *
+     * @see ComponentName#flattenToShortString()
      */
     public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME
         = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME";
@@ -404,53 +418,105 @@
      * initializer package. When not provided it is assumed that the device initializer package is
      * already installed.
      *
-     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
      * provisioning via an NFC bump.
      */
     public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION
         = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION";
 
     /**
+     * An int extra holding a minimum required version code for the device initializer package.
+     * If the initializer is already installed on the device, it will only be re-downloaded from
+     * {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION} if the version of
+     * the installed package is less than this version code.
+     *
+     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
+     * provisioning via an NFC bump.
+     */
+    public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE
+        = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE";
+
+    /**
      * A String extra holding a http cookie header which should be used in the http request to the
      * url specified in {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}.
      *
-     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
      * provisioning via an NFC bump.
      */
     public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER
         = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER";
 
     /**
-     * A String extra holding the SHA-1 checksum of the file at download location specified in
+     * A String extra holding the URL-safe base64 encoded SHA-1 checksum of the file at download
+     * location specified in
      * {@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}. If this doesn't
      * match the file at the download location an error will be shown to the user and the user will
      * be asked to factory reset the device.
      *
-     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
      * provisioning via an NFC bump.
      */
     public static final String EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM
         = "android.app.extra.PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM";
 
     /**
-     * This MIME type is used for starting the Device Owner provisioning.
+     * A String extra holding the MAC address of the Bluetooth device to connect to with status
+     * updates during provisioning.
      *
-     * <p>During device owner provisioning a device admin app is set as the owner of the device.
-     * A device owner has full control over the device. The device owner can not be modified by the
-     * user and the only way of resetting the device is if the device owner app calls a factory
-     * reset.
+     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
+     * provisioning via an NFC bump.
+     */
+    public static final String EXTRA_PROVISIONING_BT_MAC_ADDRESS
+            = "android.app.extra.PROVISIONING_BT_MAC_ADDRESS";
+
+    /**
+     * A String extra holding the Bluetooth service UUID on the device to connect to with status
+     * updates during provisioning.
      *
-     * <p> A typical use case would be a device that is owned by a company, but used by either an
-     * employee or client.
+     * <p>This value must be specified when {@code #EXTRA_PROVISIONING_BT_MAC_ADDRESS} is present.
      *
-     * <p> The NFC message should be send to an unprovisioned device.
+     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
+     * provisioning via an NFC bump.
+     */
+    public static final String EXTRA_PROVISIONING_BT_UUID
+            = "android.app.extra.PROVISIONING_BT_UUID";
+
+    /**
+     * A String extra holding a unique identifier used to identify the device connecting over
+     * Bluetooth. This identifier will be part of every status message sent to the remote device.
+     *
+     * <p>This value must be specified when {@code #EXTRA_PROVISIONING_BT_MAC_ADDRESS} is present.
+     *
+     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
+     * provisioning via an NFC bump.
+     */
+    public static final String EXTRA_PROVISIONING_BT_DEVICE_ID
+            = "android.app.extra.PROVISIONING_BT_DEVICE_ID";
+
+    /**
+     * A Boolean extra that that will cause a provisioned device to temporarily proxy network
+     * traffic over Bluetooth. When a Wi-Fi network is available, the network proxy will stop.
+     *
+     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
+     * provisioning via an NFC bump.
+     */
+    public static final String EXTRA_PROVISIONING_BT_USE_PROXY
+            = "android.app.extra.PROVISIONING_BT_USE_PROXY";
+    /**
+     * This MIME type is used for starting the Device Owner provisioning that does not require
+     * provisioning features introduced in Android API level
+     * {@link android.os.Build.VERSION_CODES#MNC} or later levels.
+     *
+     * <p>For more information about the provisioning process see
+     * {@link #MIME_TYPE_PROVISIONING_NFC_V2}.
      *
      * <p>The NFC record must contain a serialized {@link java.util.Properties} object which
      * contains the following properties:
      * <ul>
-     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}</li>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}</li>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_LOCATION}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_DOWNLOAD_COOKIE_HEADER}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM}</li>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_CHECKSUM}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_LOCAL_TIME} (convert to String), optional</li>
      * <li>{@link #EXTRA_PROVISIONING_TIME_ZONE}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_LOCALE}, optional</li>
@@ -461,17 +527,56 @@
      * <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_HOST}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_PORT} (convert to String), optional</li>
      * <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_BYPASS}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_WIFI_PAC_URL}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li></ul>
+     * <li>{@link #EXTRA_PROVISIONING_WIFI_PAC_URL}, optional</li></ul>
      *
      * <p>
-     * In version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, it should also contain
-     * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}.
-     * As of {@link android.os.Build.VERSION_CODES#MNC}, it should contain
-     * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME} instead, (although
-     * specifying only {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} is still supported).
-     * This componentName must have been converted to a String via
-     * {@link android.content.ComponentName#flattenToString()}
+     * As of {@link android.os.Build.VERSION_CODES#MNC}, the properties should contain
+     * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME} instead of
+     * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}, (although specifying only
+     * {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME} is still supported).
+     *
+     * @see #MIME_TYPE_PROVISIONING_NFC_V2
+     *
+     */
+    public static final String MIME_TYPE_PROVISIONING_NFC
+        = "application/com.android.managedprovisioning";
+
+
+    /**
+     * This MIME type is used for starting the Device Owner provisioning that requires
+     * new provisioning features introduced in API version
+     * {@link android.os.Build.VERSION_CODES#MNC} in addition to those supported in earlier
+     * versions.
+     *
+     * <p>During device owner provisioning a device admin app is set as the owner of the device.
+     * A device owner has full control over the device. The device owner can not be modified by the
+     * user and the only way of resetting the device is if the device owner app calls a factory
+     * reset.
+     *
+     * <p> A typical use case would be a device that is owned by a company, but used by either an
+     * employee or client.
+     *
+     * <p> The NFC message should be sent to an unprovisioned device.
+     *
+     * <p>The NFC record must contain a serialized {@link java.util.Properties} object which
+     * contains the following properties in addition to properties listed at
+     * {@link #MIME_TYPE_PROVISIONING_NFC}:
+     * <ul>
+     * <li>{@link #EXTRA_PROVISIONING_SKIP_ENCRYPTION}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_LOCATION}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_DOWNLOAD_COOKIE_HEADER}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_PACKAGE_CHECKSUM}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_INITIALIZER_MINIMUM_VERSION_CODE}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME}.
+     * Replaces {@link #EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME}. The value of the property
+     * should be converted to a String via
+     * {@link android.content.ComponentName#flattenToString()}</li>
+     * <li>{@link #EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_BT_MAC_ADDRESS}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_BT_UUID}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_BT_DEVICE_ID}, optional</li>
+     * <li>{@link #EXTRA_PROVISIONING_BT_USE_PROXY}, optional</li></ul>
      *
      * <p> When device owner provisioning has completed, an intent of the type
      * {@link DeviceAdminReceiver#ACTION_PROFILE_PROVISIONING_COMPLETE} is broadcasted to the
@@ -483,8 +588,8 @@
      * <p>Input: Nothing.</p>
      * <p>Output: Nothing</p>
      */
-    public static final String MIME_TYPE_PROVISIONING_NFC
-        = "application/com.android.managedprovisioning";
+    public static final String MIME_TYPE_PROVISIONING_NFC_V2
+            = "application/com.android.managedprovisioning.v2";
 
     /**
      * Activity action: ask the user to add a new device administrator to the system.
@@ -509,11 +614,10 @@
     /**
      * @hide
      * Activity action: ask the user to add a new device administrator as the profile owner
-     * for this user. Only system privileged apps that have MANAGE_USERS and MANAGE_DEVICE_ADMINS
-     * permission can call this API.
+     * for this user. Only system apps can launch this intent.
      *
-     * <p>The ComponentName of the profile owner admin is pass in {@link #EXTRA_DEVICE_ADMIN} extra
-     * field. This will invoke a UI to bring the user through adding the profile owner admin
+     * <p>The ComponentName of the profile owner admin is passed in the {@link #EXTRA_DEVICE_ADMIN}
+     * extra field. This will invoke a UI to bring the user through adding the profile owner admin
      * to remotely control restrictions on the user.
      *
      * <p>The intent must be invoked via {@link Activity#startActivityForResult()} to receive the
@@ -525,8 +629,8 @@
      * field to provide the user with additional explanation (in addition
      * to your component's description) about what is being added.
      *
-     * <p>If there is already a profile owner active or the caller doesn't have the required
-     * permissions, the operation will return a failure result.
+     * <p>If there is already a profile owner active or the caller is not a system app, the
+     * operation will return a failure result.
      */
     @SystemApi
     public static final String ACTION_SET_PROFILE_OWNER
@@ -3061,6 +3165,22 @@
     }
 
     /**
+     * Start Quick Contact on the managed profile for the current user, if the policy allows.
+     * @hide
+     */
+    public void startManagedQuickContact(String actualLookupKey, long actualContactId,
+            Intent originalIntent) {
+        if (mService != null) {
+            try {
+                mService.startManagedQuickContact(
+                        actualLookupKey, actualContactId, originalIntent);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
      * Called by the profile owner of a managed profile so that some intents sent in the managed
      * profile can also be resolved in the parent, or vice versa.
      * Only activity intents are supported.
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index a1f1d92..0a0d77d 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -60,4 +60,13 @@
      */
     public abstract void addOnCrossProfileWidgetProvidersChangeListener(
             OnCrossProfileWidgetProvidersChangeListener listener);
+
+    /**
+     * Checks if an app with given uid is an active device admin of its user and has the policy
+     * specified.
+     * @param uid App uid.
+     * @param reqPolicy Required policy, for policies see {@link DevicePolicyManager}.
+     * @return true if the uid is an active admin with the given policy.
+     */
+    public abstract boolean isActiveAdminWithPolicy(int uid, int reqPolicy);
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9ca52e5..73b0684 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -189,6 +189,7 @@
     void setCrossProfileCallerIdDisabled(in ComponentName who, boolean disabled);
     boolean getCrossProfileCallerIdDisabled(in ComponentName who);
     boolean getCrossProfileCallerIdDisabledForUser(int userId);
+    void startManagedQuickContact(String lookupKey, long contactId, in Intent originalIntent);
 
     void setTrustAgentConfiguration(in ComponentName admin, in ComponentName agent,
             in PersistableBundle args);
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 7f89100..2bf267a 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -424,10 +424,12 @@
         }
 
         // And now that we know where it lives, semantically, back it up appropriately
-        Log.i(TAG, "backupFile() of " + filePath + " => domain=" + domain
+        // In the measurement case, backupToTar() updates the size in output and returns
+        // without transmitting any file data.
+        if (DEBUG) Log.i(TAG, "backupFile() of " + filePath + " => domain=" + domain
                 + " rootpath=" + rootpath);
-        FullBackup.backupToTar(getPackageName(), domain, null, rootpath, filePath,
-                output.getData());
+        
+        FullBackup.backupToTar(getPackageName(), domain, null, rootpath, filePath, output);
     }
 
     /**
@@ -477,9 +479,8 @@
                     continue;
                 }
 
-                // Finally, back this file up before proceeding
-                FullBackup.backupToTar(packageName, domain, null, rootPath, filePath,
-                        output.getData());
+                // Finally, back this file up (or measure it) before proceeding
+                FullBackup.backupToTar(packageName, domain, null, rootPath, filePath, output);
             }
         }
     }
@@ -640,7 +641,7 @@
 
                 Binder.restoreCallingIdentity(ident);
                 try {
-                    callbackBinder.opComplete(token);
+                    callbackBinder.opComplete(token, 0);
                 } catch (RemoteException e) {
                     // we'll time out anyway, so we're safe
                 }
@@ -670,7 +671,7 @@
 
                 Binder.restoreCallingIdentity(ident);
                 try {
-                    callbackBinder.opComplete(token);
+                    callbackBinder.opComplete(token, 0);
                 } catch (RemoteException e) {
                     // we'll time out anyway, so we're safe
                 }
@@ -692,10 +693,10 @@
             try {
                 BackupAgent.this.onFullBackup(new FullBackupDataOutput(data));
             } catch (IOException ex) {
-                Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+                Log.d(TAG, "onFullBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
                 throw new RuntimeException(ex);
             } catch (RuntimeException ex) {
-                Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+                Log.d(TAG, "onFullBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex);
                 throw ex;
             } finally {
                 // ... and then again after, as in the doBackup() case
@@ -713,13 +714,37 @@
 
                 Binder.restoreCallingIdentity(ident);
                 try {
-                    callbackBinder.opComplete(token);
+                    callbackBinder.opComplete(token, 0);
                 } catch (RemoteException e) {
                     // we'll time out anyway, so we're safe
                 }
             }
         }
 
+        public void doMeasureFullBackup(int token, IBackupManager callbackBinder) {
+            // Ensure that we're running with the app's normal permission level
+            final long ident = Binder.clearCallingIdentity();
+            FullBackupDataOutput measureOutput = new FullBackupDataOutput();
+
+            waitForSharedPrefs();
+            try {
+                BackupAgent.this.onFullBackup(measureOutput);
+            } catch (IOException ex) {
+                Log.d(TAG, "onFullBackup[M] (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+                throw new RuntimeException(ex);
+            } catch (RuntimeException ex) {
+                Log.d(TAG, "onFullBackup[M] (" + BackupAgent.this.getClass().getName() + ") threw", ex);
+                throw ex;
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+                try {
+                    callbackBinder.opComplete(token, measureOutput.getSize());
+                } catch (RemoteException e) {
+                    // timeout, so we're safe
+                }
+            }
+        }
+
         @Override
         public void doRestoreFile(ParcelFileDescriptor data, long size,
                 int type, String domain, String path, long mode, long mtime,
@@ -728,6 +753,7 @@
             try {
                 BackupAgent.this.onRestoreFile(data, size, type, domain, path, mode, mtime);
             } catch (IOException e) {
+                Log.d(TAG, "onRestoreFile (" + BackupAgent.this.getClass().getName() + ") threw", e);
                 throw new RuntimeException(e);
             } finally {
                 // Ensure that any side-effect SharedPreferences writes have landed
@@ -735,7 +761,7 @@
 
                 Binder.restoreCallingIdentity(ident);
                 try {
-                    callbackBinder.opComplete(token);
+                    callbackBinder.opComplete(token, 0);
                 } catch (RemoteException e) {
                     // we'll time out anyway, so we're safe
                 }
@@ -747,13 +773,16 @@
             long ident = Binder.clearCallingIdentity();
             try {
                 BackupAgent.this.onRestoreFinished();
+            } catch (Exception e) {
+                Log.d(TAG, "onRestoreFinished (" + BackupAgent.this.getClass().getName() + ") threw", e);
+                throw e;
             } finally {
                 // Ensure that any side-effect SharedPreferences writes have landed
                 waitForSharedPrefs();
 
                 Binder.restoreCallingIdentity(ident);
                 try {
-                    callbackBinder.opComplete(token);
+                    callbackBinder.opComplete(token, 0);
                 } catch (RemoteException e) {
                     // we'll time out anyway, so we're safe
                 }
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index e853540..ca6dc69 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -393,6 +393,26 @@
     }
 
     /**
+     * Called after {@link #performFullBackup} to make sure that the transport is willing to
+     * handle a full-data backup operation of the specified size on the current package.
+     * If the transport returns anything other than TRANSPORT_OK, the package's backup
+     * operation will be skipped (and {@link #finishBackup() invoked} with no data for that
+     * package being passed to {@link #sendBackupData}.
+     *
+     * Added in MNC (API 23).
+     *
+     * @param size The estimated size of the full-data payload for this app.  This includes
+     *         manifest and archive format overhead, but is not guaranteed to be precise.
+     * @return TRANSPORT_OK if the platform is to proceed with the full-data backup,
+     *         TRANSPORT_PACKAGE_REJECTED if the proposed payload size is too large for
+     *         the transport to handle, or TRANSPORT_ERROR to indicate a fatal error
+     *         condition that means the platform cannot perform a backup at this time.
+     */
+    public int checkFullBackupSize(long size) {
+        return BackupTransport.TRANSPORT_OK;
+    }
+
+    /**
      * Tells the transport to read {@code numBytes} bytes of data from the socket file
      * descriptor provided in the {@link #performFullBackup(PackageInfo, ParcelFileDescriptor)}
      * call, and deliver those bytes to the datastore.
@@ -588,6 +608,11 @@
         }
 
         @Override
+        public int checkFullBackupSize(long size) {
+            return BackupTransport.this.checkFullBackupSize(size);
+        }
+
+        @Override
         public int sendBackupData(int numBytes) throws RemoteException {
             return BackupTransport.this.sendBackupData(numBytes);
         }
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index e5b47c6..259884e 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -58,7 +58,7 @@
      * @hide
      */
     static public native int backupToTar(String packageName, String domain,
-            String linkdomain, String rootpath, String path, BackupDataOutput output);
+            String linkdomain, String rootpath, String path, FullBackupDataOutput output);
 
     /**
      * Copy data from a socket to the given File location on permanent storage.  The
diff --git a/core/java/android/app/backup/FullBackupDataOutput.java b/core/java/android/app/backup/FullBackupDataOutput.java
index 99dab1f..94704b9 100644
--- a/core/java/android/app/backup/FullBackupDataOutput.java
+++ b/core/java/android/app/backup/FullBackupDataOutput.java
@@ -9,7 +9,14 @@
  */
 public class FullBackupDataOutput {
     // Currently a name-scoping shim around BackupDataOutput
-    private BackupDataOutput mData;
+    private final BackupDataOutput mData;
+    private long mSize;
+
+    /** @hide - used only in measure operation */
+    public FullBackupDataOutput() {
+        mData = null;
+        mSize = 0;
+    }
 
     /** @hide */
     public FullBackupDataOutput(ParcelFileDescriptor fd) {
@@ -18,4 +25,14 @@
 
     /** @hide */
     public BackupDataOutput getData() { return mData; }
+
+    /** @hide - used for measurement pass */
+    public void addSize(long size) {
+        if (size > 0) {
+            mSize += size;
+        }
+    }
+
+    /** @hide - used for measurement pass */
+    public long getSize() { return mSize; }
 }
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 41ad936..8f36dc4 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -286,11 +286,14 @@
      * Notify the backup manager that a BackupAgent has completed the operation
      * corresponding to the given token.
      *
-     * @param token The transaction token passed to a BackupAgent's doBackup() or
-     *        doRestore() method.
+     * @param token The transaction token passed to the BackupAgent method being
+     *        invoked.
+     * @param result In the case of a full backup measure operation, the estimated
+     *        total file size that would result from the operation. Unused in all other
+     *        cases.
      * {@hide}
      */
-    void opComplete(int token);
+    void opComplete(int token, long result);
 
     /**
      * Make the device's backup and restore machinery (in)active.  When it is inactive,
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
new file mode 100644
index 0000000..af7c053
--- /dev/null
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -0,0 +1,233 @@
+/**
+ * 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 android.app.usage;
+
+import android.app.usage.NetworkUsageStats.Bucket;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkIdentity;
+import android.net.NetworkTemplate;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+/**
+ * Provides access to network usage history and statistics. Usage data is collected in
+ * discrete bins of time called 'Buckets'. See {@link NetworkUsageStats.Bucket} for details.
+ * <p />
+ * Queries can define a time interval in the form of start and end timestamps (Long.MIN_VALUE and
+ * Long.MAX_VALUE can be used to simulate open ended intervals). All queries (except
+ * {@link #querySummaryForDevice}) collect only network usage of apps belonging to the same user
+ * as the client. In addition tethering usage, usage by removed users and apps, and usage by system
+ * is also included in the results.
+ * <h3>
+ * Summary queries
+ * </h3>
+ * These queries aggregate network usage across the whole interval. Therefore there will be only one
+ * bucket for a particular key and state combination. In case of the user-wide and device-wide
+ * summaries a single bucket containing the totalised network usage is returned.
+ * <h3>
+ * History queries
+ * </h3>
+ * These queries do not aggregate over time but do aggregate over state. Therefore there can be
+ * multiple buckets for a particular key but all Bucket's state is going to be
+ * {@link NetworkUsageStats.Bucket#STATE_ALL}.
+ * <p />
+ * <b>NOTE:</b> This API requires the permission
+ * {@link android.Manifest.permission#PACKAGE_USAGE_STATS}, which is a system-level permission and
+ * will not be granted to third-party apps. However, declaring the permission implies intention to
+ * use the API and the user of the device can grant permission through the Settings application.
+ * Profile owner apps are automatically granted permission to query data on the profile they manage
+ * (that is, for any query except {@link #querySummaryForDevice}). Device owner apps likewise get
+ * access to usage data of the primary user.
+ */
+public class NetworkStatsManager {
+    private final static String TAG = "NetworkStatsManager";
+
+    private final Context mContext;
+
+    /**
+     * {@hide}
+     */
+    public NetworkStatsManager(Context context) {
+        mContext = context;
+    }
+    /**
+     * Query network usage statistics summaries. Result is summarised data usage for the whole
+     * device. Result is a single Bucket aggregated over time, state and uid.
+     *
+     * @param networkType As defined in {@link ConnectivityManager}, e.g.
+     *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
+     *            etc.
+     * @param subscriberId If applicable, the subscriber id of the network interface.
+     * @param startTime Start of period. Defined in terms of "Unix time", see
+     *            {@link java.lang.System#currentTimeMillis}.
+     * @param endTime End of period. Defined in terms of "Unix time", see
+     *            {@link java.lang.System#currentTimeMillis}.
+     * @return Bucket object or null if permissions are insufficient or error happened during
+     *         statistics collection.
+     */
+    public Bucket querySummaryForDevice(int networkType, String subscriberId,
+            long startTime, long endTime) throws SecurityException, RemoteException {
+        NetworkTemplate template = createTemplate(networkType, subscriberId);
+        if (template == null) {
+            return null;
+        }
+
+        Bucket bucket = null;
+        NetworkUsageStats stats = new NetworkUsageStats(mContext, template, startTime, endTime);
+        bucket = stats.getDeviceSummaryForNetwork(startTime, endTime);
+
+        stats.close();
+        return bucket;
+    }
+
+    /**
+     * Query network usage statistics summaries. Result is summarised data usage for all uids
+     * belonging to calling user. Result is a single Bucket aggregated over time, state and uid.
+     *
+     * @param networkType As defined in {@link ConnectivityManager}, e.g.
+     *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
+     *            etc.
+     * @param subscriberId If applicable, the subscriber id of the network interface.
+     * @param startTime Start of period. Defined in terms of "Unix time", see
+     *            {@link java.lang.System#currentTimeMillis}.
+     * @param endTime End of period. Defined in terms of "Unix time", see
+     *            {@link java.lang.System#currentTimeMillis}.
+     * @return Bucket object or null if permissions are insufficient or error happened during
+     *         statistics collection.
+     */
+    public Bucket querySummaryForUser(int networkType, String subscriberId, long startTime,
+            long endTime) throws SecurityException, RemoteException {
+        NetworkTemplate template = createTemplate(networkType, subscriberId);
+        if (template == null) {
+            return null;
+        }
+
+        NetworkUsageStats stats;
+        stats = new NetworkUsageStats(mContext, template, startTime, endTime);
+        stats.startSummaryEnumeration(startTime, endTime);
+
+        stats.close();
+        return stats.getSummaryAggregate();
+    }
+
+    /**
+     * Query network usage statistics summaries. Result filtered to include only uids belonging to
+     * calling user. Result is aggregated over time, hence all buckets will have the same start and
+     * end timestamps. Not aggregated over state or uid.
+     *
+     * @param networkType As defined in {@link ConnectivityManager}, e.g.
+     *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
+     *            etc.
+     * @param subscriberId If applicable, the subscriber id of the network interface.
+     * @param startTime Start of period. Defined in terms of "Unix time", see
+     *            {@link java.lang.System#currentTimeMillis}.
+     * @param endTime End of period. Defined in terms of "Unix time", see
+     *            {@link java.lang.System#currentTimeMillis}.
+     * @return Statistics object or null if permissions are insufficient or error happened during
+     *         statistics collection.
+     */
+    public NetworkUsageStats querySummary(int networkType, String subscriberId, long startTime,
+            long endTime) throws SecurityException, RemoteException {
+        NetworkTemplate template = createTemplate(networkType, subscriberId);
+        if (template == null) {
+            return null;
+        }
+
+        NetworkUsageStats result;
+        result = new NetworkUsageStats(mContext, template, startTime, endTime);
+        result.startSummaryEnumeration(startTime, endTime);
+
+        return result;
+    }
+
+    /**
+     * Query network usage statistics details. Only usable for uids belonging to calling user.
+     * Result is aggregated over state but not aggregated over time.
+     *
+     * @param networkType As defined in {@link ConnectivityManager}, e.g.
+     *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
+     *            etc.
+     * @param subscriberId If applicable, the subscriber id of the network interface.
+     * @param startTime Start of period. Defined in terms of "Unix time", see
+     *            {@link java.lang.System#currentTimeMillis}.
+     * @param endTime End of period. Defined in terms of "Unix time", see
+     *            {@link java.lang.System#currentTimeMillis}.
+     * @param uid UID of app
+     * @return Statistics object or null if permissions are insufficient or error happened during
+     *         statistics collection.
+     */
+    public NetworkUsageStats queryDetailsForUid(int networkType, String subscriberId,
+            long startTime, long endTime, int uid) throws SecurityException, RemoteException {
+        NetworkTemplate template = createTemplate(networkType, subscriberId);
+        if (template == null) {
+            return null;
+        }
+
+        NetworkUsageStats result;
+        result = new NetworkUsageStats(mContext, template, startTime, endTime);
+        result.startHistoryEnumeration(uid);
+
+        return result;
+    }
+
+    /**
+     * Query network usage statistics details. Result filtered to include only uids belonging to
+     * calling user. Result is aggregated over state but not aggregated over time or uid.
+     *
+     * @param networkType As defined in {@link ConnectivityManager}, e.g.
+     *            {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
+     *            etc.
+     * @param subscriberId If applicable, the subscriber id of the network interface.
+     * @param startTime Start of period. Defined in terms of "Unix time", see
+     *            {@link java.lang.System#currentTimeMillis}.
+     * @param endTime End of period. Defined in terms of "Unix time", see
+     *            {@link java.lang.System#currentTimeMillis}.
+     * @return Statistics object or null if permissions are insufficient or error happened during
+     *         statistics collection.
+     */
+    public NetworkUsageStats queryDetails(int networkType, String subscriberId, long startTime,
+            long endTime) throws SecurityException, RemoteException {
+        NetworkTemplate template = createTemplate(networkType, subscriberId);
+        if (template == null) {
+            return null;
+        }
+        NetworkUsageStats result;
+        result = new NetworkUsageStats(mContext, template, startTime, endTime);
+        result.startUserUidEnumeration();
+        return result;
+    }
+
+    private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
+        NetworkTemplate template = null;
+        switch (networkType) {
+            case ConnectivityManager.TYPE_MOBILE: {
+                template = NetworkTemplate.buildTemplateMobileAll(subscriberId);
+                } break;
+            case ConnectivityManager.TYPE_WIFI: {
+                template = NetworkTemplate.buildTemplateWifiWildcard();
+                } break;
+            default: {
+                Log.w(TAG, "Cannot create template for network type " + networkType
+                        + ", subscriberId '" + NetworkIdentity.scrubSubscriberId(subscriberId) +
+                        "'.");
+            }
+        }
+        return template;
+    }
+}
diff --git a/core/java/android/app/usage/NetworkUsageStats.java b/core/java/android/app/usage/NetworkUsageStats.java
new file mode 100644
index 0000000..990d231
--- /dev/null
+++ b/core/java/android/app/usage/NetworkUsageStats.java
@@ -0,0 +1,479 @@
+/**
+ * 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 android.app.usage;
+
+import android.content.Context;
+import android.net.INetworkStatsService;
+import android.net.INetworkStatsSession;
+import android.net.NetworkStats;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.net.TrafficStats;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import dalvik.system.CloseGuard;
+
+/**
+ * Class providing enumeration over buckets of network usage statistics. NetworkUsageStats objects
+ * are returned as results to various queries in {@link NetworkStatsManager}.
+ */
+public final class NetworkUsageStats implements AutoCloseable {
+    private final static String TAG = "NetworkUsageStats";
+
+    private final CloseGuard mCloseGuard = CloseGuard.get();
+
+    /**
+     * Start timestamp of stats collected
+     */
+    private final long mStartTimeStamp;
+
+    /**
+     * End timestamp of stats collected
+     */
+    private final long mEndTimeStamp;
+
+
+    /**
+     * Non-null array indicates the query enumerates over uids.
+     */
+    private int[] mUids;
+
+    /**
+     * Index of the current uid in mUids when doing uid enumeration or a single uid value,
+     * depending on query type.
+     */
+    private int mUidOrUidIndex;
+
+    /**
+     * The session while the query requires it, null if all the stats have been collected or close()
+     * has been called.
+     */
+    private INetworkStatsSession mSession;
+    private NetworkTemplate mTemplate;
+
+    /**
+     * Results of a summary query.
+     */
+    private NetworkStats mSummary = null;
+
+    /**
+     * Results of detail queries.
+     */
+    private NetworkStatsHistory mHistory = null;
+
+    /**
+     * Where we are in enumerating over the current result.
+     */
+    private int mEnumerationIndex = 0;
+
+    /**
+     * Recycling entry objects to prevent heap fragmentation.
+     */
+    private NetworkStats.Entry mRecycledSummaryEntry = null;
+    private NetworkStatsHistory.Entry mRecycledHistoryEntry = null;
+
+    /** @hide */
+    NetworkUsageStats(Context context, NetworkTemplate template, long startTimestamp,
+            long endTimestamp) throws RemoteException, SecurityException {
+        final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
+                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+        // Open network stats session
+        mSession = statsService.openSessionForUsageStats(context.getOpPackageName());
+        mCloseGuard.open("close");
+        mTemplate = template;
+        mStartTimeStamp = startTimestamp;
+        mEndTimeStamp = endTimestamp;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
+            }
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    // -------------------------BEGINNING OF PUBLIC API-----------------------------------
+
+    /**
+     * Buckets are the smallest elements of a query result. As some dimensions of a result may be
+     * aggregated (e.g. time or state) some values may be equal across all buckets.
+     */
+    public static class Bucket {
+        /**
+         * Combined usage across all other states.
+         */
+        public static final int STATE_ALL = -1;
+
+        /**
+         * Usage not accounted in any other states.
+         */
+        public static final int STATE_DEFAULT = 0x1;
+
+        /**
+         * Foreground usage.
+         */
+        public static final int STATE_FOREGROUND = 0x2;
+
+        /**
+         * Special UID value for removed apps.
+         */
+        public static final int UID_REMOVED = -4;
+
+        /**
+         * Special UID value for data usage by tethering.
+         */
+        public static final int UID_TETHERING = -5;
+
+        private int mUid;
+        private int mState;
+        private long mBeginTimeStamp;
+        private long mEndTimeStamp;
+        private long mRxBytes;
+        private long mRxPackets;
+        private long mTxBytes;
+        private long mTxPackets;
+
+        private static int convertState(int networkStatsSet) {
+            switch (networkStatsSet) {
+                case NetworkStats.SET_ALL : return STATE_ALL;
+                case NetworkStats.SET_DEFAULT : return STATE_DEFAULT;
+                case NetworkStats.SET_FOREGROUND : return STATE_FOREGROUND;
+            }
+            return 0;
+        }
+
+        private static int convertUid(int uid) {
+            switch (uid) {
+                case TrafficStats.UID_REMOVED: return UID_REMOVED;
+                case TrafficStats.UID_TETHERING: return UID_TETHERING;
+            }
+            return uid;
+        }
+
+        public Bucket() {
+        }
+
+        /**
+         * Key of the bucket. Usually an app uid or one of the following special values:<p />
+         * <ul>
+         * <li>{@link #UID_REMOVED}</li>
+         * <li>{@link #UID_TETHERING}</li>
+         * <li>{@link android.os.Process#SYSTEM_UID}</li>
+         * </ul>
+         * @return Bucket key.
+         */
+        public int getUid() {
+            return mUid;
+        }
+
+        /**
+         * Usage state. One of the following values:<p/>
+         * <ul>
+         * <li>{@link #STATE_ALL}</li>
+         * <li>{@link #STATE_DEFAULT}</li>
+         * <li>{@link #STATE_FOREGROUND}</li>
+         * </ul>
+         * @return Usage state.
+         */
+        public int getState() {
+            return mState;
+        }
+
+        /**
+         * Start timestamp of the bucket's time interval. Defined in terms of "Unix time", see
+         * {@link java.lang.System#currentTimeMillis}.
+         * @return Start of interval.
+         */
+        public long getStartTimeStamp() {
+            return mBeginTimeStamp;
+        }
+
+        /**
+         * End timestamp of the bucket's time interval. Defined in terms of "Unix time", see
+         * {@link java.lang.System#currentTimeMillis}.
+         * @return End of interval.
+         */
+        public long getEndTimeStamp() {
+            return mEndTimeStamp;
+        }
+
+        /**
+         * Number of bytes received during the bucket's time interval. Statistics are measured at
+         * the network layer, so they include both TCP and UDP usage.
+         * @return Number of bytes.
+         */
+        public long getRxBytes() {
+            return mRxBytes;
+        }
+
+        /**
+         * Number of bytes transmitted during the bucket's time interval. Statistics are measured at
+         * the network layer, so they include both TCP and UDP usage.
+         * @return Number of bytes.
+         */
+        public long getTxBytes() {
+            return mTxBytes;
+        }
+
+        /**
+         * Number of packets received during the bucket's time interval. Statistics are measured at
+         * the network layer, so they include both TCP and UDP usage.
+         * @return Number of packets.
+         */
+        public long getRxPackets() {
+            return mRxPackets;
+        }
+
+        /**
+         * Number of packets transmitted during the bucket's time interval. Statistics are measured
+         * at the network layer, so they include both TCP and UDP usage.
+         * @return Number of packets.
+         */
+        public long getTxPackets() {
+            return mTxPackets;
+        }
+    }
+
+    /**
+     * Fills the recycled bucket with data of the next bin in the enumeration.
+     * @param bucketOut Bucket to be filled with data.
+     * @return true if successfully filled the bucket, false otherwise.
+     */
+    public boolean getNextBucket(Bucket bucketOut) {
+        if (mSummary != null) {
+            return getNextSummaryBucket(bucketOut);
+        } else {
+            return getNextHistoryBucket(bucketOut);
+        }
+    }
+
+    /**
+     * Check if it is possible to ask for a next bucket in the enumeration.
+     * @return true if there is at least one more bucket.
+     */
+    public boolean hasNextBucket() {
+        if (mSummary != null) {
+            return mEnumerationIndex < mSummary.size();
+        } else if (mHistory != null) {
+            return mEnumerationIndex < mHistory.size()
+                    || hasNextUid();
+        }
+        return false;
+    }
+
+    /**
+     * Closes the enumeration. Call this method before this object gets out of scope.
+     */
+    @Override
+    public void close() {
+        if (mSession != null) {
+            try {
+                mSession.close();
+            } catch (RemoteException e) {
+                Log.w(TAG, e);
+                // Otherwise, meh
+            }
+        }
+        mSession = null;
+        if (mCloseGuard != null) {
+            mCloseGuard.close();
+        }
+    }
+
+    // -------------------------END OF PUBLIC API-----------------------------------
+
+    /**
+     * Collects device summary results into a Bucket.
+     * @param startTime
+     * @param endTime
+     * @throws RemoteException
+     */
+    Bucket getDeviceSummaryForNetwork(long startTime, long endTime) throws RemoteException {
+        mSummary = mSession.getDeviceSummaryForNetwork(mTemplate, startTime, endTime);
+
+        // Setting enumeration index beyond end to avoid accidental enumeration over data that does
+        // not belong to the calling user.
+        mEnumerationIndex = mSummary.size();
+
+        return getSummaryAggregate();
+    }
+
+    /**
+     * Collects summary results and sets summary enumeration mode.
+     * @param startTime
+     * @param endTime
+     * @throws RemoteException
+     */
+    void startSummaryEnumeration(long startTime, long endTime) throws RemoteException {
+        mSummary = mSession.getSummaryForAllUid(mTemplate, startTime, endTime, false);
+
+        mEnumerationIndex = 0;
+    }
+
+    /**
+     * Collects history results for uid and resets history enumeration index.
+     */
+    void startHistoryEnumeration(int uid) {
+        mHistory = null;
+        try {
+            mHistory = mSession.getHistoryForUid(mTemplate, uid, NetworkStats.SET_ALL,
+                    NetworkStats.TAG_NONE, NetworkStatsHistory.FIELD_ALL);
+            setSingleUid(uid);
+        } catch (RemoteException e) {
+            Log.w(TAG, e);
+            // Leaving mHistory null
+        }
+        mEnumerationIndex = 0;
+    }
+
+    /**
+     * Starts uid enumeration for current user.
+     * @throws RemoteException
+     */
+    void startUserUidEnumeration() throws RemoteException {
+        setUidEnumeration(mSession.getRelevantUids());
+        stepHistory();
+    }
+
+    /**
+     * Steps to next uid in enumeration and collects history for that.
+     */
+    private void stepHistory(){
+        if (hasNextUid()) {
+            stepUid();
+            mHistory = null;
+            try {
+                mHistory = mSession.getHistoryForUid(mTemplate, getUid(), NetworkStats.SET_ALL,
+                        NetworkStats.TAG_NONE, NetworkStatsHistory.FIELD_ALL);
+            } catch (RemoteException e) {
+                Log.w(TAG, e);
+                // Leaving mHistory null
+            }
+            mEnumerationIndex = 0;
+        }
+    }
+
+    private void fillBucketFromSummaryEntry(Bucket bucketOut) {
+        bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
+        bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
+        bucketOut.mBeginTimeStamp = mStartTimeStamp;
+        bucketOut.mEndTimeStamp = mEndTimeStamp;
+        bucketOut.mRxBytes = mRecycledSummaryEntry.rxBytes;
+        bucketOut.mRxPackets = mRecycledSummaryEntry.rxPackets;
+        bucketOut.mTxBytes = mRecycledSummaryEntry.txBytes;
+        bucketOut.mTxPackets = mRecycledSummaryEntry.txPackets;
+    }
+
+    /**
+     * Getting the next item in summary enumeration.
+     * @param bucketOut Next item will be set here.
+     * @return true if a next item could be set.
+     */
+    private boolean getNextSummaryBucket(Bucket bucketOut) {
+        if (bucketOut != null && mEnumerationIndex < mSummary.size()) {
+            mRecycledSummaryEntry = mSummary.getValues(mEnumerationIndex++, mRecycledSummaryEntry);
+            fillBucketFromSummaryEntry(bucketOut);
+            return true;
+        }
+        return false;
+    }
+
+    Bucket getSummaryAggregate() {
+        if (mSummary == null) {
+            return null;
+        }
+        Bucket bucket = new Bucket();
+        if (mRecycledSummaryEntry == null) {
+            mRecycledSummaryEntry = new NetworkStats.Entry();
+        }
+        mSummary.getTotal(mRecycledSummaryEntry);
+        fillBucketFromSummaryEntry(bucket);
+        return bucket;
+    }
+    /**
+     * Getting the next item in a history enumeration.
+     * @param bucketOut Next item will be set here.
+     * @return true if a next item could be set.
+     */
+    private boolean getNextHistoryBucket(Bucket bucketOut) {
+        if (bucketOut != null && mHistory != null) {
+            if (mEnumerationIndex < mHistory.size()) {
+                mRecycledHistoryEntry = mHistory.getValues(mEnumerationIndex++,
+                        mRecycledHistoryEntry);
+                bucketOut.mUid = Bucket.convertUid(getUid());
+                bucketOut.mState = Bucket.STATE_ALL;
+                bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
+                bucketOut.mEndTimeStamp = mRecycledHistoryEntry.bucketStart +
+                        mRecycledHistoryEntry.bucketDuration;
+                bucketOut.mRxBytes = mRecycledHistoryEntry.rxBytes;
+                bucketOut.mRxPackets = mRecycledHistoryEntry.rxPackets;
+                bucketOut.mTxBytes = mRecycledHistoryEntry.txBytes;
+                bucketOut.mTxPackets = mRecycledHistoryEntry.txPackets;
+                return true;
+            } else if (hasNextUid()) {
+                stepHistory();
+                return getNextHistoryBucket(bucketOut);
+            }
+        }
+        return false;
+    }
+
+    // ------------------ UID LOGIC------------------------
+
+    private boolean isUidEnumeration() {
+        return mUids != null;
+    }
+
+    private boolean hasNextUid() {
+        return isUidEnumeration() && (mUidOrUidIndex + 1) < mUids.length;
+    }
+
+    private int getUid() {
+        // Check if uid enumeration.
+        if (isUidEnumeration()) {
+            if (mUidOrUidIndex < 0 || mUidOrUidIndex >= mUids.length) {
+                throw new IndexOutOfBoundsException(
+                        "Index=" + mUidOrUidIndex + " mUids.length=" + mUids.length);
+            }
+            return mUids[mUidOrUidIndex];
+        }
+        // Single uid mode.
+        return mUidOrUidIndex;
+    }
+
+    private void setSingleUid(int uid) {
+        mUidOrUidIndex = uid;
+    }
+
+    private void setUidEnumeration(int[] uids) {
+        mUids = uids;
+        mUidOrUidIndex = -1;
+    }
+
+    private void stepUid() {
+        if (mUids != null) {
+            ++mUidOrUidIndex;
+        }
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index be26eac..edb768d0 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
 import android.bluetooth.le.BluetoothLeAdvertiser;
 import android.bluetooth.le.BluetoothLeScanner;
 import android.bluetooth.le.ScanCallback;
@@ -206,6 +207,23 @@
             "android.bluetooth.adapter.action.REQUEST_ENABLE";
 
     /**
+     * Activity Action: Show a system activity that allows user to enable BLE scans even when
+     * Bluetooth is turned off.<p>
+     *
+     * Notification of result of this activity is posted using
+     * {@link android.app.Activity#onActivityResult}. The <code>resultCode</code> will be
+     * {@link android.app.Activity#RESULT_OK} if BLE scan always available setting is turned on or
+     * {@link android.app.Activity#RESULT_CANCELED} if the user has rejected the request or an
+     * error occurred.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE =
+            "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
+
+    /**
      * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter
      * has changed.
      * <p>Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link
@@ -916,6 +934,22 @@
     }
 
     /**
+     * Returns {@code true} if BLE scan is always available, {@code false} otherwise. <p>
+     *
+     * If this returns {@code true}, application can issue {@link BluetoothLeScanner#startScan} and
+     * fetch scan results even when Bluetooth is turned off.<p>
+     *
+     * To change this setting, use {@link #ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean isBleScanAlwaysAvailable() {
+        // TODO: implement after Settings UI change.
+        return false;
+    }
+
+    /**
      * Returns whether peripheral mode is supported.
      *
      * @hide
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
index 7eae439..0106686 100644
--- a/core/java/android/bluetooth/le/ScanSettings.java
+++ b/core/java/android/bluetooth/le/ScanSettings.java
@@ -25,6 +25,13 @@
  * parameters for the scan.
  */
 public final class ScanSettings implements Parcelable {
+
+    /**
+     * A special Bluetooth LE scan mode. Applications using this scan mode will passively listen for
+     * other scan results without starting BLE scans themselves.
+     */
+    public static final int SCAN_MODE_OPPORTUNISTIC = -1;
+
     /**
      * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the
      * least power.
@@ -177,7 +184,7 @@
          * @throws IllegalArgumentException If the {@code scanMode} is invalid.
          */
         public Builder setScanMode(int scanMode) {
-            if (scanMode < SCAN_MODE_LOW_POWER || scanMode > SCAN_MODE_LOW_LATENCY) {
+            if (scanMode < SCAN_MODE_OPPORTUNISTIC || scanMode > SCAN_MODE_LOW_LATENCY) {
                 throw new IllegalArgumentException("invalid scan mode " + scanMode);
             }
             mScanMode = scanMode;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 80b5e0b..9450dce 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import android.annotation.AttrRes;
 import android.annotation.CheckResult;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -474,8 +475,7 @@
      *
      * @see android.content.res.Resources.Theme#obtainStyledAttributes(int[])
      */
-    public final TypedArray obtainStyledAttributes(
-            int[] attrs) {
+    public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) {
         return getTheme().obtainStyledAttributes(attrs);
     }
 
@@ -487,7 +487,7 @@
      * @see android.content.res.Resources.Theme#obtainStyledAttributes(int, int[])
      */
     public final TypedArray obtainStyledAttributes(
-            @StyleableRes int resid, int[] attrs) throws Resources.NotFoundException {
+            @StyleRes int resid, @StyleableRes int[] attrs) throws Resources.NotFoundException {
         return getTheme().obtainStyledAttributes(resid, attrs);
     }
 
@@ -499,7 +499,7 @@
      * @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
      */
     public final TypedArray obtainStyledAttributes(
-            AttributeSet set, int[] attrs) {
+            AttributeSet set, @StyleableRes int[] attrs) {
         return getTheme().obtainStyledAttributes(set, attrs, 0, 0);
     }
 
@@ -511,7 +511,8 @@
      * @see android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
      */
     public final TypedArray obtainStyledAttributes(
-            AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) {
+            AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
+            @StyleRes int defStyleRes) {
         return getTheme().obtainStyledAttributes(
             set, attrs, defStyleAttr, defStyleRes);
     }
@@ -2149,7 +2150,7 @@
             CONNECTIVITY_SERVICE,
             //@hide: UPDATE_LOCK_SERVICE,
             //@hide: NETWORKMANAGEMENT_SERVICE,
-            //@hide: NETWORK_STATS_SERVICE,
+            NETWORK_STATS_SERVICE,
             //@hide: NETWORK_POLICY_SERVICE,
             WIFI_SERVICE,
             WIFI_PASSPOINT_SERVICE,
@@ -2259,6 +2260,9 @@
      * <dd> A {@link android.os.BatteryManager} for managing battery state
      * <dt> {@link #JOB_SCHEDULER_SERVICE} ("taskmanager")
      * <dd>  A {@link android.app.job.JobScheduler} for managing scheduled tasks
+     * <dt> {@link #NETWORK_STATS_SERVICE} ("netstats")
+     * <dd> A {@link android.app.usage.NetworkStatsManager NetworkStatsManager} for querying network
+     * usage statistics.
      * </dl>
      *
      * <p>Note:  System services obtained via this API may be closely associated with
@@ -2316,6 +2320,8 @@
      * @see android.os.BatteryManager
      * @see #JOB_SCHEDULER_SERVICE
      * @see android.app.job.JobScheduler
+     * @see #NETWORK_STATS_SERVICE
+     * @see android.app.usage.NetworkStatsManager
      */
     public abstract Object getSystemService(@ServiceName @NonNull String name);
 
@@ -2334,7 +2340,8 @@
      * {@link android.telephony.TelephonyManager}, {@link android.telephony.SubscriptionManager},
      * {@link android.view.inputmethod.InputMethodManager},
      * {@link android.app.UiModeManager}, {@link android.app.DownloadManager},
-     * {@link android.os.BatteryManager}, {@link android.app.job.JobScheduler}.
+     * {@link android.os.BatteryManager}, {@link android.app.job.JobScheduler},
+     * {@link android.app.usage.NetworkStatsManager}.
      * </p><p>
      * Note: System services obtained via this API may be closely associated with
      * the Context in which they are obtained from.  In general, do not share the
@@ -2563,7 +2570,13 @@
      */
     public static final String NETWORKMANAGEMENT_SERVICE = "network_management";
 
-    /** {@hide} */
+    /**
+     * Use with {@link #getSystemService} to retrieve a {@link
+     * android.app.usage.NetworkStatsManager} for querying network usage stats.
+     *
+     * @see #getSystemService
+     * @see android.app.usage.NetworkStatsManager
+     */
     public static final String NETWORK_STATS_SERVICE = "netstats";
     /** {@hide} */
     public static final String NETWORK_POLICY_SERVICE = "netpolicy";
@@ -2819,7 +2832,7 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a
-     * {@link android.bluetooth.BluetoothAdapter} for using Bluetooth.
+     * {@link android.bluetooth.BluetoothManager} for using Bluetooth.
      *
      * @see #getSystemService
      */
@@ -3109,6 +3122,20 @@
     public abstract int checkCallingOrSelfPermission(@NonNull String permission);
 
     /**
+     * Determine whether <em>you</em> have been granted a particular permission.
+     *
+     * @param permission The name of the permission being checked.
+     *
+     * @return {@link PackageManager#PERMISSION_GRANTED} if you have the
+     * permission, or {@link PackageManager#PERMISSION_DENIED} if not.
+     *
+     * @see PackageManager#checkPermission(String, String)
+     * @see #checkCallingPermission(String)
+     */
+    @PackageManager.PermissionResult
+    public abstract int checkSelfPermission(@NonNull String permission);
+
+    /**
      * If the given permission is not allowed for a particular process
      * and user ID running in the system, throw a {@link SecurityException}.
      *
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 6e8b7c1..8c5a87c 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -602,6 +602,11 @@
     }
 
     @Override
+    public int checkSelfPermission(String permission) {
+       return mBase.checkSelfPermission(permission);
+    }
+
+    @Override
     public void enforcePermission(
             String permission, int pid, int uid, String message) {
         mBase.enforcePermission(permission, pid, uid, message);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9be96a1..514802e9 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1041,6 +1041,18 @@
      */
     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: Send a message to someone specified by the data.
      * <p>Input: {@link #getData} is URI describing the target.
      * <p>Output: nothing.
@@ -1502,6 +1514,66 @@
      */
     public static final String METADATA_SETUP_VERSION = "android.SETUP_VERSION";
 
+    /**
+     * Activity action: Launch UI to manage the permissions of an app.
+     * <p>
+     * Input: {@link #EXTRA_PACKAGE_NAME} specifies the package whose permissions
+     * will be managed by the launched UI.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @see #EXTRA_PACKAGE_NAME
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_APP_PERMISSIONS =
+            "android.intent.action.MANAGE_APP_PERMISSIONS";
+
+    /**
+     * Intent extra: An app package name.
+     * <p>
+     * Type: String
+     * </p>S
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
+
+    /**
+     * Activity action: Launch UI to manage which apps have a given permission.
+     * <p>
+     * Input: {@link #EXTRA_PERMISSION_NAME} specifies the permission access
+     * to which will be managed by the launched UI.
+     * </p>
+     * <p>
+     * Output: Nothing.
+     * </p>
+     *
+     * @see #EXTRA_PERMISSION_NAME
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_PERMISSION_APPS =
+            "android.intent.action.MANAGE_PERMISSION_APPS";
+
+    /**
+     * Intent extra: The name of a permission.
+     * <p>
+     * Type: String
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard intent broadcast actions (see action variable).
@@ -1851,6 +1923,19 @@
     public static final String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
 
     /**
+     * Broadcast Action: Sent to the system intent filter verifier when an intent filter
+     * needs to be verified. The data contains the filter data hosts to be verified against.
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
+
+    /**
      * Broadcast Action: Resources for a set of packages (which were
      * previously unavailable) are currently
      * available since the media on which they exist is available.
@@ -3547,6 +3632,15 @@
     /** {@hide} */
     public static final String EXTRA_REASON = "android.intent.extra.REASON";
 
+    /**
+     * Optional {@link android.app.PendingIntent} extra used to deliver the result of the SIM
+     * activation request.
+     * TODO: Add information about the structure and response data used with the pending intent.
+     * @hide
+     */
+    public static final String EXTRA_SIM_ACTIVATION_RESPONSE =
+            "android.intent.extra.SIM_ACTIVATION_RESPONSE";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Intent flags (see mFlags variable).
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 1240a23..590d791 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -16,10 +16,12 @@
 
 package android.content;
 
+import android.content.pm.PackageParser;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PatternMatcher;
+import android.text.TextUtils;
 import android.util.AndroidException;
 import android.util.Log;
 import android.util.Printer;
@@ -150,6 +152,7 @@
     private static final String CAT_STR = "cat";
     private static final String NAME_STR = "name";
     private static final String ACTION_STR = "action";
+    private static final String AUTO_VERIFY_STR = "autoVerify";
 
     /**
      * The filter {@link #setPriority} value at which system high-priority
@@ -247,6 +250,19 @@
      */
     public static final int NO_MATCH_CATEGORY = -4;
 
+    /**
+     * HTTP scheme.
+     *
+     * @see #addDataScheme(String)
+     */
+    public static final String SCHEME_HTTP = "http";
+    /**
+     * HTTPS scheme.
+     *
+     * @see #addDataScheme(String)
+     */
+    public static final String SCHEME_HTTPS = "https";
+
     private int mPriority;
     private final ArrayList<String> mActions;
     private ArrayList<String> mCategories = null;
@@ -257,6 +273,13 @@
     private ArrayList<String> mDataTypes = null;
     private boolean mHasPartialTypes = false;
 
+    private static final int STATE_VERIFY_AUTO         = 0x00000001;
+    private static final int STATE_NEED_VERIFY         = 0x00000010;
+    private static final int STATE_NEED_VERIFY_CHECKED = 0x00000100;
+    private static final int STATE_VERIFIED            = 0x00001000;
+
+    private int mVerifyState;
+
     // These functions are the start of more optimized code for managing
     // the string sets...  not yet implemented.
 
@@ -326,7 +349,7 @@
         public MalformedMimeTypeException(String name) {
             super(name);
         }
-    };
+    }
 
     /**
      * Create a new IntentFilter instance with a specified action and MIME
@@ -421,6 +444,7 @@
             mDataPaths = new ArrayList<PatternMatcher>(o.mDataPaths);
         }
         mHasPartialTypes = o.mHasPartialTypes;
+        mVerifyState = o.mVerifyState;
     }
 
     /**
@@ -452,6 +476,94 @@
     }
 
     /**
+     * Set whether this filter will needs to be automatically verified against its data URIs or not.
+     * The default is false.
+     *
+     * The verification would need to happen only and only if the Intent action is
+     * {@link android.content.Intent#ACTION_VIEW} and the Intent category is
+     * {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent data scheme
+     * is "http" or "https".
+     *
+     * True means that the filter will need to use its data URIs to be verified.
+     *
+     * @param autoVerify The new autoVerify value.
+     *
+     * @see #getAutoVerify()
+     * @see #addAction(String)
+     * @see #getAction(int)
+     * @see #addCategory(String)
+     * @see #getCategory(int)
+     * @see #addDataScheme(String)
+     * @see #getDataScheme(int)
+     *
+     * @hide
+     */
+    public final void setAutoVerify(boolean autoVerify) {
+        mVerifyState &= ~STATE_VERIFY_AUTO;
+        if (autoVerify) mVerifyState |= STATE_VERIFY_AUTO;
+    }
+
+    /**
+     * Return if this filter will needs to be automatically verified again its data URIs or not.
+     *
+     * @return True if the filter will needs to be automatically verified. False otherwise.
+     *
+     * @see #setAutoVerify(boolean)
+     *
+     * @hide
+     */
+    public final boolean getAutoVerify() {
+        return ((mVerifyState & STATE_VERIFY_AUTO) == 1);
+    }
+
+    /**
+     * Return if this filter needs to be automatically verified again its data URIs or not.
+     *
+     * @return True if the filter needs to be automatically verified. False otherwise.
+     *
+     * This will check if if the Intent action is {@link android.content.Intent#ACTION_VIEW} and
+     * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent
+     * data scheme is "http" or "https".
+     *
+     * @see #setAutoVerify(boolean)
+     *
+     * @hide
+     */
+    public final boolean needsVerification() {
+        return hasAction(Intent.ACTION_VIEW) &&
+                hasCategory(Intent.CATEGORY_BROWSABLE) &&
+                (hasDataScheme(SCHEME_HTTP) || hasDataScheme(SCHEME_HTTPS)) &&
+                getAutoVerify();
+    }
+
+    /**
+     * Return if this filter has been verified
+     *
+     * @return true if the filter has been verified or if autoVerify is false.
+     *
+     * @hide
+     */
+    public final boolean isVerified() {
+        if ((mVerifyState & STATE_NEED_VERIFY_CHECKED) == STATE_NEED_VERIFY_CHECKED) {
+            return ((mVerifyState & STATE_NEED_VERIFY) == STATE_NEED_VERIFY);
+        }
+        return false;
+    }
+
+    /**
+     * Set if this filter has been verified
+     *
+     * @param verified true if this filter has been verified. False otherwise.
+     *
+     * @hide
+     */
+    public void setVerified(boolean verified) {
+        mVerifyState |= STATE_NEED_VERIFY_CHECKED;
+        mVerifyState &= ~STATE_VERIFIED;
+        if (verified) mVerifyState |= STATE_VERIFIED;
+    }
+
+    /**
      * Add a new Intent action to match against.  If any actions are included
      * in the filter, then an Intent's action must be one of those values for
      * it to match.  If no actions are included, the Intent action is ignored.
@@ -1333,6 +1445,7 @@
      * Write the contents of the IntentFilter as an XML stream.
      */
     public void writeToXml(XmlSerializer serializer) throws IOException {
+        serializer.attribute(null, AUTO_VERIFY_STR, Boolean.toString(getAutoVerify()));
         int N = countActions();
         for (int i=0; i<N; i++) {
             serializer.startTag(null, ACTION_STR);
@@ -1407,6 +1520,9 @@
 
     public void readFromXml(XmlPullParser parser) throws XmlPullParserException,
             IOException {
+        String autoVerify = parser.getAttributeValue(null, AUTO_VERIFY_STR);
+        setAutoVerify(TextUtils.isEmpty(autoVerify) ? false : Boolean.getBoolean(autoVerify));
+
         int outerDepth = parser.getDepth();
         int type;
         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1548,6 +1664,11 @@
                     sb.append(", mHasPartialTypes="); sb.append(mHasPartialTypes);
             du.println(sb.toString());
         }
+        {
+            sb.setLength(0);
+            sb.append(prefix); sb.append("AutoVerify="); sb.append(getAutoVerify());
+            du.println(sb.toString());
+        }
     }
 
     public static final Parcelable.Creator<IntentFilter> CREATOR
@@ -1614,6 +1735,7 @@
         }
         dest.writeInt(mPriority);
         dest.writeInt(mHasPartialTypes ? 1 : 0);
+        dest.writeInt(getAutoVerify() ? 1 : 0);
     }
 
     /**
@@ -1680,6 +1802,7 @@
         }
         mPriority = source.readInt();
         mHasPartialTypes = source.readInt() > 0;
+        setAutoVerify(source.readInt() > 0);
     }
 
     private final boolean findMimeType(String type) {
@@ -1724,4 +1847,27 @@
 
         return false;
     }
+
+    /**
+     * @hide
+     */
+    public ArrayList<String> getHostsList() {
+        ArrayList<String> result = new ArrayList<>();
+        Iterator<IntentFilter.AuthorityEntry> it = authoritiesIterator();
+        if (it != null) {
+            while (it.hasNext()) {
+                IntentFilter.AuthorityEntry entry = it.next();
+                result.add(entry.getHost());
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @hide
+     */
+    public String[] getHosts() {
+        ArrayList<String> list = getHostsList();
+        return list.toArray(new String[list.size()]);
+    }
 }
diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java
index 6d79626..342ee38 100644
--- a/core/java/android/content/RestrictionEntry.java
+++ b/core/java/android/content/RestrictionEntry.java
@@ -20,6 +20,9 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Arrays;
+import java.util.Objects;
+
 /**
  * Applications can expose restrictions for a restricted user on a
  * multiuser device. The administrator can configure these restrictions that will then be
@@ -33,19 +36,19 @@
 public class RestrictionEntry implements Parcelable {
 
     /**
-     * A type of restriction. Use this type for information that needs to be transferred across
-     * but shouldn't be presented to the user in the UI. Stores a single String value.
+     * Hidden restriction type. Use this type for information that needs to be transferred
+     * across but shouldn't be presented to the user in the UI. Stores a single String value.
      */
     public static final int TYPE_NULL         = 0;
 
     /**
-     * A type of restriction. Use this for storing a boolean value, typically presented as
+     * Restriction of type "bool". Use this for storing a boolean value, typically presented as
      * a checkbox in the UI.
      */
     public static final int TYPE_BOOLEAN      = 1;
 
     /**
-     * A type of restriction. Use this for storing a string value, typically presented as
+     * Restriction of type "choice". Use this for storing a string value, typically presented as
      * a single-select list. Call {@link #setChoiceEntries(String[])} and
      * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
      * and the corresponding values, respectively.
@@ -53,7 +56,7 @@
     public static final int TYPE_CHOICE       = 2;
 
     /**
-     * A type of restriction. Use this for storing a string value, typically presented as
+     * Internal restriction type. Use this for storing a string value, typically presented as
      * a single-select list. Call {@link #setChoiceEntries(String[])} and
      * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
      * and the corresponding values, respectively.
@@ -64,8 +67,8 @@
     public static final int TYPE_CHOICE_LEVEL = 3;
 
     /**
-     * A type of restriction. Use this for presenting a multi-select list where more than one
-     * entry can be selected, such as for choosing specific titles to white-list.
+     * Restriction of type "multi-select". Use this for presenting a multi-select list where more
+     * than one entry can be selected, such as for choosing specific titles to white-list.
      * Call {@link #setChoiceEntries(String[])} and
      * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
      * and the corresponding values, respectively.
@@ -75,18 +78,30 @@
     public static final int TYPE_MULTI_SELECT = 4;
 
     /**
-     * A type of restriction. Use this for storing an integer value. The range of values
+     * Restriction of type "integer". Use this for storing an integer value. The range of values
      * is from {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE}.
      */
     public static final int TYPE_INTEGER = 5;
 
     /**
-     * A type of restriction. Use this for storing a string value.
+     * Restriction of type "string". Use this for storing a string value.
      * @see #setSelectedString
      * @see #getSelectedString
      */
     public static final int TYPE_STRING = 6;
 
+    /**
+     * Restriction of type "bundle". Use this for storing {@link android.os.Bundle bundles} of
+     * restrictions
+     */
+    public static final int TYPE_BUNDLE = 7;
+
+    /**
+     * Restriction of type "bundle_array". Use this for storing arrays of
+     * {@link android.os.Bundle bundles} of restrictions
+     */
+    public static final int TYPE_BUNDLE_ARRAY = 8;
+
     /** The type of restriction. */
     private int mType;
 
@@ -100,13 +115,13 @@
     private String mDescription;
 
     /** The user-visible set of choices used for single-select and multi-select lists. */
-    private String [] mChoiceEntries;
+    private String[] mChoiceEntries;
 
     /** The values corresponding to the user-visible choices. The value(s) of this entry will
      * one or more of these, returned by {@link #getAllSelectedStrings()} and
      * {@link #getSelectedString()}.
      */
-    private String [] mChoiceValues;
+    private String[] mChoiceValues;
 
     /* The chosen value, whose content depends on the type of the restriction. */
     private String mCurrentValue;
@@ -115,6 +130,12 @@
     private String[] mCurrentValues;
 
     /**
+     * List of nested restrictions. Used by {@link #TYPE_BUNDLE bundle} and
+     * {@link #TYPE_BUNDLE_ARRAY bundle_array} restrictions.
+     */
+    private RestrictionEntry[] mRestrictions;
+
+    /**
      * Constructor for specifying the type and key, with no initial value;
      *
      * @param type the restriction type.
@@ -170,6 +191,35 @@
     }
 
     /**
+     * Constructor for {@link #TYPE_BUNDLE}/{@link #TYPE_BUNDLE_ARRAY} type.
+     * @param key the unique key for this restriction
+     * @param restrictionEntries array of nested restriction entries. If the entry, being created
+     * represents a {@link #TYPE_BUNDLE_ARRAY bundle-array}, {@code restrictionEntries} array may
+     * only contain elements of type {@link #TYPE_BUNDLE bundle}.
+     * @param isBundleArray true if this restriction represents
+     * {@link #TYPE_BUNDLE_ARRAY bundle-array} type, otherwise the type will be set to
+     * {@link #TYPE_BUNDLE bundle}.
+     */
+    public RestrictionEntry(String key, RestrictionEntry[] restrictionEntries,
+            boolean isBundleArray) {
+        mKey = key;
+        if (isBundleArray) {
+            mType = TYPE_BUNDLE_ARRAY;
+            if (restrictionEntries != null) {
+                for (RestrictionEntry restriction : restrictionEntries) {
+                    if (restriction.getType() != TYPE_BUNDLE) {
+                        throw new IllegalArgumentException("bundle_array restriction can only have "
+                                + "nested restriction entries of type bundle");
+                    }
+                }
+            }
+        } else {
+            mType = TYPE_BUNDLE;
+        }
+        setRestrictions(restrictionEntries);
+    }
+
+    /**
      * Sets the type for this restriction.
      * @param type the type for this restriction.
      */
@@ -283,6 +333,22 @@
     }
 
     /**
+     * Returns array of possible restriction entries that this entry may contain.
+     */
+    public RestrictionEntry[] getRestrictions() {
+        return mRestrictions;
+    }
+
+   /**
+    * Sets an array of possible restriction entries, that this entry may contain.
+    * <p>This method is only relevant for types {@link #TYPE_BUNDLE} and
+    * {@link #TYPE_BUNDLE_ARRAY}
+    */
+    public void setRestrictions(RestrictionEntry[] restrictions) {
+        mRestrictions = restrictions;
+    }
+
+    /**
      * Returns the list of possible string values set earlier.
      * @return the list of possible values.
      */
@@ -362,27 +428,30 @@
         this.mTitle = title;
     }
 
-    private boolean equalArrays(String[] one, String[] other) {
-        if (one.length != other.length) return false;
-        for (int i = 0; i < one.length; i++) {
-            if (!one[i].equals(other[i])) return false;
-        }
-        return true;
-    }
-
     @Override
     public boolean equals(Object o) {
         if (o == this) return true;
         if (!(o instanceof RestrictionEntry)) return false;
         final RestrictionEntry other = (RestrictionEntry) o;
-        // Make sure that either currentValue matches or currentValues matches.
-        return mType == other.mType && mKey.equals(other.mKey)
-                &&
-                ((mCurrentValues == null && other.mCurrentValues == null
-                  && mCurrentValue != null && mCurrentValue.equals(other.mCurrentValue))
-                 ||
-                 (mCurrentValue == null && other.mCurrentValue == null
-                  && mCurrentValues != null && equalArrays(mCurrentValues, other.mCurrentValues)));
+        if (mType != other.mType || mKey.equals(other.mKey)) {
+            return false;
+        }
+        if (mCurrentValues == null && other.mCurrentValues == null
+                && mRestrictions == null && other.mRestrictions == null
+                && Objects.equals(mCurrentValue, other.mCurrentValue)) {
+            return true;
+        }
+        if (mCurrentValue == null && other.mCurrentValue == null
+                && mRestrictions == null && other.mRestrictions == null
+                && Arrays.equals(mCurrentValues, other.mCurrentValues)) {
+            return true;
+        }
+        if (mCurrentValue == null && other.mCurrentValue == null
+                && mCurrentValue == null && other.mCurrentValue == null
+                && Arrays.equals(mRestrictions, other.mRestrictions)) {
+            return true;
+        }
+        return false;
     }
 
     @Override
@@ -397,28 +466,28 @@
                     result = 31 * result + value.hashCode();
                 }
             }
+        } else if (mRestrictions != null) {
+            result = 31 * result + Arrays.hashCode(mRestrictions);
         }
         return result;
     }
 
-    private String[] readArray(Parcel in) {
-        int count = in.readInt();
-        String[] values = new String[count];
-        for (int i = 0; i < count; i++) {
-            values[i] = in.readString();
-        }
-        return values;
-    }
-
     public RestrictionEntry(Parcel in) {
         mType = in.readInt();
         mKey = in.readString();
         mTitle = in.readString();
         mDescription = in.readString();
-        mChoiceEntries = readArray(in);
-        mChoiceValues = readArray(in);
+        mChoiceEntries = in.readStringArray();
+        mChoiceValues = in.readStringArray();
         mCurrentValue = in.readString();
-        mCurrentValues = readArray(in);
+        mCurrentValues = in.readStringArray();
+        Parcelable[] parcelables = in.readParcelableArray(null);
+        if (parcelables != null) {
+            mRestrictions = new RestrictionEntry[parcelables.length];
+            for (int i = 0; i < parcelables.length; i++) {
+                mRestrictions[i] = (RestrictionEntry) parcelables[i];
+            }
+        }
     }
 
     @Override
@@ -426,27 +495,17 @@
         return 0;
     }
 
-    private void writeArray(Parcel dest, String[] values) {
-        if (values == null) {
-            dest.writeInt(0);
-        } else {
-            dest.writeInt(values.length);
-            for (int i = 0; i < values.length; i++) {
-                dest.writeString(values[i]);
-            }
-        }
-    }
-
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mType);
         dest.writeString(mKey);
         dest.writeString(mTitle);
         dest.writeString(mDescription);
-        writeArray(dest, mChoiceEntries);
-        writeArray(dest, mChoiceValues);
+        dest.writeStringArray(mChoiceEntries);
+        dest.writeStringArray(mChoiceValues);
         dest.writeString(mCurrentValue);
-        writeArray(dest, mCurrentValues);
+        dest.writeStringArray(mCurrentValues);
+        dest.writeParcelableArray(mRestrictions, 0);
     }
 
     public static final Creator<RestrictionEntry> CREATOR = new Creator<RestrictionEntry>() {
@@ -461,6 +520,16 @@
 
     @Override
     public String toString() {
-        return "RestrictionsEntry {type=" + mType + ", key=" + mKey + ", value=" + mCurrentValue + "}";
+        return "RestrictionEntry{" +
+                "mType=" + mType +
+                ", mKey='" + mKey + '\'' +
+                ", mTitle='" + mTitle + '\'' +
+                ", mDescription='" + mDescription + '\'' +
+                ", mChoiceEntries=" + Arrays.toString(mChoiceEntries) +
+                ", mChoiceValues=" + Arrays.toString(mChoiceValues) +
+                ", mCurrentValue='" + mCurrentValue + '\'' +
+                ", mCurrentValues=" + Arrays.toString(mCurrentValues) +
+                ", mRestrictions=" + Arrays.toString(mRestrictions) +
+                '}';
     }
 }
diff --git a/core/java/android/content/RestrictionsManager.java b/core/java/android/content/RestrictionsManager.java
index 21a6a0d..1fac06e 100644
--- a/core/java/android/content/RestrictionsManager.java
+++ b/core/java/android/content/RestrictionsManager.java
@@ -32,12 +32,14 @@
 import android.util.Xml;
 
 import com.android.internal.R;
+import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -71,12 +73,15 @@
  *         android:key="string"
  *         android:title="string resource"
  *         android:restrictionType=["bool" | "string" | "integer"
- *                                         | "choice" | "multi-select" | "hidden"]
+ *                                         | "choice" | "multi-select" | "hidden"
+ *                                         | "bundle" | "bundle_array"]
  *         android:description="string resource"
  *         android:entries="string-array resource"
  *         android:entryValues="string-array resource"
- *         android:defaultValue="reference"
- *         /&gt;
+ *         android:defaultValue="reference" &gt;
+ *             &lt;restriction ... /&gt;
+ *             ...
+ *     &lt;/restriction&gt;
  *     &lt;restriction ... /&gt;
  *     ...
  * &lt;/restrictions&gt;
@@ -97,6 +102,9 @@
  * administrator controlling the values, if the title is not sufficient.</li>
  * </ul>
  * <p>
+ * Only restrictions of type {@code bundle} and {@code bundle_array} can have one or multiple nested
+ * restriction elements.
+ * <p>
  * In your manifest's <code>application</code> section, add the meta-data tag to point to
  * the restrictions XML file as shown below:
  * <pre>
@@ -537,9 +545,7 @@
 
         XmlResourceParser xml =
                 appInfo.loadXmlMetaData(mContext.getPackageManager(), META_DATA_APP_RESTRICTIONS);
-        List<RestrictionEntry> restrictions = loadManifestRestrictions(packageName, xml);
-
-        return restrictions;
+        return loadManifestRestrictions(packageName, xml);
     }
 
     private List<RestrictionEntry> loadManifestRestrictions(String packageName,
@@ -550,23 +556,16 @@
         } catch (NameNotFoundException nnfe) {
             return null;
         }
-        ArrayList<RestrictionEntry> restrictions = new ArrayList<RestrictionEntry>();
+        ArrayList<RestrictionEntry> restrictions = new ArrayList<>();
         RestrictionEntry restriction;
 
         try {
             int tagType = xml.next();
             while (tagType != XmlPullParser.END_DOCUMENT) {
                 if (tagType == XmlPullParser.START_TAG) {
-                    if (xml.getName().equals(TAG_RESTRICTION)) {
-                        AttributeSet attrSet = Xml.asAttributeSet(xml);
-                        if (attrSet != null) {
-                            TypedArray a = appContext.obtainStyledAttributes(attrSet,
-                                    com.android.internal.R.styleable.RestrictionEntry);
-                            restriction = loadRestriction(appContext, a);
-                            if (restriction != null) {
-                                restrictions.add(restriction);
-                            }
-                        }
+                    restriction = loadRestrictionElement(appContext, xml);
+                    if (restriction != null) {
+                        restrictions.add(restriction);
                     }
                 }
                 tagType = xml.next();
@@ -582,7 +581,21 @@
         return restrictions;
     }
 
-    private RestrictionEntry loadRestriction(Context appContext, TypedArray a) {
+    private RestrictionEntry loadRestrictionElement(Context appContext, XmlResourceParser xml)
+            throws IOException, XmlPullParserException {
+        if (xml.getName().equals(TAG_RESTRICTION)) {
+            AttributeSet attrSet = Xml.asAttributeSet(xml);
+            if (attrSet != null) {
+                TypedArray a = appContext.obtainStyledAttributes(attrSet,
+                        com.android.internal.R.styleable.RestrictionEntry);
+                return loadRestriction(appContext, a, xml);
+            }
+        }
+        return null;
+    }
+
+    private RestrictionEntry loadRestriction(Context appContext, TypedArray a, XmlResourceParser xml)
+            throws IOException, XmlPullParserException {
         String key = a.getString(R.styleable.RestrictionEntry_key);
         int restrictionType = a.getInt(
                 R.styleable.RestrictionEntry_restrictionType, -1);
@@ -633,9 +646,90 @@
                 restriction.setSelectedState(
                         a.getBoolean(R.styleable.RestrictionEntry_defaultValue, false));
                 break;
+            case RestrictionEntry.TYPE_BUNDLE:
+            case RestrictionEntry.TYPE_BUNDLE_ARRAY:
+                final int outerDepth = xml.getDepth();
+                List<RestrictionEntry> restrictionEntries = new ArrayList<>();
+                while (XmlUtils.nextElementWithin(xml, outerDepth)) {
+                    RestrictionEntry childEntry = loadRestrictionElement(appContext, xml);
+                    if (childEntry == null) {
+                        Log.w(TAG, "Child entry cannot be loaded for bundle restriction " + key);
+                    } else {
+                        restrictionEntries.add(childEntry);
+                        if (restrictionType == RestrictionEntry.TYPE_BUNDLE_ARRAY
+                                && childEntry.getType() != RestrictionEntry.TYPE_BUNDLE) {
+                            Log.w(TAG, "bundle_array " + key
+                                    + " can only contain entries of type bundle");
+                        }
+                    }
+                }
+                restriction.setRestrictions(restrictionEntries.toArray(new RestrictionEntry[
+                        restrictionEntries.size()]));
+                break;
             default:
                 Log.w(TAG, "Unknown restriction type " + restrictionType);
         }
         return restriction;
     }
+
+    /**
+     * Converts a list of restrictions to the corresponding bundle, using the following mapping:
+     * <table>
+     *     <tr><th>RestrictionEntry</th><th>Bundle</th></tr>
+     *     <tr><td>{@link RestrictionEntry#TYPE_BOOLEAN}</td><td>{@link Bundle#putBoolean}</td></tr>
+     *     <tr><td>{@link RestrictionEntry#TYPE_CHOICE}, {@link RestrictionEntry#TYPE_CHOICE}</td>
+     *     <td>{@link Bundle#putStringArray}</td></tr>
+     *     <tr><td>{@link RestrictionEntry#TYPE_INTEGER}</td><td>{@link Bundle#putInt}</td></tr>
+     *     <tr><td>{@link RestrictionEntry#TYPE_STRING}</td><td>{@link Bundle#putString}</td></tr>
+     *     <tr><td>{@link RestrictionEntry#TYPE_BUNDLE}</td><td>{@link Bundle#putBundle}</td></tr>
+     *     <tr><td>{@link RestrictionEntry#TYPE_BUNDLE_ARRAY}</td>
+     *     <td>{@link Bundle#putParcelableArray}</td></tr>
+     * </table>
+     * @param entries list of restrictions
+     */
+    public static Bundle convertRestrictionsToBundle(List<RestrictionEntry> entries) {
+        final Bundle bundle = new Bundle();
+        for (RestrictionEntry entry : entries) {
+            addRestrictionToBundle(bundle, entry);
+        }
+        return bundle;
+    }
+
+    private static Bundle addRestrictionToBundle(Bundle bundle, RestrictionEntry entry) {
+        switch (entry.getType()) {
+            case RestrictionEntry.TYPE_BOOLEAN:
+                bundle.putBoolean(entry.getKey(), entry.getSelectedState());
+                break;
+            case RestrictionEntry.TYPE_CHOICE:
+            case RestrictionEntry.TYPE_CHOICE_LEVEL:
+            case RestrictionEntry.TYPE_MULTI_SELECT:
+                bundle.putStringArray(entry.getKey(), entry.getAllSelectedStrings());
+                break;
+            case RestrictionEntry.TYPE_INTEGER:
+                bundle.putInt(entry.getKey(), entry.getIntValue());
+                break;
+            case RestrictionEntry.TYPE_STRING:
+            case RestrictionEntry.TYPE_NULL:
+                bundle.putString(entry.getKey(), entry.getSelectedString());
+                break;
+            case RestrictionEntry.TYPE_BUNDLE:
+                RestrictionEntry[] restrictions = entry.getRestrictions();
+                Bundle childBundle = convertRestrictionsToBundle(Arrays.asList(restrictions));
+                bundle.putBundle(entry.getKey(), childBundle);
+                break;
+            case RestrictionEntry.TYPE_BUNDLE_ARRAY:
+                restrictions = entry.getRestrictions();
+                Bundle[] bundleArray = new Bundle[restrictions.length];
+                for (int i = 0; i < restrictions.length; i++) {
+                    bundleArray[i] = addRestrictionToBundle(new Bundle(), restrictions[i]);
+                }
+                bundle.putParcelableArray(entry.getKey(), bundleArray);
+                break;
+            default:
+                throw new IllegalArgumentException(
+                        "Unsupported restrictionEntry type: " + entry.getType());
+        }
+        return bundle;
+    }
+
 }
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 29befc8..2496e45 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -340,12 +340,15 @@
      * cleartext network traffic, in which case platform components (e.g., HTTP stacks,
      * {@code WebView}, {@code MediaPlayer}) will refuse app's requests to use cleartext traffic.
      * Third-party libraries are encouraged to honor this flag as well.
-     *
-     * @hide
      */
     public static final int FLAG_USES_CLEARTEXT_TRAFFIC = 1<<27;
 
     /**
+     * When set installer extracts native libs from .apk files.
+     */
+    public static final int FLAG_EXTRACT_NATIVE_LIBS = 1<<28;
+
+    /**
      * Value for {@link #flags}: true if code from this application will need to be
      * loaded into other applications' processes. On devices that support multiple
      * instruction sets, this implies the code might be loaded into a process that's
@@ -374,7 +377,8 @@
      * {@link #FLAG_LARGE_HEAP}, {@link #FLAG_STOPPED},
      * {@link #FLAG_SUPPORTS_RTL}, {@link #FLAG_INSTALLED},
      * {@link #FLAG_IS_DATA_ONLY}, {@link #FLAG_IS_GAME},
-     * {@link #FLAG_FULL_BACKUP_ONLY}, {@link #FLAG_MULTIARCH}.
+     * {@link #FLAG_FULL_BACKUP_ONLY}, {@link #FLAG_USES_CLEARTEXT_TRAFFIC},
+     * {@link #FLAG_MULTIARCH}.
      */
     public int flags = 0;
 
@@ -650,7 +654,7 @@
         }
         pw.println(prefix + "dataDir=" + dataDir);
         if (sharedLibraryFiles != null) {
-            pw.println(prefix + "sharedLibraryFiles=" + sharedLibraryFiles);
+            pw.println(prefix + "sharedLibraryFiles=" + Arrays.toString(sharedLibraryFiles));
         }
         pw.println(prefix + "enabled=" + enabled + " targetSdkVersion=" + targetSdkVersion
                 + " versionCode=" + versionCode);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 3e5d362..66b0d1a 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -31,6 +31,7 @@
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
+import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.KeySet;
 import android.content.pm.PackageInfo;
@@ -46,32 +47,34 @@
 import android.content.pm.VerificationParams;
 import android.content.pm.VerifierDeviceIdentity;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.content.IntentSender;
+import com.android.internal.os.IResultReceiver;
 
 /**
  *  See {@link PackageManager} for documentation on most of the APIs
  *  here.
- * 
+ *
  *  {@hide}
  */
 interface IPackageManager {
     boolean isPackageAvailable(String packageName, int userId);
     PackageInfo getPackageInfo(String packageName, int flags, int userId);
     int getPackageUid(String packageName, int userId);
-    int[] getPackageGids(String packageName);
-    
+    int[] getPackageGids(String packageName, int userId);
+
     String[] currentToCanonicalPackageNames(in String[] names);
     String[] canonicalToCurrentPackageNames(in String[] names);
 
     PermissionInfo getPermissionInfo(String name, int flags);
-    
+
     List<PermissionInfo> queryPermissionsByGroup(String group, int flags);
-    
+
     PermissionGroupInfo getPermissionGroupInfo(String name, int flags);
-    
+
     List<PermissionGroupInfo> getAllPermissionGroups(int flags);
-    
+
     ApplicationInfo getApplicationInfo(String packageName, int flags ,int userId);
 
     ActivityInfo getActivityInfo(in ComponentName className, int flags, int userId);
@@ -85,28 +88,28 @@
 
     ProviderInfo getProviderInfo(in ComponentName className, int flags, int userId);
 
-    int checkPermission(String permName, String pkgName);
-    
+    int checkPermission(String permName, String pkgName, int userId);
+
     int checkUidPermission(String permName, int uid);
-    
+
     boolean addPermission(in PermissionInfo info);
-    
+
     void removePermission(String name);
 
-    void grantPermission(String packageName, String permissionName);
+    boolean grantPermission(String packageName, String permissionName, int userId);
 
-    void revokePermission(String packageName, String permissionName);
+    boolean revokePermission(String packageName, String permissionName, int userId);
 
     boolean isProtectedBroadcast(String actionName);
-    
+
     int checkSignatures(String pkg1, String pkg2);
-    
+
     int checkUidSignatures(int uid1, int uid2);
-    
+
     String[] getPackagesForUid(int uid);
-    
+
     String getNameForUid(int uid);
-    
+
     int getUidForSharedUser(String sharedUserName);
 
     int getFlagsForUid(int uid);
@@ -121,7 +124,7 @@
 
     boolean canForwardTo(in Intent intent, String resolvedType, int sourceUserId, int targetUserId);
 
-    List<ResolveInfo> queryIntentActivities(in Intent intent, 
+    List<ResolveInfo> queryIntentActivities(in Intent intent,
             String resolvedType, int flags, int userId);
 
     List<ResolveInfo> queryIntentActivityOptions(
@@ -168,7 +171,7 @@
 
     /**
      * Retrieve all applications that are marked as persistent.
-     * 
+     *
      * @return A List&lt;applicationInfo> containing one entry for each persistent
      *         application.
      */
@@ -178,7 +181,7 @@
 
     /**
      * Retrieve sync information for all content providers.
-     * 
+     *
      * @param outNames Filled in with a list of the root names of the content
      *                 providers that can sync.
      * @param outInfo Filled in with a list of the ProviderInfo for each
@@ -434,6 +437,11 @@
     void verifyPendingInstall(int id, int verificationCode);
     void extendVerificationTimeout(int id, int verificationCodeAtTimeout, long millisecondsToDelay);
 
+    void verifyIntentFilter(int id, int verificationCode, in List<String> outFailedDomains);
+    int getIntentVerificationStatus(String packageName, int userId);
+    boolean updateIntentVerificationStatus(String packageName, int status, int userId);
+    List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName);
+
     VerifierDeviceIdentity getVerifierDeviceIdentity();
 
     boolean isFirstBoot();
diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.aidl b/core/java/android/content/pm/IntentFilterVerificationInfo.aidl
new file mode 100644
index 0000000..00220e5
--- /dev/null
+++ b/core/java/android/content/pm/IntentFilterVerificationInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.content.pm;
+
+parcelable IntentFilterVerificationInfo;
diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.java b/core/java/android/content/pm/IntentFilterVerificationInfo.java
new file mode 100644
index 0000000..60cb4a8
--- /dev/null
+++ b/core/java/android/content/pm/IntentFilterVerificationInfo.java
@@ -0,0 +1,224 @@
+/*
+ * 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 android.content.pm;
+
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * The {@link com.android.server.pm.PackageManagerService} maintains some
+ * {@link IntentFilterVerificationInfo}s for each domain / package / class name per user.
+ *
+ * @hide
+ */
+public final class IntentFilterVerificationInfo implements Parcelable {
+    private static final String TAG = IntentFilterVerificationInfo.class.getName();
+
+    private static final String TAG_DOMAIN = "domain";
+    private static final String ATTR_DOMAIN_NAME = "name";
+    private static final String ATTR_PACKAGE_NAME = "packageName";
+    private static final String ATTR_STATUS = "status";
+
+    private String[] mDomains;
+    private String mPackageName;
+    private int mMainStatus;
+
+    public IntentFilterVerificationInfo() {
+        mPackageName = null;
+        mDomains = new String[0];
+        mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+    }
+
+    public IntentFilterVerificationInfo(String packageName, String[] domains) {
+        mPackageName = packageName;
+        mDomains = domains;
+        mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+    }
+
+    public IntentFilterVerificationInfo(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        readFromXml(parser);
+    }
+
+    public IntentFilterVerificationInfo(Parcel source) {
+        readFromParcel(source);
+    }
+
+    public String[] getDomains() {
+        return mDomains;
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public int getStatus() {
+        return mMainStatus;
+    }
+
+    public void setStatus(int s) {
+        if (s >= INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED &&
+                s <= INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
+            mMainStatus = s;
+        } else {
+            Log.w(TAG, "Trying to set a non supported status: " + s);
+        }
+    }
+
+    public String getDomainsString() {
+        StringBuilder sb = new StringBuilder();
+        for (String str : mDomains) {
+            if (sb.length() > 0) {
+                sb.append(" ");
+            }
+            sb.append(str);
+        }
+        return sb.toString();
+    }
+
+    String getStringFromXml(XmlPullParser parser, String attribute, String defaultValue) {
+        String value = parser.getAttributeValue(null, attribute);
+        if (value == null) {
+            String msg = "Missing element under " + TAG +": " + attribute + " at " +
+                    parser.getPositionDescription();
+            Log.w(TAG, msg);
+            return defaultValue;
+        } else {
+            return value;
+        }
+    }
+
+    int getIntFromXml(XmlPullParser parser, String attribute, int defaultValue) {
+        String value = parser.getAttributeValue(null, attribute);
+        if (TextUtils.isEmpty(value)) {
+            String msg = "Missing element under " + TAG +": " + attribute + " at " +
+                    parser.getPositionDescription();
+            Log.w(TAG, msg);
+            return defaultValue;
+        } else {
+            return Integer.parseInt(value);
+        }
+    }
+
+    public void readFromXml(XmlPullParser parser) throws XmlPullParserException,
+            IOException {
+        mPackageName = getStringFromXml(parser, ATTR_PACKAGE_NAME, null);
+        if (mPackageName == null) {
+            Log.e(TAG, "Package name cannot be null!");
+        }
+        int status = getIntFromXml(parser, ATTR_STATUS, -1);
+        if (status == -1) {
+            Log.e(TAG, "Unknown status value: " + status);
+        }
+        mMainStatus = status;
+
+        ArrayList<String> list = new ArrayList<>();
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG
+                    || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals(TAG_DOMAIN)) {
+                String name = getStringFromXml(parser, ATTR_DOMAIN_NAME, null);
+                if (!TextUtils.isEmpty(name)) {
+                    if (list == null) {
+                        list = new ArrayList<>();
+                    }
+                    list.add(name);
+                }
+            } else {
+                Log.w(TAG, "Unknown tag parsing IntentFilter: " + tagName);
+            }
+            XmlUtils.skipCurrentTag(parser);
+        }
+
+        mDomains = list.toArray(new String[list.size()]);
+    }
+
+    public void writeToXml(XmlSerializer serializer) throws IOException {
+        serializer.attribute(null, ATTR_PACKAGE_NAME, mPackageName);
+        serializer.attribute(null, ATTR_STATUS, String.valueOf(mMainStatus));
+        for (String str : mDomains) {
+            serializer.startTag(null, TAG_DOMAIN);
+            serializer.attribute(null, ATTR_DOMAIN_NAME, str);
+            serializer.endTag(null, TAG_DOMAIN);
+        }
+    }
+
+    public String getStatusString() {
+        return getStatusStringFromValue(mMainStatus);
+    }
+
+    public static String getStatusStringFromValue(int val) {
+        switch (val) {
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK       : return "ask";
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS    : return "always";
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER     : return "never";
+            default:
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED : return "undefined";
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    private void readFromParcel(Parcel source) {
+        mPackageName = source.readString();
+        mMainStatus = source.readInt();
+        mDomains = source.readStringArray();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mPackageName);
+        dest.writeInt(mMainStatus);
+        dest.writeStringArray(mDomains);
+    }
+
+    public static final Creator<IntentFilterVerificationInfo> CREATOR =
+            new Creator<IntentFilterVerificationInfo>() {
+                public IntentFilterVerificationInfo createFromParcel(Parcel source) {
+                    return new IntentFilterVerificationInfo(source);
+                }
+                public IntentFilterVerificationInfo[] newArray(int size) {
+                    return new IntentFilterVerificationInfo[size];
+                }
+            };
+
+}
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 9223269..9e6c6b5 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -167,8 +167,7 @@
      * or null if there were none.  This is only filled in if the flag
      * {@link PackageManager#GET_PERMISSIONS} was set.  Each value matches
      * the corresponding entry in {@link #requestedPermissions}, and will have
-     * the flags {@link #REQUESTED_PERMISSION_REQUIRED} and
-     * {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate.
+     * the flag {@link #REQUESTED_PERMISSION_GRANTED} set as appropriate.
      */
     public int[] requestedPermissionsFlags;
 
@@ -176,6 +175,8 @@
      * Flag for {@link #requestedPermissionsFlags}: the requested permission
      * is required for the application to run; the user can not optionally
      * disable it.  Currently all permissions are required.
+     *
+     * @removed We do not support required permissions.
      */
     public static final int REQUESTED_PERMISSION_REQUIRED = 1<<0;
 
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 3da57cb..46d6ffb3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -44,6 +44,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.AndroidException;
+import com.android.internal.util.ArrayUtils;
 
 import java.io.File;
 import java.lang.annotation.Retention;
@@ -377,6 +378,16 @@
     public static final int INSTALL_ALLOW_DOWNGRADE = 0x00000080;
 
     /**
+     * Flag parameter for {@link #installPackage} to indicate that all runtime
+     * permissions should be granted to the package. If {@link #INSTALL_ALL_USERS}
+     * is set the runtime permissions will be granted to all users, otherwise
+     * only to the owner.
+     *
+     * @hide
+     */
+    public static final int INSTALL_GRANT_RUNTIME_PERMISSIONS = 0x00000100;
+
+    /**
      * Flag parameter for
      * {@link #setComponentEnabledSetting(android.content.ComponentName, int, int)} to indicate
      * that you don't want to kill the app containing the component.  Be careful when you set this
@@ -958,6 +969,60 @@
     public static final int VERIFICATION_REJECT = -1;
 
     /**
+     * Used as the {@code verificationCode} argument for
+     * {@link PackageManager#verifyIntentFilter} to indicate that the calling
+     * IntentFilter Verifier confirms that the IntentFilter is verified.
+     *
+     * @hide
+     */
+    public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1;
+
+    /**
+     * Used as the {@code verificationCode} argument for
+     * {@link PackageManager#verifyIntentFilter} to indicate that the calling
+     * IntentFilter Verifier confirms that the IntentFilter is NOT verified.
+     *
+     * @hide
+     */
+    public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1;
+
+    /**
+     * Internal status code to indicate that an IntentFilter verification result is not specified.
+     *
+     * @hide
+     */
+    public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED = 0;
+
+    /**
+     * Used as the {@code status} argument for {@link PackageManager#updateIntentVerificationStatus}
+     * to indicate that the User will always be prompted the Intent Disambiguation Dialog if there
+     * are two or more Intent resolved for the IntentFilter's domain(s).
+     *
+     * @hide
+     */
+    public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK = 1;
+
+    /**
+     * Used as the {@code status} argument for {@link PackageManager#updateIntentVerificationStatus}
+     * to indicate that the User will never be prompted the Intent Disambiguation Dialog if there
+     * are two or more resolution of the Intent. The default App for the domain(s) specified in the
+     * IntentFilter will also ALWAYS be used.
+     *
+     * @hide
+     */
+    public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS = 2;
+
+    /**
+     * Used as the {@code status} argument for {@link PackageManager#updateIntentVerificationStatus}
+     * to indicate that the User may be prompted the Intent Disambiguation Dialog if there
+     * are two or more Intent resolved. The default App for the domain(s) specified in the
+     * IntentFilter will also NEVER be presented to the User.
+     *
+     * @hide
+     */
+    public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER = 3;
+
+    /**
      * Can be used as the {@code millisecondsToDelay} argument for
      * {@link PackageManager#extendVerificationTimeout}. This is the
      * maximum time {@code PackageManager} waits for the verification
@@ -1589,6 +1654,12 @@
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_GAMEPAD = "android.hardware.gamepad";
 
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device has a full implementation of the android.media.midi.* APIs.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_MIDI = "android.software.midi";
 
     /**
      * Action to external storage service to clean out removed apps.
@@ -1663,21 +1734,90 @@
             = "android.content.pm.extra.VERIFICATION_VERSION_CODE";
 
     /**
+     * Extra field name for the ID of a intent filter pending verification. Passed to
+     * an intent filter verifier and is used to call back to
+     * {@link PackageManager#verifyIntentFilter(int, int)}
+     *
+     * @hide
+     */
+    public static final String EXTRA_INTENT_FILTER_VERIFICATION_ID
+            = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_ID";
+
+    /**
+     * Extra field name for the scheme used for an intent filter pending verification. Passed to
+     * an intent filter verifier and is used to construct the URI to verify against.
+     *
+     * Usually this is "https"
+     *
+     * @hide
+     */
+    public static final String EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME
+            = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_URI_SCHEME";
+
+    /**
+     * Extra field name for the host names to be used for an intent filter pending verification.
+     * Passed to an intent filter verifier and is used to construct the URI to verify the
+     * intent filter.
+     *
+     * This is a space delimited list of hosts.
+     *
+     * @hide
+     */
+    public static final String EXTRA_INTENT_FILTER_VERIFICATION_HOSTS
+            = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_HOSTS";
+
+    /**
+     * Extra field name for the package name to be used for an intent filter pending verification.
+     * Passed to an intent filter verifier and is used to check the verification responses coming
+     * from the hosts. Each host response will need to include the package name of APK containing
+     * the intent filter.
+     *
+     * @hide
+     */
+    public static final String EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME
+            = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_PACKAGE_NAME";
+
+    /**
      * The action used to request that the user approve a permission request
      * from the application.
      *
      * @hide
      */
-    public static final String ACTION_REQUEST_PERMISSION
-            = "android.content.pm.action.REQUEST_PERMISSION";
+    @SystemApi
+    public static final String ACTION_REQUEST_PERMISSIONS =
+            "android.content.pm.action.REQUEST_PERMISSIONS";
 
     /**
-     * Extra field name for the list of permissions, which the user must approve.
+     * The component name handling runtime permission grants.
      *
      * @hide
      */
-    public static final String EXTRA_REQUEST_PERMISSION_PERMISSION_LIST
-            = "android.content.pm.extra.PERMISSION_LIST";
+    public static final String GRANT_PERMISSIONS_PACKAGE_NAME =
+            "com.android.packageinstaller";
+
+    /**
+     * The names of the requested permissions.
+     * <p>
+     * <strong>Type:</strong> String[]
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_REQUEST_PERMISSIONS_NAMES =
+            "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
+
+    /**
+     * The results from the permissions request.
+     * <p>
+     * <strong>Type:</strong> int[] of #PermissionResult
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS
+            = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS";
 
     /**
      * String extra for {@link PackageInstallObserver} in the 'extras' Bundle in case of
@@ -2184,53 +2324,72 @@
     public abstract void removePermission(String name);
 
     /**
-     * Returns an {@link Intent} suitable for passing to {@code startActivityForResult}
-     * which prompts the user to grant {@code permissions} to this application.
-     * @hide
+     * Grant a runtime permission to an application which the application does not
+     * already have. The permission must have been requested by the application.
+     * If the application is not allowed to hold the permission, a {@link
+     * java.lang.SecurityException} is thrown.
+     * <p>
+     * <strong>Note: </strong>Using this API requires holding
+     * android.permission.GRANT_REVOKE_PERMISSIONS and if the user id is
+     * not the current user android.permission.INTERACT_ACROSS_USERS_FULL.
+     * </p>
      *
-     * @throws NullPointerException if {@code permissions} is {@code null}.
-     * @throws IllegalArgumentException if {@code permissions} contains {@code null}.
+     * @param packageName The package to which to grant the permission.
+     * @param permissionName The permission name to grant.
+     * @param user The user for which to grant the permission.
+     *
+     * @see #revokePermission(String, String, android.os.UserHandle)
+     *
+     * @hide
      */
-    public Intent buildPermissionRequestIntent(String... permissions) {
-        if (permissions == null) {
-            throw new NullPointerException("permissions cannot be null");
-        }
-        for (String permission : permissions) {
-            if (permission == null) {
-                throw new IllegalArgumentException("permissions cannot contain null");
-            }
-        }
+    @SystemApi
+    public abstract void grantPermission(@NonNull String packageName,
+            @NonNull String permissionName, @NonNull UserHandle user);
 
-        Intent i = new Intent(ACTION_REQUEST_PERMISSION);
-        i.putExtra(EXTRA_REQUEST_PERMISSION_PERMISSION_LIST, permissions);
-        i.setPackage("com.android.packageinstaller");
-        return i;
+    /**
+     * Revoke a runtime permission that was previously granted by {@link
+     * #grantPermission(String, String, android.os.UserHandle)}. The permission
+     * must have been requested by and granted to the application. If the
+     * application is not allowed to hold the permission, a {@link
+     * java.lang.SecurityException} is thrown.
+     * <p>
+     * <strong>Note: </strong>Using this API requires holding
+     * android.permission.GRANT_REVOKE_PERMISSIONS and if the user id is
+     * not the current user android.permission.INTERACT_ACROSS_USERS_FULL.
+     * </p>
+     *
+     * @param packageName The package from which to revoke the permission.
+     * @param permissionName The permission name to revoke.
+     * @param user The user for which to revoke the permission.
+     *
+     * @see #grantPermission(String, String, android.os.UserHandle)
+     *
+     * @hide
+     */
+    @SystemApi
+    public abstract void revokePermission(@NonNull String packageName,
+            @NonNull String permissionName, @NonNull UserHandle user);
+
+    /**
+     * Returns an {@link android.content.Intent} suitable for passing to
+     * {@link android.app.Activity#startActivityForResult(android.content.Intent, int)}
+     * which prompts the user to grant permissions to this application.
+     *
+     * @throws NullPointerException if {@code permissions} is {@code null} or empty.
+     *
+     * @hide
+     */
+    public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) {
+        if (ArrayUtils.isEmpty(permissions)) {
+           throw new NullPointerException("permission cannot be null or empty");
+        }
+        Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
+        intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);
+        intent.setPackage(GRANT_PERMISSIONS_PACKAGE_NAME);
+        return intent;
     }
 
     /**
-     * Grant a permission to an application which the application does not
-     * already have.  The permission must have been requested by the application,
-     * but as an optional permission.  If the application is not allowed to
-     * hold the permission, a SecurityException is thrown.
-     * @hide
-     *
-     * @param packageName The name of the package that the permission will be
-     * granted to.
-     * @param permissionName The name of the permission.
-     */
-    public abstract void grantPermission(String packageName, String permissionName);
-
-    /**
-     * Revoke a permission that was previously granted by {@link #grantPermission}.
-     * @hide
-     *
-     * @param packageName The name of the package that the permission will be
-     * granted to.
-     * @param permissionName The name of the permission.
-     */
-    public abstract void revokePermission(String packageName, String permissionName);
-
-    /**
      * Compare the signatures of two packages to determine if the same
      * signature appears in both of them.  If they do contain the same
      * signature, then they are allowed special privileges when working
@@ -3400,6 +3559,85 @@
             int verificationCodeAtTimeout, long millisecondsToDelay);
 
     /**
+     * Allows a package listening to the
+     * {@link Intent#ACTION_INTENT_FILTER_NEEDS_VERIFICATION intent filter verification
+     * broadcast} to respond to the package manager. The response must include
+     * the {@code verificationCode} which is one of
+     * {@link PackageManager#INTENT_FILTER_VERIFICATION_SUCCESS} or
+     * {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}.
+     *
+     * @param verificationId pending package identifier as passed via the
+     *            {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra.
+     * @param verificationCode either {@link PackageManager#INTENT_FILTER_VERIFICATION_SUCCESS}
+     *            or {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}.
+     * @param outFailedDomains a list of failed domains if the verificationCode is
+     *            {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}, otherwise null;
+     * @throws SecurityException if the caller does not have the
+     *            INTENT_FILTER_VERIFICATION_AGENT permission.
+     *
+     * @hide
+     */
+    public abstract void verifyIntentFilter(int verificationId, int verificationCode,
+            List<String> outFailedDomains);
+
+    /**
+     * Get the status of a Domain Verification Result for an IntentFilter. This is
+     * related to the {@link android.content.IntentFilter#setAutoVerify(boolean)} and
+     * {@link android.content.IntentFilter#getAutoVerify()}
+     *
+     * This is used by the ResolverActivity to change the status depending on what the User select
+     * in the Disambiguation Dialog and also used by the Settings App for changing the default App
+     * for a domain.
+     *
+     * @param packageName The package name of the Activity associated with the IntentFilter.
+     * @param userId The user id.
+     *
+     * @return The status to set to. This can be
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK} or
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS} or
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER} or
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED}
+     *
+     * @hide
+     */
+    public abstract int getIntentVerificationStatus(String packageName, int userId);
+
+    /**
+     * Allow to change the status of a Intent Verification status for all IntentFilter of an App.
+     * This is related to the {@link android.content.IntentFilter#setAutoVerify(boolean)} and
+     * {@link android.content.IntentFilter#getAutoVerify()}
+     *
+     * This is used by the ResolverActivity to change the status depending on what the User select
+     * in the Disambiguation Dialog and also used by the Settings App for changing the default App
+     * for a domain.
+     *
+     * @param packageName The package name of the Activity associated with the IntentFilter.
+     * @param status The status to set to. This can be
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK} or
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS} or
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER}
+     * @param userId The user id.
+     *
+     * @return true if the status has been set. False otherwise.
+     *
+     * @hide
+     */
+    public abstract boolean updateIntentVerificationStatus(String packageName, int status,
+            int userId);
+
+    /**
+     * Get the list of IntentFilterVerificationInfo for a specific package and User.
+     *
+     * @param packageName the package name. When this parameter is set to a non null value,
+     *                    the results will be filtered by the package name provided.
+     *                    Otherwise, there will be no filtering and it will return a list
+     *                    corresponding for all packages for the provided userId.
+     * @return a list of IntentFilterVerificationInfo for a specific package and User.
+     */
+    public abstract List<IntentFilterVerificationInfo> getIntentFilterVerifications(
+            String packageName);
+
+    /**
      * Change the installer associated with a given package.  There are limitations
      * on how the installer package can be changed; in particular:
      * <ul>
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index c443ff3..4b81fd4 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -269,6 +269,7 @@
 
         public final boolean coreApp;
         public final boolean multiArch;
+        public final boolean extractNativeLibs;
 
         public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
                 String[] splitCodePaths, int[] splitRevisionCodes) {
@@ -284,6 +285,7 @@
             this.splitRevisionCodes = splitRevisionCodes;
             this.coreApp = baseApk.coreApp;
             this.multiArch = baseApk.multiArch;
+            this.extractNativeLibs = baseApk.extractNativeLibs;
         }
 
         public List<String> getAllCodePaths() {
@@ -310,10 +312,12 @@
         public final Signature[] signatures;
         public final boolean coreApp;
         public final boolean multiArch;
+        public final boolean extractNativeLibs;
 
         public ApkLite(String codePath, String packageName, String splitName, int versionCode,
                 int revisionCode, int installLocation, List<VerifierInfo> verifiers,
-                Signature[] signatures, boolean coreApp, boolean multiArch) {
+                Signature[] signatures, boolean coreApp, boolean multiArch,
+                boolean extractNativeLibs) {
             this.codePath = codePath;
             this.packageName = packageName;
             this.splitName = splitName;
@@ -324,6 +328,7 @@
             this.signatures = signatures;
             this.coreApp = coreApp;
             this.multiArch = multiArch;
+            this.extractNativeLibs = extractNativeLibs;
         }
     }
 
@@ -371,16 +376,6 @@
         return path.endsWith(".apk");
     }
 
-    /*
-    public static PackageInfo generatePackageInfo(PackageParser.Package p,
-            int gids[], int flags, long firstInstallTime, long lastUpdateTime,
-            HashSet<String> grantedPermissions) {
-        PackageUserState state = new PackageUserState();
-        return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
-                grantedPermissions, state, UserHandle.getCallingUserId());
-    }
-    */
-
     /**
      * Generate and return the {@link PackageInfo} for a parsed package.
      *
@@ -389,7 +384,7 @@
      */
     public static PackageInfo generatePackageInfo(PackageParser.Package p,
             int gids[], int flags, long firstInstallTime, long lastUpdateTime,
-            ArraySet<String> grantedPermissions, PackageUserState state) {
+            Set<String> grantedPermissions, PackageUserState state) {
 
         return generatePackageInfo(p, gids, flags, firstInstallTime, lastUpdateTime,
                 grantedPermissions, state, UserHandle.getCallingUserId());
@@ -410,7 +405,7 @@
 
     public static PackageInfo generatePackageInfo(PackageParser.Package p,
             int gids[], int flags, long firstInstallTime, long lastUpdateTime,
-            ArraySet<String> grantedPermissions, PackageUserState state, int userId) {
+            Set<String> grantedPermissions, PackageUserState state, int userId) {
 
         if (!checkUseInstalledOrHidden(flags, state)) {
             return null;
@@ -569,9 +564,8 @@
                 for (int i=0; i<N; i++) {
                     final String perm = p.requestedPermissions.get(i);
                     pi.requestedPermissions[i] = perm;
-                    if (p.requestedPermissionsRequired.get(i)) {
-                        pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
-                    }
+                    // The notion of required permissions is deprecated but for compatibility.
+                    pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_REQUIRED;
                     if (grantedPermissions != null && grantedPermissions.contains(perm)) {
                         pi.requestedPermissionsFlags[i] |= PackageInfo.REQUESTED_PERMISSION_GRANTED;
                     }
@@ -1270,6 +1264,7 @@
         int revisionCode = 0;
         boolean coreApp = false;
         boolean multiArch = false;
+        boolean extractNativeLibs = true;
 
         for (int i = 0; i < attrs.getAttributeCount(); i++) {
             final String attr = attrs.getAttributeName(i);
@@ -1308,14 +1303,17 @@
                     final String attr = attrs.getAttributeName(i);
                     if ("multiArch".equals(attr)) {
                         multiArch = attrs.getAttributeBooleanValue(i, false);
-                        break;
+                    }
+                    if ("extractNativeLibs".equals(attr)) {
+                        extractNativeLibs = attrs.getAttributeBooleanValue(i, true);
                     }
                 }
             }
         }
 
         return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode,
-                revisionCode, installLocation, verifiers, signatures, coreApp, multiArch);
+                revisionCode, installLocation, verifiers, signatures, coreApp, multiArch,
+                extractNativeLibs);
     }
 
     /**
@@ -1812,7 +1810,6 @@
                 }
                 implicitPerms.append(npi.name);
                 pkg.requestedPermissions.add(npi.name);
-                pkg.requestedPermissionsRequired.add(Boolean.TRUE);
             }
         }
         if (implicitPerms != null) {
@@ -1831,7 +1828,6 @@
                 final String perm = spi.newPerms[in];
                 if (!pkg.requestedPermissions.contains(perm)) {
                     pkg.requestedPermissions.add(perm);
-                    pkg.requestedPermissionsRequired.add(Boolean.TRUE);
                 }
             }
         }
@@ -1865,17 +1861,6 @@
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
         }
 
-        /*
-         * b/8528162: Ignore the <uses-permission android:required> attribute if
-         * targetSdkVersion < JELLY_BEAN_MR2. There are lots of apps in the wild
-         * which are improperly using this attribute, even though it never worked.
-         */
-        if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) {
-            for (int i = 0; i < pkg.requestedPermissionsRequired.size(); i++) {
-                pkg.requestedPermissionsRequired.set(i, Boolean.TRUE);
-            }
-        }
-
         return pkg;
     }
 
@@ -1911,11 +1896,6 @@
         // that may change.
         String name = sa.getNonResourceString(
                 com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
-/*
-        boolean required = sa.getBoolean(
-                com.android.internal.R.styleable.AndroidManifestUsesPermission_required, true);
-*/
-        boolean required = true; // Optional <uses-permission> not supported
 
         int maxSdkVersion = 0;
         TypedValue val = sa.peekValue(
@@ -1933,13 +1913,9 @@
                 int index = pkg.requestedPermissions.indexOf(name);
                 if (index == -1) {
                     pkg.requestedPermissions.add(name.intern());
-                    pkg.requestedPermissionsRequired.add(required ? Boolean.TRUE : Boolean.FALSE);
                 } else {
-                    if (pkg.requestedPermissionsRequired.get(index) != required) {
-                        outError[0] = "conflicting <uses-permission> entries";
-                        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
-                        return false;
-                    }
+                    Slog.w(TAG, "Ignoring duplicate uses-permission: " + name + " in package: "
+                            + pkg.packageName + " at: " + parser.getPositionDescription());
                 }
             }
         }
@@ -2569,6 +2545,12 @@
             ai.flags |= ApplicationInfo.FLAG_MULTIARCH;
         }
 
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_extractNativeLibs,
+                true)) {
+            ai.flags |= ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS;
+        }
+
         String str;
         str = sa.getNonConfigurationString(
                 com.android.internal.R.styleable.AndroidManifestApplication_permission, 0);
@@ -2768,15 +2750,21 @@
             }
         }
 
-        addSharedLibrariesForBackwardCompatibility(owner);
+        modifySharedLibrariesForBackwardCompatibility(owner);
 
         return true;
     }
 
-    private static void addSharedLibrariesForBackwardCompatibility(Package owner) {
-        if (owner.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1) {
-            owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, "org.apache.http.legacy");
-        }
+    private static void modifySharedLibrariesForBackwardCompatibility(Package owner) {
+        // "org.apache.http.legacy" is now a part of the boot classpath so it doesn't need
+        // to be an explicit dependency.
+        //
+        // A future change will remove this library from the boot classpath, at which point
+        // all apps that target SDK 21 and earlier will have it automatically added to their
+        // dependency lists.
+        owner.usesLibraries = ArrayUtils.remove(owner.usesLibraries, "org.apache.http.legacy");
+        owner.usesOptionalLibraries = ArrayUtils.remove(owner.usesOptionalLibraries,
+                "org.apache.http.legacy");
     }
 
     /**
@@ -3116,8 +3104,7 @@
             }
 
             a.info.resizeable = sa.getBoolean(
-                    R.styleable.AndroidManifestActivity_resizeableActivity,
-                    owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.MNC);
+                    R.styleable.AndroidManifestActivity_resizeableActivity, false);
             if (a.info.resizeable) {
                 // Fixed screen orientation isn't supported with resizeable activities.
                 a.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -3168,7 +3155,7 @@
 
             if (parser.getName().equals("intent-filter")) {
                 ActivityIntentInfo intent = new ActivityIntentInfo(a);
-                if (!parseIntent(res, parser, attrs, true, intent, outError)) {
+                if (!parseIntent(res, parser, attrs, true, true, intent, outError)) {
                     return null;
                 }
                 if (intent.countActions() == 0) {
@@ -3180,7 +3167,7 @@
                 }
             } else if (!receiver && parser.getName().equals("preferred")) {
                 ActivityIntentInfo intent = new ActivityIntentInfo(a);
-                if (!parseIntent(res, parser, attrs, false, intent, outError)) {
+                if (!parseIntent(res, parser, attrs, false, false, intent, outError)) {
                     return null;
                 }
                 if (intent.countActions() == 0) {
@@ -3360,7 +3347,7 @@
 
             if (parser.getName().equals("intent-filter")) {
                 ActivityIntentInfo intent = new ActivityIntentInfo(a);
-                if (!parseIntent(res, parser, attrs, true, intent, outError)) {
+                if (!parseIntent(res, parser, attrs, true, true, intent, outError)) {
                     return null;
                 }
                 if (intent.countActions() == 0) {
@@ -3540,7 +3527,7 @@
 
             if (parser.getName().equals("intent-filter")) {
                 ProviderIntentInfo intent = new ProviderIntentInfo(outInfo);
-                if (!parseIntent(res, parser, attrs, true, intent, outError)) {
+                if (!parseIntent(res, parser, attrs, true, false, intent, outError)) {
                     return false;
                 }
                 outInfo.intents.add(intent);
@@ -3799,7 +3786,7 @@
 
             if (parser.getName().equals("intent-filter")) {
                 ServiceIntentInfo intent = new ServiceIntentInfo(s);
-                if (!parseIntent(res, parser, attrs, true, intent, outError)) {
+                if (!parseIntent(res, parser, attrs, true, false, intent, outError)) {
                     return null;
                 }
 
@@ -4000,7 +3987,7 @@
             = "http://schemas.android.com/apk/res/android";
 
     private boolean parseIntent(Resources res, XmlPullParser parser, AttributeSet attrs,
-            boolean allowGlobs, IntentInfo outInfo, String[] outError)
+            boolean allowGlobs, boolean allowAutoVerify, IntentInfo outInfo, String[] outError)
             throws XmlPullParserException, IOException {
 
         TypedArray sa = res.obtainAttributes(attrs,
@@ -4025,6 +4012,12 @@
         outInfo.banner = sa.getResourceId(
                 com.android.internal.R.styleable.AndroidManifestIntentFilter_banner, 0);
 
+        if (allowAutoVerify) {
+            outInfo.setAutoVerify(sa.getBoolean(
+                    com.android.internal.R.styleable.AndroidManifestIntentFilter_autoVerify,
+                    false));
+        }
+
         sa.recycle();
 
         int outerDepth = parser.getDepth();
@@ -4218,7 +4211,6 @@
         public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);
 
         public final ArrayList<String> requestedPermissions = new ArrayList<String>();
-        public final ArrayList<Boolean> requestedPermissionsRequired = new ArrayList<Boolean>();
 
         public ArrayList<String> protectedBroadcasts;
 
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index a9c7be3..92b8055 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -37,10 +37,14 @@
     public ArraySet<String> disabledComponents;
     public ArraySet<String> enabledComponents;
 
+    public int domainVerificationStatus;
+
     public PackageUserState() {
         installed = true;
         hidden = false;
         enabled = COMPONENT_ENABLED_STATE_DEFAULT;
+        domainVerificationStatus =
+                PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
     }
 
     public PackageUserState(PackageUserState o) {
@@ -51,9 +55,10 @@
         hidden = o.hidden;
         lastDisableAppCaller = o.lastDisableAppCaller;
         disabledComponents = o.disabledComponents != null
-                ? new ArraySet<String>(o.disabledComponents) : null;
+                ? new ArraySet<>(o.disabledComponents) : null;
         enabledComponents = o.enabledComponents != null
-                ? new ArraySet<String>(o.enabledComponents) : null;
+                ? new ArraySet<>(o.enabledComponents) : null;
         blockUninstall = o.blockUninstall;
+        domainVerificationStatus = o.domainVerificationStatus;
     }
 }
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index fe3aec9..7b141f0 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -143,6 +143,11 @@
      */
     public boolean system;
 
+    /**
+     * @hide Does the associated IntentFilter needs verification ?
+     */
+    public boolean filterNeedsVerification;
+
     private ComponentInfo getComponentInfo() {
         if (activityInfo != null) return activityInfo;
         if (serviceInfo != null) return serviceInfo;
@@ -283,6 +288,7 @@
         resolvePackageName = orig.resolvePackageName;
         system = orig.system;
         targetUserId = orig.targetUserId;
+        filterNeedsVerification = orig.filterNeedsVerification;
     }
 
     public String toString() {
@@ -344,6 +350,7 @@
         dest.writeInt(targetUserId);
         dest.writeInt(system ? 1 : 0);
         dest.writeInt(noResourceId ? 1 : 0);
+        dest.writeInt(filterNeedsVerification ? 1 : 0);
     }
 
     public static final Creator<ResolveInfo> CREATOR
@@ -389,6 +396,7 @@
         targetUserId = source.readInt();
         system = source.readInt() != 0;
         noResourceId = source.readInt() != 0;
+        filterNeedsVerification = source.readInt() != 0;
     }
     
     public static class DisplayNameComparator
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index 841b09d..fdafb04 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -71,10 +71,15 @@
  */
 public class ColorStateList implements Parcelable {
     private static final String TAG = "ColorStateList";
+
     private static final int DEFAULT_COLOR = Color.RED;
     private static final int[][] EMPTY = new int[][] { new int[0] };
-    private static final SparseArray<WeakReference<ColorStateList>> sCache =
-                            new SparseArray<WeakReference<ColorStateList>>();
+
+    /** Thread-safe cache of single-color ColorStateLists. */
+    private static final SparseArray<WeakReference<ColorStateList>> sCache = new SparseArray<>();
+
+    /** Lazily-created factory for this color state list. */
+    private ColorStateListFactory mFactory;
 
     private int[][] mThemeAttrs;
     private int mChangingConfigurations;
@@ -125,7 +130,7 @@
             }
 
             final ColorStateList csl = new ColorStateList(EMPTY, new int[] { color });
-            sCache.put(color, new WeakReference<ColorStateList>(csl));
+            sCache.put(color, new WeakReference<>(csl));
             return csl;
         }
     }
@@ -141,11 +146,13 @@
      */
     private ColorStateList(ColorStateList orig) {
         if (orig != null) {
+            mChangingConfigurations = orig.mChangingConfigurations;
             mStateSpecs = orig.mStateSpecs;
             mDefaultColor = orig.mDefaultColor;
             mIsOpaque = orig.mIsOpaque;
 
-            // Deep copy, this may change due to theming.
+            // Deep copy, these may change due to applyTheme().
+            mThemeAttrs = orig.mThemeAttrs.clone();
             mColors = orig.mColors.clone();
         }
     }
@@ -329,6 +336,7 @@
      * attributes.
      *
      * @return whether a theme can be applied to this color state list
+     * @hide only for resource preloading
      */
     public boolean canApplyTheme() {
         return mThemeAttrs != null;
@@ -336,10 +344,15 @@
 
     /**
      * Applies a theme to this color state list.
+     * <p>
+     * <strong>Note:</strong> Applying a theme may affect the changing
+     * configuration parameters of this color state list. After calling this
+     * method, any dependent configurations must be updated by obtaining the
+     * new configuration mask from {@link #getChangingConfigurations()}.
      *
      * @param t the theme to apply
      */
-    public void applyTheme(Theme t) {
+    private void applyTheme(Theme t) {
         if (mThemeAttrs == null) {
             return;
         }
@@ -376,6 +389,38 @@
         onColorsChanged();
     }
 
+    /**
+     * Returns an appropriately themed color state list.
+     *
+     * @param t the theme to apply
+     * @return a copy of the color state list with the theme applied, or the
+     *         color state list itself if there were no unresolved theme
+     *         attributes
+     * @hide only for resource preloading
+     */
+    public ColorStateList obtainForTheme(Theme t) {
+        if (t == null || !canApplyTheme()) {
+            return this;
+        }
+
+        final ColorStateList clone = new ColorStateList(this);
+        clone.applyTheme(t);
+        return clone;
+    }
+
+    /**
+     * Returns a mask of the configuration parameters for which this color
+     * state list may change, requiring that it be re-created.
+     *
+     * @return a mask of the changing configuration parameters, as defined by
+     *         {@link android.content.pm.ActivityInfo}
+     *
+     * @see android.content.pm.ActivityInfo
+     */
+    public int getChangingConfigurations() {
+        return mChangingConfigurations;
+    }
+
     private int modulateColorAlpha(int baseColor, float alphaMod) {
         if (alphaMod == 1.0f) {
             return baseColor;
@@ -383,8 +428,7 @@
 
         final int baseAlpha = Color.alpha(baseColor);
         final int alpha = MathUtils.constrain((int) (baseAlpha * alphaMod + 0.5f), 0, 255);
-        final int color = (baseColor & 0xFFFFFF) | (alpha << 24);
-        return color;
+        return (baseColor & 0xFFFFFF) | (alpha << 24);
     }
 
     /**
@@ -464,6 +508,33 @@
         return mColors;
     }
 
+    /**
+     * Returns whether the specified state is referenced in any of the state
+     * specs contained within this ColorStateList.
+     * <p>
+     * Any reference, either positive or negative {ex. ~R.attr.state_enabled},
+     * will cause this method to return {@code true}. Wildcards are not counted
+     * as references.
+     *
+     * @param state the state to search for
+     * @return {@code true} if the state if referenced, {@code false} otherwise
+     * @hide Use only as directed. For internal use only.
+     */
+    public boolean hasState(int state) {
+        final int[][] stateSpecs = mStateSpecs;
+        final int specCount = stateSpecs.length;
+        for (int specIndex = 0; specIndex < specCount; specIndex++) {
+            final int[] states = stateSpecs[specIndex];
+            final int stateCount = states.length;
+            for (int stateIndex = 0; stateIndex < stateCount; stateIndex++) {
+                if (states[stateIndex] == state || states[stateIndex] == ~state) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     @Override
     public String toString() {
         return "ColorStateList{" +
@@ -507,14 +578,18 @@
     }
 
     /**
-     * @return A factory that can create new instances of this ColorStateList.
+     * @return a factory that can create new instances of this ColorStateList
+     * @hide only for resource preloading
      */
-    ColorStateListFactory getFactory() {
-        return new ColorStateListFactory(this);
+    public ConstantState<ColorStateList> getConstantState() {
+        if (mFactory != null) {
+            mFactory = new ColorStateListFactory(this);
+        }
+        return mFactory;
     }
 
-    static class ColorStateListFactory extends ConstantState<ColorStateList> {
-        final ColorStateList mSrc;
+    private static class ColorStateListFactory extends ConstantState<ColorStateList> {
+        private final ColorStateList mSrc;
 
         public ColorStateListFactory(ColorStateList src) {
             mSrc = src;
@@ -532,13 +607,7 @@
 
         @Override
         public ColorStateList newInstance(Resources res, Theme theme) {
-            if (theme == null || !mSrc.canApplyTheme()) {
-                return mSrc;
-            }
-
-            final ColorStateList clone = new ColorStateList(mSrc);
-            clone.applyTheme(theme);
-            return clone;
+            return mSrc.obtainForTheme(theme);
         }
     }
 
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 14af584..b5eeb30 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1314,7 +1314,8 @@
      * {@link View#LAYOUT_DIRECTION_LTR}. If not null will set it to the layout direction
      * corresponding to the Locale.
      *
-     * @see {@link View#LAYOUT_DIRECTION_LTR} and {@link View#LAYOUT_DIRECTION_RTL}
+     * @see View#LAYOUT_DIRECTION_LTR
+     * @see View#LAYOUT_DIRECTION_RTL
      */
     public void setLayoutDirection(Locale locale) {
         // There is a "1" difference between the configuration values for
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 95ad57e..299eb7e 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -16,7 +16,10 @@
 
 package android.content.res;
 
+import android.annotation.AttrRes;
 import android.annotation.ColorInt;
+import android.annotation.StyleRes;
+import android.annotation.StyleableRes;
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -41,7 +44,6 @@
 import android.annotation.StringRes;
 import android.annotation.XmlRes;
 import android.content.pm.ActivityInfo;
-import android.content.res.ColorStateList.ColorStateListFactory;
 import android.graphics.Movie;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
@@ -111,12 +113,12 @@
     // single-threaded, and after that these are immutable.
     private static final LongSparseArray<ConstantState>[] sPreloadedDrawables;
     private static final LongSparseArray<ConstantState> sPreloadedColorDrawables
-            = new LongSparseArray<ConstantState>();
-    private static final LongSparseArray<ColorStateListFactory> sPreloadedColorStateLists
-            = new LongSparseArray<ColorStateListFactory>();
+            = new LongSparseArray<>();
+    private static final LongSparseArray<android.content.res.ConstantState<ColorStateList>>
+            sPreloadedColorStateLists = new LongSparseArray<>();
 
     // Pool of TypedArrays targeted to this Resources object.
-    final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<TypedArray>(5);
+    final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5);
 
     // Used by BridgeResources in layoutlib
     static Resources mSystem = null;
@@ -128,21 +130,19 @@
     private final Object mAccessLock = new Object();
     private final Configuration mTmpConfig = new Configuration();
     private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mDrawableCache =
-            new ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>>();
+            new ArrayMap<>();
     private final ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>> mColorDrawableCache =
-            new ArrayMap<String, LongSparseArray<WeakReference<ConstantState>>>();
+            new ArrayMap<>();
     private final ConfigurationBoundResourceCache<ColorStateList> mColorStateListCache =
-            new ConfigurationBoundResourceCache<ColorStateList>(this);
+            new ConfigurationBoundResourceCache<>(this);
     private final ConfigurationBoundResourceCache<Animator> mAnimatorCache =
-            new ConfigurationBoundResourceCache<Animator>(this);
+            new ConfigurationBoundResourceCache<>(this);
     private final ConfigurationBoundResourceCache<StateListAnimator> mStateListAnimatorCache =
-            new ConfigurationBoundResourceCache<StateListAnimator>(this);
+            new ConfigurationBoundResourceCache<>(this);
 
     private TypedValue mTmpValue = new TypedValue();
     private boolean mPreloading;
 
-    private TypedArray mCachedStyledAttributes = null;
-
     private int mLastCachedXmlBlockIndex = -1;
     private final int[] mCachedXmlBlockIds = { 0, 0, 0, 0 };
     private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[4];
@@ -157,8 +157,8 @@
 
     static {
         sPreloadedDrawables = new LongSparseArray[2];
-        sPreloadedDrawables[0] = new LongSparseArray<ConstantState>();
-        sPreloadedDrawables[1] = new LongSparseArray<ConstantState>();
+        sPreloadedDrawables[0] = new LongSparseArray<>();
+        sPreloadedDrawables[1] = new LongSparseArray<>();
     }
 
     /**
@@ -1475,7 +1475,7 @@
          * @see #obtainStyledAttributes(int, int[])
          * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
          */
-        public TypedArray obtainStyledAttributes(int[] attrs) {
+        public TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) {
             final int len = attrs.length;
             final TypedArray array = TypedArray.obtain(Resources.this, len);
             array.mTheme = this;
@@ -1503,7 +1503,8 @@
          * @see #obtainStyledAttributes(int[])
          * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
          */
-        public TypedArray obtainStyledAttributes(int resid, int[] attrs) throws NotFoundException {
+        public TypedArray obtainStyledAttributes(@StyleRes int resid, @StyleableRes int[] attrs)
+                throws NotFoundException {
             final int len = attrs.length;
             final TypedArray array = TypedArray.obtain(Resources.this, len);
             array.mTheme = this;
@@ -1586,7 +1587,7 @@
          * @see #obtainStyledAttributes(int, int[])
          */
         public TypedArray obtainStyledAttributes(AttributeSet set,
-                int[] attrs, int defStyleAttr, int defStyleRes) {
+                @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
             final int len = attrs.length;
             final TypedArray array = TypedArray.obtain(Resources.this, len);
 
@@ -1876,7 +1877,7 @@
             // the framework.
             mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
 
-            int configChanges = calcConfigChanges(config);
+            final int configChanges = calcConfigChanges(config);
             if (mConfiguration.locale == null) {
                 mConfiguration.locale = Locale.getDefault();
                 mConfiguration.setLayoutDirection(mConfiguration.locale);
@@ -1891,7 +1892,8 @@
             if (mConfiguration.locale != null) {
                 locale = adjustLanguageTag(mConfiguration.locale.toLanguageTag());
             }
-            int width, height;
+
+            final int width, height;
             if (mMetrics.widthPixels >= mMetrics.heightPixels) {
                 width = mMetrics.widthPixels;
                 height = mMetrics.heightPixels;
@@ -1901,12 +1903,15 @@
                 //noinspection SuspiciousNameCombination
                 height = mMetrics.widthPixels;
             }
-            int keyboardHidden = mConfiguration.keyboardHidden;
-            if (keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
-                    && mConfiguration.hardKeyboardHidden
-                            == Configuration.HARDKEYBOARDHIDDEN_YES) {
+
+            final int keyboardHidden;
+            if (mConfiguration.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
+                    && mConfiguration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
                 keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
+            } else {
+                keyboardHidden = mConfiguration.keyboardHidden;
             }
+
             mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
                     locale, mConfiguration.orientation,
                     mConfiguration.touchscreen,
@@ -2508,10 +2513,10 @@
                     // Clean out the caches before we add more. This shouldn't
                     // happen very often.
                     pruneCaches(caches);
-                    themedCache = new LongSparseArray<WeakReference<ConstantState>>(1);
+                    themedCache = new LongSparseArray<>(1);
                     caches.put(themeKey, themedCache);
                 }
-                themedCache.put(key, new WeakReference<ConstantState>(cs));
+                themedCache.put(key, new WeakReference<>(cs));
             }
         }
     }
@@ -2665,7 +2670,8 @@
         // Handle inline color definitions.
         if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
                 && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
-            final ColorStateListFactory factory = sPreloadedColorStateLists.get(key);
+            final android.content.res.ConstantState<ColorStateList> factory =
+                    sPreloadedColorStateLists.get(key);
             if (factory != null) {
                 return factory.newInstance();
             }
@@ -2675,7 +2681,7 @@
             if (mPreloading) {
                 if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
                         "color")) {
-                    sPreloadedColorStateLists.put(key, csl.getFactory());
+                    sPreloadedColorStateLists.put(key, csl.getConstantState());
                 }
             }
 
@@ -2689,7 +2695,8 @@
             return csl;
         }
 
-        final ColorStateListFactory factory = sPreloadedColorStateLists.get(key);
+        final android.content.res.ConstantState<ColorStateList> factory =
+                sPreloadedColorStateLists.get(key);
         if (factory != null) {
             csl = factory.newInstance(this, theme);
         }
@@ -2702,10 +2709,10 @@
             if (mPreloading) {
                 if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
                         "color")) {
-                    sPreloadedColorStateLists.put(key, csl.getFactory());
+                    sPreloadedColorStateLists.put(key, csl.getConstantState());
                 }
             } else {
-                cache.put(key, theme, csl.getFactory());
+                cache.put(key, theme, csl.getConstantState());
             }
         }
 
@@ -2830,15 +2837,6 @@
                 + Integer.toHexString(id));
     }
 
-    /*package*/ void recycleCachedStyledAttributes(TypedArray attrs) {
-        synchronized (mAccessLock) {
-            final TypedArray cached = mCachedStyledAttributes;
-            if (cached == null || cached.mData.length < attrs.mData.length) {
-                mCachedStyledAttributes = attrs;
-            }
-        }
-    }
-
     /**
      * Obtains styled attributes from the theme, if available, or unstyled
      * resources if the theme is null.
diff --git a/core/java/android/database/DatabaseErrorHandler.java b/core/java/android/database/DatabaseErrorHandler.java
index f0c5452..55ad921 100644
--- a/core/java/android/database/DatabaseErrorHandler.java
+++ b/core/java/android/database/DatabaseErrorHandler.java
@@ -19,13 +19,12 @@
 import android.database.sqlite.SQLiteDatabase;
 
 /**
- * An interface to let the apps define the actions to take when the following errors are detected
- *   database corruption
+ * An interface to let apps define an action to take when database corruption is detected.
  */
 public interface DatabaseErrorHandler {
 
     /**
-     * defines the method to be invoked when database corruption is detected.
+     * The method invoked when database corruption is detected.
      * @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption
      * is detected.
      */
diff --git a/core/java/android/database/DefaultDatabaseErrorHandler.java b/core/java/android/database/DefaultDatabaseErrorHandler.java
index b234e34..7fa2b40 100755
--- a/core/java/android/database/DefaultDatabaseErrorHandler.java
+++ b/core/java/android/database/DefaultDatabaseErrorHandler.java
@@ -24,7 +24,7 @@
 import android.util.Pair;
 
 /**
- * Default class used to define the actions to take when the database corruption is reported
+ * Default class used to define the action to take when database corruption is reported
  * by sqlite.
  * <p>
  * An application can specify an implementation of {@link DatabaseErrorHandler} on the
@@ -38,7 +38,7 @@
  * The specified {@link DatabaseErrorHandler} is used to handle database corruption errors, if they
  * occur.
  * <p>
- * If null is specified for DatabaeErrorHandler param in the above calls, then this class is used
+ * If null is specified for the DatabaseErrorHandler param in the above calls, this class is used
  * as the default {@link DatabaseErrorHandler}.
  */
 public final class DefaultDatabaseErrorHandler implements DatabaseErrorHandler {
diff --git a/core/java/android/hardware/ICameraService.aidl b/core/java/android/hardware/ICameraService.aidl
index d5dfaf6..9bc2f46 100644
--- a/core/java/android/hardware/ICameraService.aidl
+++ b/core/java/android/hardware/ICameraService.aidl
@@ -75,4 +75,11 @@
                     out BinderHolder device);
 
     int setTorchMode(String CameraId, boolean enabled, IBinder clientBinder);
+
+    /**
+     * Notify the camera service of a system event.  Should only be called from system_server.
+     *
+     * Callers require the android.permission.CAMERA_SEND_SYSTEM_EVENTS permission.
+     */
+    oneway void notifySystemEvent(int eventId, int arg0);
 }
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index a6c3ea4..88fa339 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -54,11 +54,13 @@
     // Looper associated with the context in which this instance was created.
     private final Looper mMainLooper;
     private final int mTargetSdkLevel;
+    private final String mPackageName;
 
     /** {@hide} */
     public SystemSensorManager(Context context, Looper mainLooper) {
         mMainLooper = mainLooper;
         mTargetSdkLevel = context.getApplicationInfo().targetSdkVersion;
+        mPackageName = context.getPackageName();
         synchronized(sSensorModuleLock) {
             if (!sSensorModuleInitialized) {
                 sSensorModuleInitialized = true;
@@ -117,14 +119,14 @@
             if (queue == null) {
                 Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
                 queue = new SensorEventQueue(listener, looper, this);
-                if (!queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs, reservedFlags)) {
+                if (!queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs)) {
                     queue.dispose();
                     return false;
                 }
                 mSensorListeners.put(listener, queue);
                 return true;
             } else {
-                return queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs, reservedFlags);
+                return queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs);
             }
         }
     }
@@ -165,14 +167,14 @@
             TriggerEventQueue queue = mTriggerListeners.get(listener);
             if (queue == null) {
                 queue = new TriggerEventQueue(listener, mMainLooper, this);
-                if (!queue.addSensor(sensor, 0, 0, 0)) {
+                if (!queue.addSensor(sensor, 0, 0)) {
                     queue.dispose();
                     return false;
                 }
                 mTriggerListeners.put(listener, queue);
                 return true;
             } else {
-                return queue.addSensor(sensor, 0, 0, 0);
+                return queue.addSensor(sensor, 0, 0);
             }
         }
     }
@@ -223,9 +225,9 @@
      */
     private static abstract class BaseEventQueue {
         private native long nativeInitBaseEventQueue(BaseEventQueue eventQ, MessageQueue msgQ,
-                float[] scratch);
+                float[] scratch, String packageName);
         private static native int nativeEnableSensor(long eventQ, int handle, int rateUs,
-                int maxBatchReportLatencyUs, int reservedFlags);
+                int maxBatchReportLatencyUs);
         private static native int nativeDisableSensor(long eventQ, int handle);
         private static native void nativeDestroySensorEventQueue(long eventQ);
         private static native int nativeFlushSensor(long eventQ);
@@ -238,7 +240,8 @@
         protected final SystemSensorManager mManager;
 
         BaseEventQueue(Looper looper, SystemSensorManager manager) {
-            nSensorEventQueue = nativeInitBaseEventQueue(this, looper.getQueue(), mScratch);
+            nSensorEventQueue = nativeInitBaseEventQueue(this, looper.getQueue(), mScratch,
+                    manager.mPackageName);
             mCloseGuard.open("dispose");
             mManager = manager;
         }
@@ -248,7 +251,7 @@
         }
 
         public boolean addSensor(
-                Sensor sensor, int delayUs, int maxBatchReportLatencyUs, int reservedFlags) {
+                Sensor sensor, int delayUs, int maxBatchReportLatencyUs) {
             // Check if already present.
             int handle = sensor.getHandle();
             if (mActiveSensors.get(handle)) return false;
@@ -256,10 +259,10 @@
             // Get ready to receive events before calling enable.
             mActiveSensors.put(handle, true);
             addSensorEvent(sensor);
-            if (enableSensor(sensor, delayUs, maxBatchReportLatencyUs, reservedFlags) != 0) {
+            if (enableSensor(sensor, delayUs, maxBatchReportLatencyUs) != 0) {
                 // Try continuous mode if batching fails.
                 if (maxBatchReportLatencyUs == 0 ||
-                    maxBatchReportLatencyUs > 0 && enableSensor(sensor, delayUs, 0, 0) != 0) {
+                    maxBatchReportLatencyUs > 0 && enableSensor(sensor, delayUs, 0) != 0) {
                   removeSensor(sensor, false);
                   return false;
                 }
@@ -328,11 +331,11 @@
         }
 
         private int enableSensor(
-                Sensor sensor, int rateUs, int maxBatchReportLatencyUs, int reservedFlags) {
+                Sensor sensor, int rateUs, int maxBatchReportLatencyUs) {
             if (nSensorEventQueue == 0) throw new NullPointerException();
             if (sensor == null) throw new NullPointerException();
             return nativeEnableSensor(nSensorEventQueue, sensor.getHandle(), rateUs,
-                    maxBatchReportLatencyUs, reservedFlags);
+                    maxBatchReportLatencyUs);
         }
 
         private int disableSensor(Sensor sensor) {
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index a0217c2..1503bf5 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -991,7 +991,8 @@
      * <ul>
      * <li>Processed (but stalling): any non-RAW format with a stallDurations &gt; 0.
      * Typically JPEG format (ImageFormat#JPEG).</li>
-     * <li>Raw formats: ImageFormat#RAW_SENSOR, ImageFormat#RAW10 and ImageFormat#RAW_OPAQUE.</li>
+     * <li>Raw formats: ImageFormat#RAW_SENSOR, ImageFormat#RAW10, ImageFormat#RAW12,
+     * and ImageFormat#RAW_OPAQUE.</li>
      * <li>Processed (but not-stalling): any non-RAW format without a stall duration.
      * Typically ImageFormat#YUV_420_888, ImageFormat#NV21, ImageFormat#YV12.</li>
      * </ul>
@@ -1023,6 +1024,7 @@
      * <ul>
      * <li>ImageFormat#RAW_SENSOR</li>
      * <li>ImageFormat#RAW10</li>
+     * <li>ImageFormat#RAW12</li>
      * <li>Opaque <code>RAW</code></li>
      * </ul>
      * <p>LEGACY mode devices ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} <code>==</code> LEGACY)
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 7569ea5..b8fb8e7 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1541,7 +1541,8 @@
      * to the camera, that the JPEG picture needs to be rotated by, to be viewed
      * upright.</p>
      * <p>Camera devices may either encode this value into the JPEG EXIF header, or
-     * rotate the image data to match this orientation.</p>
+     * rotate the image data to match this orientation. When the image data is rotated,
+     * the thumbnail data will also be rotated.</p>
      * <p>Note that this orientation is relative to the orientation of the camera sensor, given
      * by {@link CameraCharacteristics#SENSOR_ORIENTATION android.sensor.orientation}.</p>
      * <p>To translate from the device orientation given by the Android sensor APIs, the following
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index b84dc2e..e346dc2 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2230,7 +2230,8 @@
      * to the camera, that the JPEG picture needs to be rotated by, to be viewed
      * upright.</p>
      * <p>Camera devices may either encode this value into the JPEG EXIF header, or
-     * rotate the image data to match this orientation.</p>
+     * rotate the image data to match this orientation. When the image data is rotated,
+     * the thumbnail data will also be rotated.</p>
      * <p>Note that this orientation is relative to the orientation of the camera sensor, given
      * by {@link CameraCharacteristics#SENSOR_ORIENTATION android.sensor.orientation}.</p>
      * <p>To translate from the device orientation given by the Android sensor APIs, the following
diff --git a/core/java/android/hardware/fingerprint/Fingerprint.aidl b/core/java/android/hardware/fingerprint/Fingerprint.aidl
new file mode 100644
index 0000000..4743354
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/Fingerprint.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.hardware.fingerprint;
+
+// @hide
+parcelable Fingerprint;
diff --git a/core/java/android/hardware/fingerprint/Fingerprint.java b/core/java/android/hardware/fingerprint/Fingerprint.java
new file mode 100644
index 0000000..c307634
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/Fingerprint.java
@@ -0,0 +1,93 @@
+/*
+ * 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 android.hardware.fingerprint;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Container for fingerprint metadata.
+ * @hide
+ */
+public final class Fingerprint implements Parcelable {
+    private CharSequence mName;
+    private int mGroupId;
+    private int mFingerId;
+    private long mDeviceId; // physical device this is associated with
+
+    public Fingerprint(CharSequence name, int groupId, int fingerId, long deviceId) {
+        mName = name;
+        mGroupId = groupId;
+        mFingerId = fingerId;
+        mDeviceId = deviceId;
+    }
+
+    private Fingerprint(Parcel in) {
+        mName = in.readString();
+        mGroupId = in.readInt();
+        mFingerId = in.readInt();
+        mDeviceId = in.readLong();
+    }
+
+    /**
+     * Gets the human-readable name for the given fingerprint.
+     * @return name given to finger
+     */
+    public CharSequence getName() { return mName; }
+
+    /**
+     * Gets the device-specific finger id.  Used by Settings to map a name to a specific
+     * fingerprint template.
+     * @return device-specific id for this finger
+     * @hide
+     */
+    public int getFingerId() { return mFingerId; }
+
+    /**
+     * Gets the group id specified when the fingerprint was enrolled.
+     * @return group id for the set of fingerprints this one belongs to.
+     * @hide
+     */
+    public int getGroupId() { return mGroupId; }
+
+    /**
+     * Device this fingerprint belongs to.
+     * @hide
+     */
+    public long getDeviceId() { return mDeviceId; }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mName.toString());
+        out.writeInt(mGroupId);
+        out.writeInt(mFingerId);
+        out.writeLong(mDeviceId);
+    }
+
+    public static final Parcelable.Creator<Fingerprint> CREATOR
+            = new Parcelable.Creator<Fingerprint>() {
+        public Fingerprint createFromParcel(Parcel in) {
+            return new Fingerprint(in);
+        }
+
+        public Fingerprint[] newArray(int size) {
+            return new Fingerprint[size];
+        }
+    };
+};
\ No newline at end of file
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
new file mode 100644
index 0000000..e3572a2
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -0,0 +1,563 @@
+/**
+ * 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 android.hardware.fingerprint;
+
+import android.app.ActivityManagerNative;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.os.Binder;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback;
+import android.util.Log;
+import android.util.Slog;
+
+import java.security.Signature;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import javax.crypto.Cipher;
+
+/**
+ * A class that coordinates access to the fingerprint hardware.
+ * @hide
+ */
+
+public class FingerprintManager {
+    private static final String TAG = "FingerprintManager";
+    private static final boolean DEBUG = true;
+    private static final int MSG_ENROLL_RESULT = 100;
+    private static final int MSG_ACQUIRED = 101;
+    private static final int MSG_PROCESSED = 102;
+    private static final int MSG_ERROR = 103;
+    private static final int MSG_REMOVED = 104;
+
+    // Message types.  Must agree with HAL (fingerprint.h)
+    public static final int FINGERPRINT_ERROR = -1;
+    public static final int FINGERPRINT_ACQUIRED = 1;
+    public static final int FINGERPRINT_PROCESSED = 2;
+    public static final int FINGERPRINT_TEMPLATE_ENROLLING = 3;
+    public static final int FINGERPRINT_TEMPLATE_REMOVED = 4;
+
+    // Error messages. Must agree with HAL (fingerprint.h)
+    public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
+    public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
+    public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
+    public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
+    public static final int FINGERPRINT_ERROR_CANCELED = 5;
+    public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
+
+    // Image acquisition messages.  Must agree with HAL (fingerprint.h)
+    public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
+    public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
+    public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
+    public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3;
+    public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4;
+    public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5;
+    public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
+
+    private IFingerprintService mService;
+    private Context mContext;
+    private IBinder mToken = new Binder();
+    private AuthenticationCallback mAuthenticationCallback;
+    private EnrollmentCallback mEnrollmentCallback;
+    private RemovalCallback mRemovalCallback;
+    private CryptoObject mCryptoObject;
+    private Fingerprint mRemovalFingerprint;
+    private boolean mListening;
+
+    /**
+     * A wrapper class for a limited number of crypto objects supported by FingerprintManager.
+     */
+    public static class CryptoObject {
+        CryptoObject(Signature signature) { mSignature = signature; }
+        CryptoObject(Cipher cipher) { mCipher = cipher; }
+        private Signature mSignature;
+        private Cipher mCipher;
+    };
+
+    /**
+     * Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
+     *     AuthenticationCallback, CancellationSignal, int)}
+     */
+    public static final class AuthenticationResult {
+        private Fingerprint mFingerprint;
+        private CryptoObject mCryptoObject;
+
+        public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint) {
+            mCryptoObject = crypto;
+            mFingerprint = fingerprint;
+        }
+
+        /**
+         * Obtain the crypto object associated with this transaction
+         * @return crypto object provided to {@link FingerprintManager#authenticate(CryptoObject,
+         *     AuthenticationCallback, CancellationSignal, int)}
+         */
+        public CryptoObject getCryptoObject() { return mCryptoObject; }
+
+        /**
+         * Obtain the Fingerprint associated with this operation.  Applications are discouraged
+         * from associating specific fingers with specific applications or operations.  Hence this
+         * is not public.
+         * @hide
+         */
+        public Fingerprint getFingerprint() { return mFingerprint; }
+    };
+
+    /**
+     * Callback structure provided to {@link FingerprintManager#authenticate(CryptoObject,
+     * AuthenticationCallback, CancellationSignal, int)}. Users of {@link #FingerprintManager()}
+     * must provide an implementation of this to {@link FingerprintManager#authenticate(
+     * CryptoObject, AuthenticationCallback, CancellationSignal, int) for listening to fingerprint
+     * events.
+     */
+    public static abstract class AuthenticationCallback {
+        /**
+         * Called when an unrecoverable error has been encountered and the operation is complete.
+         * No further callbacks will be made on this object.
+         * @param errMsgId an integer identifying the error message.
+         * @param errString a human-readible error string that can be shown in UI.
+         */
+        public abstract void onAuthenticationError(int errMsgId, CharSequence errString);
+
+        /**
+         * Called when a recoverable error has been encountered during authentication.  The help
+         * string is provided to give the user guidance for what went wrong, such as
+         * "Sensor dirty, please clean it."
+         * @param helpMsgId an integer identifying the error message.
+         * @param helpString a human-readible string that can be shown in UI.
+         */
+        public abstract void onAuthenticationHelp(int helpMsgId, CharSequence helpString);
+
+        /**
+         * Called when a fingerprint is recognized.
+         * @param result an object containing authentication-related data.
+         */
+        public abstract void onAuthenticationSucceeded(AuthenticationResult result);
+    };
+
+    /**
+     * Callback structure provided to {@link FingerprintManager#enroll(long, EnrollmentCallback,
+     * CancellationSignal, int). Users of {@link #FingerprintManager()}
+     * must provide an implementation of this to {@link FingerprintManager#enroll(long,
+     * EnrollmentCallback, CancellationSignal, int) for listening to fingerprint events.
+     */
+    public static abstract class EnrollmentCallback {
+        /**
+         * Called when an unrecoverable error has been encountered and the operation is complete.
+         * No further callbacks will be made on this object.
+         * @param errMsgId an integer identifying the error message.
+         * @param errString a human-readible error string that can be shown in UI.
+         */
+        public abstract void onEnrollmentError(int errMsgId, CharSequence errString);
+
+        /**
+         * Called when a recoverable error has been encountered during enrollment.  The help
+         * string is provided to give the user guidance for what went wrong, such as
+         * "Sensor dirty, please clean it" or what they need to do next, such as
+         * "Touch sensor again."
+         * @param helpMsgId an integer identifying the error message.
+         * @param helpString a human-readible string that can be shown in UI.
+         */
+        public abstract void onEnrollmentHelp(int helpMsgId, CharSequence helpString);
+
+        /**
+         * Called as each enrollment step progresses. Enrollment is considered complete when
+         * remaining reaches 0.  This function will not be called if enrollment fails. See
+         * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)}
+         * @param remaining the number of remaining steps.
+         */
+        public abstract void onEnrollmentProgress(int remaining);
+    };
+
+    /**
+     * Callback structure provided to {@link FingerprintManager#remove(int). Users of
+     * {@link #FingerprintManager()} may optionally provide an implementation of this to
+     * {@link FingerprintManager#remove(int, int, RemovalCallback)} for listening to
+     * fingerprint template removal events.
+     */
+    public static abstract class RemovalCallback {
+        /**
+         * Called when the given fingerprint can't be removed.
+         * @param fp the fingerprint that the call attempted to remove.
+         * @param errMsgId an associated error message id.
+         * @param errString an error message indicating why the fingerprint id can't be removed.
+         */
+        public abstract void onRemovalError(Fingerprint fp, int errMsgId, CharSequence errString);
+
+        /**
+         * Called when a given fingerprint is successfully removed.
+         * @param fingerprint the fingerprint template that was removed.
+         */
+        public abstract void onRemovalSucceeded(Fingerprint fingerprint);
+    };
+
+    /**
+     * Request authentication of a crypto object.  This call warms up the fingerprint hardware
+     * and starts scanning for a fingerprint.  It terminates when
+     * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
+     * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult) is called, at
+     * which point the object is no longer valid. The operation can be canceled by using the
+     * provided cancel object.
+     *
+     * @param crypto object associated with the call or null if none required.
+     * @param callback an object to receive authentication events
+     * @param cancel an object that can be used to cancel authentication
+     * @param flags optional flags
+     */
+    public void authenticate(CryptoObject crypto, AuthenticationCallback callback,
+            CancellationSignal cancel, int flags) {
+        if (callback == null) {
+            throw new IllegalArgumentException("Must supply an authentication callback");
+        }
+
+        // TODO: handle cancel
+
+        if (mService != null) try {
+            mAuthenticationCallback = callback;
+            mCryptoObject = crypto;
+            long sessionId = 0; // TODO: get from crypto object
+            startListening();
+            mService.authenticate(mToken, sessionId, getCurrentUserId(), flags);
+        } catch (RemoteException e) {
+            Log.v(TAG, "Remote exception while authenticating: ", e);
+            stopListening();
+        }
+    }
+
+    /**
+     * Request fingerprint enrollment. This call warms up the fingerprint hardware
+     * and starts scanning for fingerprints. Progress will be indicated by callbacks to the
+     * {@link EnrollmentCallback} object. It terminates when
+     * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or
+     * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at
+     * which point the object is no longer valid. The operation can be canceled by using the
+     * provided cancel object.
+     * @param challenge a unique id provided by a recent verification of device credentials
+     *     (e.g. pin, pattern or password).
+     * @param callback an object to receive enrollment events
+     * @param cancel an object that can be used to cancel enrollment
+     * @param flags optional flags
+     */
+    public void enroll(long challenge, EnrollmentCallback callback,
+            CancellationSignal cancel, int flags) {
+        if (callback == null) {
+            throw new IllegalArgumentException("Must supply an enrollment callback");
+        }
+
+        // TODO: handle cancel
+
+        if (mService != null) try {
+            mEnrollmentCallback = callback;
+            startListening();
+            mService.enroll(mToken, getCurrentUserId(), flags);
+        } catch (RemoteException e) {
+            Log.v(TAG, "Remote exception in enroll: ", e);
+            stopListening();
+        }
+    }
+
+    /**
+     * Remove given fingerprint template from fingerprint hardware and/or protected storage.
+     * @param fp the fingerprint item to remove
+     * @param callback an optional callback to verify that fingerprint templates have been
+     * successfully removed.  May be null of no callback is required.
+     * @hide
+     */
+    public void remove(Fingerprint fp, RemovalCallback callback) {
+        if (mService != null) try {
+            mRemovalCallback = callback;
+            mRemovalFingerprint = fp;
+            startListening();
+            mService.remove(mToken, fp.getFingerId(), getCurrentUserId());
+        } catch (RemoteException e) {
+            Log.v(TAG, "Remote in remove: ", e);
+            stopListening();
+        }
+    }
+
+    /**
+     * Renames the given fingerprint template
+     * @param fpId the fingerprint id
+     * @param newName the new name
+     * @hide
+     */
+    public void rename(int fpId, String newName) {
+        // Renames the given fpId
+        if (mService != null) {
+            try {
+                mService.rename(fpId, getCurrentUserId(), newName);
+            } catch (RemoteException e) {
+                Log.v(TAG, "Remote exception in rename(): ", e);
+            }
+        } else {
+            Log.w(TAG, "rename(): Service not connected!");
+        }
+    }
+
+    /**
+     * Obtain the list of enrolled fingerprints templates.
+     * @return list of current fingerprint items
+     */
+    public List<Fingerprint> getEnrolledFingerprints() {
+        if (mService != null) try {
+            return mService.getEnrolledFingerprints(getCurrentUserId());
+        } catch (RemoteException e) {
+            Log.v(TAG, "Remote exception in getEnrolledFingerprints: ", e);
+        }
+        return null;
+    }
+
+    /**
+     * Determine if fingerprint hardware is present and functional.
+     * @return true if hardware is present and functional, false otherwise.
+     * @hide
+     */
+    public boolean isHardwareDetected() {
+        if (mService != null) {
+            try {
+                long deviceId = 0; /* TODO: plumb hardware id to FPMS */
+                return mService.isHardwareDetected(deviceId);
+            } catch (RemoteException e) {
+                Log.v(TAG, "Remote exception in isFingerprintHardwareDetected(): ", e);
+            }
+        } else {
+            Log.w(TAG, "isFingerprintHardwareDetected(): Service not connected!");
+        }
+        return false;
+    }
+
+    private Handler mHandler = new Handler() {
+        public void handleMessage(android.os.Message msg) {
+            switch(msg.what) {
+                case MSG_ENROLL_RESULT:
+                    sendEnrollResult((Fingerprint) msg.obj, msg.arg1 /* remaining */);
+                    break;
+                case MSG_ACQUIRED:
+                    sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */);
+                    break;
+                case MSG_PROCESSED:
+                    sendProcessedResult((Fingerprint) msg.obj);
+                    break;
+                case MSG_ERROR:
+                    sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */);
+                    break;
+                case MSG_REMOVED:
+                    sendRemovedResult((Long) msg.obj /* deviceId */, msg.arg1 /* fingerId */,
+                            msg.arg2 /* groupId */);
+            }
+        }
+
+        private void sendRemovedResult(long deviceId, int fingerId, int groupId) {
+            if (mRemovalCallback != null) {
+                int reqFingerId = mRemovalFingerprint.getFingerId();
+                int reqGroupId = mRemovalFingerprint.getGroupId();
+                if (fingerId != reqFingerId) {
+                    Log.w(TAG, "Finger id didn't match: " + fingerId + " != " + reqFingerId);
+                }
+                if (fingerId != reqFingerId) {
+                    Log.w(TAG, "Group id didn't match: " + groupId + " != " + reqGroupId);
+                }
+                mRemovalCallback.onRemovalSucceeded(mRemovalFingerprint);
+            }
+        }
+
+        private void sendErrorResult(long deviceId, int errMsgId) {
+            if (mEnrollmentCallback != null) {
+                mEnrollmentCallback.onEnrollmentError(errMsgId, getErrorString(errMsgId));
+            } else if (mAuthenticationCallback != null) {
+                mAuthenticationCallback.onAuthenticationError(errMsgId, getErrorString(errMsgId));
+            } else if (mRemovalCallback != null) {
+                mRemovalCallback.onRemovalError(mRemovalFingerprint, errMsgId,
+                        getErrorString(errMsgId));
+            }
+        }
+
+        private void sendEnrollResult(Fingerprint fp, int remaining) {
+            if (mEnrollmentCallback != null) {
+                mEnrollmentCallback.onEnrollmentProgress(remaining);
+            }
+        }
+
+        private void sendProcessedResult(Fingerprint fp) {
+            if (mAuthenticationCallback != null) {
+                AuthenticationResult result = new AuthenticationResult(mCryptoObject, fp);
+                mAuthenticationCallback.onAuthenticationSucceeded(result);
+            }
+        }
+
+        private void sendAcquiredResult(long deviceId, int acquireInfo) {
+            final String msg = getAcquiredString(acquireInfo);
+            if (msg == null) return;
+
+            if (mEnrollmentCallback != null) {
+                mEnrollmentCallback.onEnrollmentHelp(acquireInfo, msg);
+            } else if (mAuthenticationCallback != null) {
+                mAuthenticationCallback.onAuthenticationHelp(acquireInfo, msg);
+            }
+        }
+
+        private String getErrorString(int errMsg) {
+            switch (errMsg) {
+                case FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
+                    return mContext.getString(
+                        com.android.internal.R.string.fingerprint_error_unable_to_process);
+                case FINGERPRINT_ERROR_HW_UNAVAILABLE:
+                    return mContext.getString(
+                        com.android.internal.R.string.fingerprint_error_hw_not_available);
+                case FINGERPRINT_ERROR_NO_SPACE:
+                    return mContext.getString(
+                        com.android.internal.R.string.fingerprint_error_no_space);
+                case FINGERPRINT_ERROR_TIMEOUT:
+                    return mContext.getString(
+                        com.android.internal.R.string.fingerprint_error_timeout);
+                default:
+                    if (errMsg >= FINGERPRINT_ERROR_VENDOR_BASE) {
+                        int msgNumber = errMsg - FINGERPRINT_ERROR_VENDOR_BASE;
+                        String[] msgArray = mContext.getResources().getStringArray(
+                                com.android.internal.R.array.fingerprint_error_vendor);
+                        if (msgNumber < msgArray.length) {
+                            return msgArray[msgNumber];
+                        }
+                    }
+                    return null;
+            }
+        }
+
+        private String getAcquiredString(int acquireInfo) {
+            switch (acquireInfo) {
+                case FINGERPRINT_ACQUIRED_GOOD:
+                    return null;
+                case FINGERPRINT_ACQUIRED_PARTIAL:
+                    return mContext.getString(
+                        com.android.internal.R.string.fingerprint_acquired_partial);
+                case FINGERPRINT_ACQUIRED_INSUFFICIENT:
+                    return mContext.getString(
+                        com.android.internal.R.string.fingerprint_acquired_insufficient);
+                case FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
+                    return mContext.getString(
+                        com.android.internal.R.string.fingerprint_acquired_imager_dirty);
+                case FINGERPRINT_ACQUIRED_TOO_SLOW:
+                    return mContext.getString(
+                        com.android.internal.R.string.fingerprint_acquired_too_slow);
+                case FINGERPRINT_ACQUIRED_TOO_FAST:
+                    return mContext.getString(
+                        com.android.internal.R.string.fingerprint_acquired_too_fast);
+                default:
+                    if (acquireInfo >= FINGERPRINT_ACQUIRED_VENDOR_BASE) {
+                        int msgNumber = acquireInfo - FINGERPRINT_ACQUIRED_VENDOR_BASE;
+                        String[] msgArray = mContext.getResources().getStringArray(
+                                com.android.internal.R.array.fingerprint_acquired_vendor);
+                        if (msgNumber < msgArray.length) {
+                            return msgArray[msgNumber];
+                        }
+                    }
+                    return null;
+            }
+        }
+    };
+
+    /**
+     * @hide
+     */
+    public FingerprintManager(Context context, IFingerprintService service) {
+        mContext = context;
+        mService = service;
+        if (mService == null) {
+            Slog.v(TAG, "FingerprintManagerService was null");
+        }
+    }
+
+    private int getCurrentUserId() {
+        try {
+            return ActivityManagerNative.getDefault().getCurrentUser().id;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to get current user id\n");
+            return UserHandle.USER_NULL;
+        }
+    }
+
+    /**
+     * Stops the client from listening to fingerprint events.
+     */
+    private void stopListening() {
+        if (mService != null) {
+            try {
+                if (mListening) {
+                    mService.removeListener(mToken, mServiceReceiver);
+                    mListening = false;
+                }
+            } catch (RemoteException e) {
+                Log.v(TAG, "Remote exception in stopListening(): ", e);
+            }
+        } else {
+            Log.w(TAG, "stopListening(): Service not connected!");
+        }
+    }
+
+    /**
+     * Starts listening for fingerprint events for this client.
+     */
+    private void startListening() {
+        if (mService != null) {
+            try {
+                if (!mListening) {
+                    mService.addListener(mToken, mServiceReceiver, getCurrentUserId());
+                    mListening = true;
+                }
+            } catch (RemoteException e) {
+                Log.v(TAG, "Remote exception in startListening(): ", e);
+            }
+        } else {
+            Log.w(TAG, "startListening(): Service not connected!");
+        }
+    }
+
+    private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
+
+        public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
+            mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
+                    new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
+        }
+
+        public void onAcquired(long deviceId, int acquireInfo) {
+            mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, 0, deviceId).sendToTarget();
+        }
+
+        public void onProcessed(long deviceId, int fingerId, int groupId) {
+            mHandler.obtainMessage(MSG_PROCESSED,
+                    new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
+        }
+
+        public void onError(long deviceId, int error) {
+            mHandler.obtainMessage(MSG_ERROR, error, 0, deviceId).sendToTarget();
+        }
+
+        public void onRemoved(long deviceId, int fingerId, int groupId) {
+            mHandler.obtainMessage(MSG_REMOVED, fingerId, groupId, deviceId).sendToTarget();
+        }
+    };
+
+}
\ No newline at end of file
diff --git a/core/java/android/service/fingerprint/FingerprintUtils.java b/core/java/android/hardware/fingerprint/FingerprintUtils.java
similarity index 96%
rename from core/java/android/service/fingerprint/FingerprintUtils.java
rename to core/java/android/hardware/fingerprint/FingerprintUtils.java
index cc17b99..ae3d4a4 100644
--- a/core/java/android/service/fingerprint/FingerprintUtils.java
+++ b/core/java/android/hardware/fingerprint/FingerprintUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.service.fingerprint;
+package android.hardware.fingerprint;
 
 import android.content.ContentResolver;
 import android.provider.Settings;
@@ -67,7 +67,7 @@
         return toIntArray(tmp);
     }
 
-    public static void addFingerprintIdForUser(int fingerId, ContentResolver res, int userId) {
+    public static void addFingerprintIdForUser(ContentResolver res, int fingerId, int userId) {
         // FingerId 0 has special meaning.
         if (fingerId == 0) {
             Log.w(TAG, "Tried to add fingerId 0");
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
new file mode 100644
index 0000000..c5a45e2
--- /dev/null
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -0,0 +1,58 @@
+/*
+ * 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 android.hardware.fingerprint;
+
+import android.os.Bundle;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.hardware.fingerprint.Fingerprint;
+import java.util.List;
+
+/**
+ * Communication channel from client to the fingerprint service.
+ * @hide
+ */
+interface IFingerprintService {
+    // Authenticate the given sessionId with a fingerprint
+    void authenticate(IBinder token, long sessionId, int groupId, int flags);
+
+    // Start fingerprint enrollment
+    void enroll(IBinder token, int groupId, int flags);
+
+    // Any errors resulting from this call will be returned to the listener
+    void remove(IBinder token, int fingerId, int groupId);
+
+    // Rename the fingerprint specified by fingerId and groupId to the given name
+    void rename(int fingerId, int groupId, String name);
+
+    // Get a list of enrolled fingerprints in the given group.
+    List<Fingerprint> getEnrolledFingerprints(int groupId);
+
+    // Register listener for an instance of FingerprintManager
+    void addListener(IBinder token, IFingerprintServiceReceiver receiver, int userId);
+
+    // Unregister listener for an instance of FingerprintManager
+    void removeListener(IBinder token, IFingerprintServiceReceiver receiver);
+
+    // Determine if HAL is loaded and ready
+    boolean isHardwareDetected(long deviceId);
+
+    // Gets the number of hardware devices
+    // int getHardwareDeviceCount();
+
+    // Gets the unique device id for hardware enumerated at i
+    // long getHardwareDevice(int i);
+
+}
diff --git a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
similarity index 70%
rename from core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
rename to core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
index af4128f..e82395f 100644
--- a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.service.fingerprint;
+package android.hardware.fingerprint;
 
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -23,9 +23,9 @@
  * @hide
  */
 oneway interface IFingerprintServiceReceiver {
-    void onEnrollResult(int fingerprintId,  int remaining);
-    void onAcquired(int acquiredInfo);
-    void onProcessed(int fingerprintId);
-    void onError(int error);
-    void onRemoved(int fingerprintId);
+    void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining);
+    void onAcquired(long deviceId, int acquiredInfo);
+    void onProcessed(long deviceId, int fingerId, int groupId);
+    void onError(long deviceId, int error);
+    void onRemoved(long deviceId, int fingerId, int groupId);
 }
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index a94c1da..a336e5c 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -168,7 +168,22 @@
     }
 
     /**
-     * Sets system audio volume
+     * Sets system audio mode.
+     *
+     * @param enabled set to {@code true} to enable the mode; otherwise {@code false}
+     * @param callback callback to get the result with
+     * @throws {@link IllegalArgumentException} if the {@code callback} is null
+     */
+    public void setSystemAudioMode(boolean enabled, SelectCallback callback) {
+        try {
+            mService.setSystemAudioMode(enabled, getCallbackWrapper(callback));
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to set system audio mode:", e);
+        }
+    }
+
+    /**
+     * Sets system audio volume.
      *
      * @param oldIndex current volume index
      * @param newIndex volume index to be set
@@ -183,7 +198,7 @@
     }
 
     /**
-     * Sets system audio mute status
+     * Sets system audio mute status.
      *
      * @param mute {@code true} if muted; otherwise, {@code false}
      */
@@ -196,7 +211,7 @@
     }
 
     /**
-     * Sets record listener
+     * Sets record listener.
      *
      * @param listener
      */
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 34a0727..3abccbc 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -40,6 +40,7 @@
 import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.internal.net.VpnConfig;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.util.Protocol;
@@ -2337,7 +2338,7 @@
      * successfully finding a network for the applications request.  Retrieve it with
      * {@link android.content.Intent#getParcelableExtra(String)}.
      * <p>
-     * Note that if you intend to invoke (@link #setProcessDefaultNetwork(Network)) or
+     * Note that if you intend to invoke {@link #setProcessDefaultNetwork} or
      * {@link Network#openConnection(java.net.URL)} then you must get a
      * ConnectivityManager instance before doing so.
      */
@@ -2447,6 +2448,38 @@
     }
 
     /**
+     * Resets all connectivity manager settings back to factory defaults.
+     * @hide
+     */
+    public void factoryReset() {
+        // Turn airplane mode off
+        setAirplaneMode(false);
+
+        // Untether
+        for (String tether : getTetheredIfaces()) {
+            untether(tether);
+        }
+
+        // Turn VPN off
+        try {
+            VpnConfig vpnConfig = mService.getVpnConfig();
+            if (vpnConfig != null) {
+                if (vpnConfig.legacy) {
+                    mService.prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+                } else {
+                    // Prevent this app from initiating VPN connections in the future without
+                    // user intervention.
+                    mService.setVpnPackageAuthorization(false);
+
+                    mService.prepareVpn(vpnConfig.user, VpnConfig.LEGACY_VPN);
+                }
+            }
+        } catch (RemoteException e) {
+            // Well, we tried
+        }
+    }
+
+    /**
      * Binds the current process to {@code network}.  All Sockets created in the future
      * (and not explicitly bound via a bound SocketFactory from
      * {@link Network#getSocketFactory() Network.getSocketFactory()}) will be bound to
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 2c3881c..6436e42 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -27,6 +27,14 @@
     /** Start a statistics query session. */
     INetworkStatsSession openSession();
 
+    /** Start a statistics query session. If calling package is profile or device owner then it is
+     *  granted automatic access if apiLevel is NetworkStatsManager.API_LEVEL_DPC_ALLOWED. If
+     *  apiLevel is at least NetworkStatsManager.API_LEVEL_REQUIRES_PACKAGE_USAGE_STATS then
+     *  PACKAGE_USAGE_STATS permission is always checked. If PACKAGE_USAGE_STATS is not granted
+     *  READ_NETWORK_USAGE_STATS is checked for.
+     */
+    INetworkStatsSession openSessionForUsageStats(String callingPackage);
+
     /** Return network layer usage total for traffic that matches template. */
     long getNetworkTotalBytes(in NetworkTemplate template, long start, long end);
 
diff --git a/core/java/android/net/INetworkStatsSession.aidl b/core/java/android/net/INetworkStatsSession.aidl
index 1596fa2..7bcb043 100644
--- a/core/java/android/net/INetworkStatsSession.aidl
+++ b/core/java/android/net/INetworkStatsSession.aidl
@@ -23,6 +23,9 @@
 /** {@hide} */
 interface INetworkStatsSession {
 
+    /** Return device aggregated network layer usage summary for traffic that matches template. */
+    NetworkStats getDeviceSummaryForNetwork(in NetworkTemplate template, long start, long end);
+
     /** Return network layer usage summary for traffic that matches template. */
     NetworkStats getSummaryForNetwork(in NetworkTemplate template, long start, long end);
     /** Return historical network layer stats for traffic that matches template. */
@@ -33,6 +36,9 @@
     /** Return historical network layer stats for specific UID traffic that matches template. */
     NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int set, int tag, int fields);
 
+    /** Return array of uids that have stats and are accessible to the calling user */
+    int[] getRelevantUids();
+
     void close();
 
 }
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index a7f9c5b..8c8bfab 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -148,9 +148,9 @@
      */
     public static final int NET_CAPABILITY_TRUSTED        = 14;
 
-    /*
+    /**
      * Indicates that this network is not a VPN.  This capability is set by default and should be
-     * explicitly cleared when creating VPN networks.
+     * explicitly cleared for VPN networks.
      */
     public static final int NET_CAPABILITY_NOT_VPN        = 15;
 
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index a8e7757..a7ffee9 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -180,6 +180,33 @@
     }
 
     /**
+     * Resets network policy settings back to factory defaults.
+     *
+     * @hide
+     */
+    public void factoryReset(String subscriber) {
+        // Turn mobile data limit off
+        NetworkPolicy[] policies = getNetworkPolicies();
+        NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriber);
+        for (NetworkPolicy policy : policies) {
+            if (policy.template.equals(template)) {
+                policy.limitBytes = NetworkPolicy.LIMIT_DISABLED;
+                policy.inferred = false;
+                policy.clearSnooze();
+            }
+        }
+        setNetworkPolicies(policies);
+
+        // Turn restrict background data off
+        setRestrictBackground(false);
+
+        // Remove app's "restrict background data" flag
+        for (int uid : getUidsWithPolicy(POLICY_REJECT_METERED_BACKGROUND)) {
+            setUidPolicy(uid, NetworkPolicyManager.POLICY_NONE);
+        }
+    }
+
+    /**
      * Compute the last cycle boundary for the given {@link NetworkPolicy}. For
      * example, if cycle day is 20th, and today is June 15th, it will return May
      * 20th. When cycle day doesn't exist in current month, it snaps to the 1st
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 0766253..77d7e0c 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -50,12 +50,19 @@
     public static final int UID_ALL = -1;
     /** {@link #tag} value matching any tag. */
     public static final int TAG_ALL = -1;
-    /** {@link #set} value when all sets combined. */
+    /** {@link #set} value when all sets combined, not including debug sets. */
     public static final int SET_ALL = -1;
     /** {@link #set} value where background data is accounted. */
     public static final int SET_DEFAULT = 0;
     /** {@link #set} value where foreground data is accounted. */
     public static final int SET_FOREGROUND = 1;
+    /** All {@link #set} value greater than SET_DEBUG_START are debug {@link #set} values. */
+    public static final int SET_DEBUG_START = 1000;
+    /** Debug {@link #set} value when the VPN stats are moved in. */
+    public static final int SET_DBG_VPN_IN = 1001;
+    /** Debug {@link #set} value when the VPN stats are moved out of a vpn UID. */
+    public static final int SET_DBG_VPN_OUT = 1002;
+
     /** {@link #tag} value for total data across all tags. */
     public static final int TAG_NONE = 0;
 
@@ -729,6 +736,10 @@
                 return "DEFAULT";
             case SET_FOREGROUND:
                 return "FOREGROUND";
+            case SET_DBG_VPN_IN:
+                return "DBG_VPN_IN";
+            case SET_DBG_VPN_OUT:
+                return "DBG_VPN_OUT";
             default:
                 return "UNKNOWN";
         }
@@ -745,12 +756,27 @@
                 return "def";
             case SET_FOREGROUND:
                 return "fg";
+            case SET_DBG_VPN_IN:
+                return "vpnin";
+            case SET_DBG_VPN_OUT:
+                return "vpnout";
             default:
                 return "unk";
         }
     }
 
     /**
+     * @return true if the querySet matches the dataSet.
+     */
+    public static boolean setMatches(int querySet, int dataSet) {
+        if (querySet == dataSet) {
+            return true;
+        }
+        // SET_ALL matches all non-debugging sets.
+        return querySet == SET_ALL && dataSet < SET_DEBUG_START;
+    }
+
+    /**
      * Return text description of {@link #tag} value.
      */
     public static String tagToString(int tag) {
@@ -843,6 +869,9 @@
             if (recycle.uid == UID_ALL) {
                 throw new IllegalStateException(
                         "Cannot adjust VPN accounting on an iface aggregated NetworkStats.");
+            } if (recycle.set == SET_DBG_VPN_IN || recycle.set == SET_DBG_VPN_OUT) {
+                throw new IllegalStateException(
+                        "Cannot adjust VPN accounting on a NetworkStats containing SET_DBG_VPN_*");
             }
 
             if (recycle.uid == tunUid && recycle.tag == TAG_NONE
@@ -906,6 +935,9 @@
                 combineValues(tmpEntry);
                 if (tag[i] == TAG_NONE) {
                     moved.add(tmpEntry);
+                    // Add debug info
+                    tmpEntry.set = SET_DBG_VPN_IN;
+                    combineValues(tmpEntry);
                 }
             }
         }
@@ -913,6 +945,13 @@
     }
 
     private void deductTrafficFromVpnApp(int tunUid, String underlyingIface, Entry moved) {
+        // Add debug info
+        moved.uid = tunUid;
+        moved.set = SET_DBG_VPN_OUT;
+        moved.tag = TAG_NONE;
+        moved.iface = underlyingIface;
+        combineValues(moved);
+
         // Caveat: if the vpn software uses tag, the total tagged traffic may be greater than
         // the TAG_NONE traffic.
         int idxVpnBackground = findIndex(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE);
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 3f18519..ff3de2b 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -51,6 +51,8 @@
     public static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
     /** @hide */
     public static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
+    /** @hide */
+    public static final long TB_IN_BYTES = GB_IN_BYTES * 1024;
 
     /**
      * Special UID value used when collecting {@link NetworkStatsHistory} for
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index bf3d9aa..f305b2a 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -366,7 +366,6 @@
     public String toSafeString() {
         String scheme = getScheme();
         String ssp = getSchemeSpecificPart();
-        String authority = null;
         if (scheme != null) {
             if (scheme.equalsIgnoreCase("tel") || scheme.equalsIgnoreCase("sip")
                     || scheme.equalsIgnoreCase("sms") || scheme.equalsIgnoreCase("smsto")
@@ -385,9 +384,11 @@
                     }
                 }
                 return builder.toString();
-            } else if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")) {
-                ssp = null;
-                authority = "//" + getAuthority() + "/...";
+            } else if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")
+                    || scheme.equalsIgnoreCase("ftp")) {
+                ssp = "//" + ((getHost() != null) ? getHost() : "")
+                        + ((getPort() != -1) ? (":" + getPort()) : "")
+                        + "/...";
             }
         }
         // Not a sensitive scheme, but let's still be conservative about
@@ -401,9 +402,6 @@
         if (ssp != null) {
             builder.append(ssp);
         }
-        if (authority != null) {
-            builder.append(authority);
-        }
         return builder.toString();
     }
 
diff --git a/core/java/android/net/WifiKey.java b/core/java/android/net/WifiKey.java
index 71df2f9..99de99e 100644
--- a/core/java/android/net/WifiKey.java
+++ b/core/java/android/net/WifiKey.java
@@ -33,7 +33,8 @@
 public class WifiKey implements Parcelable {
 
     // Patterns used for validation.
-    private static final Pattern SSID_PATTERN = Pattern.compile("(\".*\")|(0x[\\p{XDigit}]+)");
+    private static final Pattern SSID_PATTERN = Pattern.compile("(\".*\")|(0x[\\p{XDigit}]+)",
+            Pattern.DOTALL);
     private static final Pattern BSSID_PATTERN =
             Pattern.compile("([\\p{XDigit}]{2}:){5}[\\p{XDigit}]{2}");
 
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index cab03da..508fdee 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -29,6 +29,7 @@
 import android.content.pm.ApplicationInfo;
 import android.telephony.SignalStrength;
 import android.text.format.DateFormat;
+import android.util.ArrayMap;
 import android.util.Printer;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -283,21 +284,21 @@
          *
          * @return a Map from Strings to Uid.Wakelock objects.
          */
-        public abstract Map<String, ? extends Wakelock> getWakelockStats();
+        public abstract ArrayMap<String, ? extends Wakelock> getWakelockStats();
 
         /**
          * Returns a mapping containing sync statistics.
          *
          * @return a Map from Strings to Timer objects.
          */
-        public abstract Map<String, ? extends Timer> getSyncStats();
+        public abstract ArrayMap<String, ? extends Timer> getSyncStats();
 
         /**
          * Returns a mapping containing scheduled job statistics.
          *
          * @return a Map from Strings to Timer objects.
          */
-        public abstract Map<String, ? extends Timer> getJobStats();
+        public abstract ArrayMap<String, ? extends Timer> getJobStats();
 
         /**
          * The statistics associated with a particular wake lock.
@@ -323,14 +324,14 @@
          *
          * @return a Map from Strings to Uid.Proc objects.
          */
-        public abstract Map<String, ? extends Proc> getProcessStats();
+        public abstract ArrayMap<String, ? extends Proc> getProcessStats();
 
         /**
          * Returns a mapping containing package statistics.
          *
          * @return a Map from Strings to Uid.Pkg objects.
          */
-        public abstract Map<String, ? extends Pkg> getPackageStats();
+        public abstract ArrayMap<String, ? extends Pkg> getPackageStats();
         
         /**
          * {@hide}
@@ -501,17 +502,16 @@
         public static abstract class Pkg {
 
             /**
-             * Returns the number of times this package has done something that could wake up the
-             * device from sleep.
-             *
-             * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+             * Returns information about all wakeup alarms that have been triggered for this
+             * package.  The mapping keys are tag names for the alarms, the counter contains
+             * the number of times the alarm was triggered while on battery.
              */
-            public abstract int getWakeups(int which);
+            public abstract ArrayMap<String, ? extends Counter> getWakeupAlarmStats();
 
             /**
              * Returns a mapping containing service statistics.
              */
-            public abstract Map<String, ? extends Serv> getServiceStats();
+            public abstract ArrayMap<String, ? extends Serv> getServiceStats();
 
             /**
              * The statistics associated with a particular service.
@@ -613,6 +613,9 @@
             if ((initMode&STEP_LEVEL_MODE_POWER_SAVE) != 0) {
                 out.append('p');
             }
+            if ((initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0) {
+                out.append('i');
+            }
             switch ((modMode&STEP_LEVEL_MODE_SCREEN_STATE) + 1) {
                 case Display.STATE_OFF: out.append('F'); break;
                 case Display.STATE_ON: out.append('O'); break;
@@ -622,6 +625,9 @@
             if ((modMode&STEP_LEVEL_MODE_POWER_SAVE) != 0) {
                 out.append('P');
             }
+            if ((modMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0) {
+                out.append('I');
+            }
             out.append('-');
             appendHex(level, 4, out);
             out.append('-');
@@ -648,6 +654,9 @@
                     case 'p': out |= (((long)STEP_LEVEL_MODE_POWER_SAVE)
                             << STEP_LEVEL_INITIAL_MODE_SHIFT);
                         break;
+                    case 'i': out |= (((long)STEP_LEVEL_MODE_DEVICE_IDLE)
+                            << STEP_LEVEL_INITIAL_MODE_SHIFT);
+                        break;
                     case 'F': out |= (((long)Display.STATE_OFF-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT);
                         break;
                     case 'O': out |= (((long)Display.STATE_ON-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT);
@@ -660,6 +669,9 @@
                     case 'P': out |= (((long)STEP_LEVEL_MODE_POWER_SAVE)
                             << STEP_LEVEL_MODIFIED_MODE_SHIFT);
                         break;
+                    case 'I': out |= (((long)STEP_LEVEL_MODE_DEVICE_IDLE)
+                            << STEP_LEVEL_MODIFIED_MODE_SHIFT);
+                        break;
                 }
             }
             i++;
@@ -820,11 +832,18 @@
         }
     }
 
+    public static final class PackageChange {
+        public String mPackageName;
+        public boolean mUpdate;
+        public int mVersionCode;
+    }
+
     public static final class DailyItem {
         public long mStartTime;
         public long mEndTime;
         public LevelStepTracker mDischargeSteps;
         public LevelStepTracker mChargeSteps;
+        public ArrayList<PackageChange> mPackageChanges;
     }
 
     public abstract DailyItem getDailyItemLocked(int daysAgo);
@@ -1333,7 +1352,7 @@
                 int idx = code&HistoryItem.EVENT_TYPE_MASK;
                 HashMap<String, SparseIntArray> active = mActiveEvents[idx];
                 if (active == null) {
-                    active = new HashMap<String, SparseIntArray>();
+                    active = new HashMap<>();
                     mActiveEvents[idx] = active;
                 }
                 SparseIntArray uids = active.get(name);
@@ -1524,6 +1543,23 @@
     public abstract int getDeviceIdleModeEnabledCount(int which);
 
     /**
+     * Returns the time in microseconds that device has been in idling while on
+     * battery.  This is broader than {@link #getDeviceIdleModeEnabledTime} -- it
+     * counts all of the time that we consider the device to be idle, whether or not
+     * it is currently in the actual device idle mode.
+     *
+     * {@hide}
+     */
+    public abstract long getDeviceIdlingTime(long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the number of times that the devie has started idling.
+     *
+     * {@hide}
+     */
+    public abstract int getDeviceIdlingCount(int which);
+
+    /**
      * Returns the number of times that connectivity state changed.
      *
      * {@hide}
@@ -2069,45 +2105,44 @@
     // Step duration mode: power save is on.
     public static final int STEP_LEVEL_MODE_POWER_SAVE = 0x04;
 
+    // Step duration mode: device is currently in idle mode.
+    public static final int STEP_LEVEL_MODE_DEVICE_IDLE = 0x08;
+
     public static final int[] STEP_LEVEL_MODES_OF_INTEREST = new int[] {
             STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE|STEP_LEVEL_MODE_DEVICE_IDLE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_DEVICE_IDLE,
             STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
             STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
             STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
             STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
             STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
-            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
-            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE|STEP_LEVEL_MODE_DEVICE_IDLE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_DEVICE_IDLE,
     };
     public static final int[] STEP_LEVEL_MODE_VALUES = new int[] {
             (Display.STATE_OFF-1),
             (Display.STATE_OFF-1)|STEP_LEVEL_MODE_POWER_SAVE,
+            (Display.STATE_OFF-1)|STEP_LEVEL_MODE_DEVICE_IDLE,
             (Display.STATE_ON-1),
             (Display.STATE_ON-1)|STEP_LEVEL_MODE_POWER_SAVE,
             (Display.STATE_DOZE-1),
             (Display.STATE_DOZE-1)|STEP_LEVEL_MODE_POWER_SAVE,
             (Display.STATE_DOZE_SUSPEND-1),
             (Display.STATE_DOZE_SUSPEND-1)|STEP_LEVEL_MODE_POWER_SAVE,
+            (Display.STATE_DOZE_SUSPEND-1)|STEP_LEVEL_MODE_DEVICE_IDLE,
     };
     public static final String[] STEP_LEVEL_MODE_LABELS = new String[] {
             "screen off",
             "screen off power save",
+            "screen off device idle",
             "screen on",
             "screen on power save",
             "screen doze",
             "screen doze power save",
             "screen doze-suspend",
             "screen doze-suspend power save",
-    };
-    public static final String[] STEP_LEVEL_MODE_TAGS = new String[] {
-            "off",
-            "off-save",
-            "on",
-            "on-save",
-            "doze",
-            "doze-save",
-            "susp",
-            "susp-save",
+            "screen doze-suspend device idle",
     };
 
     /**
@@ -2140,6 +2175,8 @@
      */
     public abstract LevelStepTracker getDailyChargeLevelStepTracker();
 
+    public abstract ArrayList<PackageChange> getDailyPackageChanges();
+
     public abstract Map<String, ? extends Timer> getWakeupReasonStats();
 
     public abstract Map<String, ? extends Timer> getKernelWakelockStats();
@@ -2338,18 +2375,19 @@
         final long interactiveTime = getInteractiveTime(rawRealtime, which);
         final long powerSaveModeEnabledTime = getPowerSaveModeEnabledTime(rawRealtime, which);
         final long deviceIdleModeEnabledTime = getDeviceIdleModeEnabledTime(rawRealtime, which);
+        final long deviceIdlingTime = getDeviceIdlingTime(rawRealtime, which);
         final int connChanges = getNumConnectivityChange(which);
         final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
         final long wifiOnTime = getWifiOnTime(rawRealtime, which);
         final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which);
         final long bluetoothOnTime = getBluetoothOnTime(rawRealtime, which);
 
-        StringBuilder sb = new StringBuilder(128);
+        final StringBuilder sb = new StringBuilder(128);
         
-        SparseArray<? extends Uid> uidStats = getUidStats();
+        final SparseArray<? extends Uid> uidStats = getUidStats();
         final int NU = uidStats.size();
         
-        String category = STAT_NAMES[which];
+        final String category = STAT_NAMES[which];
 
         // Dump "battery" stat
         dumpLine(pw, 0 /* uid */, category, BATTERY_DATA, 
@@ -2364,37 +2402,35 @@
         long partialWakeLockTimeTotal = 0;
         
         for (int iu = 0; iu < NU; iu++) {
-            Uid u = uidStats.valueAt(iu);
+            final Uid u = uidStats.valueAt(iu);
 
-            Map<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats();
-            if (wakelocks.size() > 0) {
-                for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> ent 
-                        : wakelocks.entrySet()) {
-                    Uid.Wakelock wl = ent.getValue();
-                    
-                    Timer fullWakeTimer = wl.getWakeTime(WAKE_TYPE_FULL);
-                    if (fullWakeTimer != null) {
-                        fullWakeLockTimeTotal += fullWakeTimer.getTotalTimeLocked(rawRealtime,
-                                which);
-                    }
+            final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks
+                    = u.getWakelockStats();
+            for (int iw=wakelocks.size()-1; iw>=0; iw--) {
+                final Uid.Wakelock wl = wakelocks.valueAt(iw);
 
-                    Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
-                    if (partialWakeTimer != null) {
-                        partialWakeLockTimeTotal += partialWakeTimer.getTotalTimeLocked(
-                            rawRealtime, which);
-                    }
+                final Timer fullWakeTimer = wl.getWakeTime(WAKE_TYPE_FULL);
+                if (fullWakeTimer != null) {
+                    fullWakeLockTimeTotal += fullWakeTimer.getTotalTimeLocked(rawRealtime,
+                            which);
+                }
+
+                final Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
+                if (partialWakeTimer != null) {
+                    partialWakeLockTimeTotal += partialWakeTimer.getTotalTimeLocked(
+                        rawRealtime, which);
                 }
             }
         }
         
-        long mobileRxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
-        long mobileTxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
-        long wifiRxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
-        long wifiTxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
-        long mobileRxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
-        long mobileTxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
-        long wifiRxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
-        long wifiTxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
+        final long mobileRxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
+        final long mobileTxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
+        final long wifiRxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
+        final long wifiTxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
+        final long mobileRxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
+        final long mobileTxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
+        final long wifiRxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
+        final long wifiTxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
 
         // Dump network stats
         dumpLine(pw, 0 /* uid */, category, GLOBAL_NETWORK_DATA,
@@ -2410,7 +2446,8 @@
                 0 /*legacy input event count*/, getMobileRadioActiveTime(rawRealtime, which) / 1000,
                 getMobileRadioActiveAdjustedTime(which) / 1000, interactiveTime / 1000,
                 powerSaveModeEnabledTime / 1000, connChanges, deviceIdleModeEnabledTime / 1000,
-                getDeviceIdleModeEnabledCount(which));
+                getDeviceIdleModeEnabledCount(which), deviceIdlingTime / 1000,
+                getDeviceIdlingCount(which));
         
         // Dump screen brightness stats
         Object[] args = new Object[NUM_SCREEN_BRIGHTNESS_BINS];
@@ -2505,7 +2542,7 @@
         }
         
         if (reqUid < 0) {
-            Map<String, ? extends Timer> kernelWakelocks = getKernelWakelockStats();
+            final Map<String, ? extends Timer> kernelWakelocks = getKernelWakelockStats();
             if (kernelWakelocks.size() > 0) {
                 for (Map.Entry<String, ? extends Timer> ent : kernelWakelocks.entrySet()) {
                     sb.setLength(0);
@@ -2514,7 +2551,7 @@
                             sb.toString());
                 }
             }
-            Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
+            final Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
             if (wakeupReasons.size() > 0) {
                 for (Map.Entry<String, ? extends Timer> ent : wakeupReasons.entrySet()) {
                     // Not doing the regular wake lock formatting to remain compatible
@@ -2527,10 +2564,10 @@
             }
         }
         
-        BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
+        final BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
         helper.create(this);
         helper.refreshStats(which, UserHandle.USER_ALL);
-        List<BatterySipper> sippers = helper.getUsageList();
+        final List<BatterySipper> sippers = helper.getUsageList();
         if (sippers != null && sippers.size() > 0) {
             dumpLine(pw, 0 /* uid */, category, POWER_USE_SUMMARY_DATA,
                     BatteryStatsHelper.makemAh(helper.getPowerProfile().getBatteryCapacity()),
@@ -2538,7 +2575,7 @@
                     BatteryStatsHelper.makemAh(helper.getMinDrainedPower()),
                     BatteryStatsHelper.makemAh(helper.getMaxDrainedPower()));
             for (int i=0; i<sippers.size(); i++) {
-                BatterySipper bs = sippers.get(i);
+                final BatterySipper bs = sippers.get(i);
                 int uid = 0;
                 String label;
                 switch (bs.drainType) {
@@ -2590,22 +2627,22 @@
             if (reqUid >= 0 && uid != reqUid) {
                 continue;
             }
-            Uid u = uidStats.valueAt(iu);
+            final Uid u = uidStats.valueAt(iu);
             // Dump Network stats per uid, if any
-            long mobileBytesRx = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
-            long mobileBytesTx = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
-            long wifiBytesRx = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
-            long wifiBytesTx = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
-            long mobilePacketsRx = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
-            long mobilePacketsTx = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
-            long mobileActiveTime = u.getMobileRadioActiveTime(which);
-            int mobileActiveCount = u.getMobileRadioActiveCount(which);
-            long wifiPacketsRx = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
-            long wifiPacketsTx = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
-            long fullWifiLockOnTime = u.getFullWifiLockTime(rawRealtime, which);
-            long wifiScanTime = u.getWifiScanTime(rawRealtime, which);
-            int wifiScanCount = u.getWifiScanCount(which);
-            long uidWifiRunningTime = u.getWifiRunningTime(rawRealtime, which);
+            final long mobileBytesRx = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
+            final long mobileBytesTx = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
+            final long wifiBytesRx = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
+            final long wifiBytesTx = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
+            final long mobilePacketsRx = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
+            final long mobilePacketsTx = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
+            final long mobileActiveTime = u.getMobileRadioActiveTime(which);
+            final int mobileActiveCount = u.getMobileRadioActiveCount(which);
+            final long wifiPacketsRx = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
+            final long wifiPacketsTx = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
+            final long fullWifiLockOnTime = u.getFullWifiLockTime(rawRealtime, which);
+            final long wifiScanTime = u.getWifiScanTime(rawRealtime, which);
+            final int wifiScanCount = u.getWifiScanCount(which);
+            final long uidWifiRunningTime = u.getWifiRunningTime(rawRealtime, which);
 
             if (mobileBytesRx > 0 || mobileBytesTx > 0 || wifiBytesRx > 0 || wifiBytesTx > 0
                     || mobilePacketsRx > 0 || mobilePacketsTx > 0 || wifiPacketsRx > 0
@@ -2636,93 +2673,90 @@
                 }
             }
             
-            Map<String, ? extends Uid.Wakelock> wakelocks = u.getWakelockStats();
-            if (wakelocks.size() > 0) {
-                for (Map.Entry<String, ? extends Uid.Wakelock> ent : wakelocks.entrySet()) {
-                    Uid.Wakelock wl = ent.getValue();
-                    String linePrefix = "";
-                    sb.setLength(0);
-                    linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_FULL), 
-                            rawRealtime, "f", which, linePrefix);
-                    linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_PARTIAL), 
-                            rawRealtime, "p", which, linePrefix);
-                    linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_WINDOW), 
-                            rawRealtime, "w", which, linePrefix);
-                    
-                    // Only log if we had at lease one wakelock...
-                    if (sb.length() > 0) {
-                        String name = ent.getKey();
-                        if (name.indexOf(',') >= 0) {
-                            name = name.replace(',', '_');
-                        }
-                        dumpLine(pw, uid, category, WAKELOCK_DATA, name, sb.toString());
+            final ArrayMap<String, ? extends Uid.Wakelock> wakelocks = u.getWakelockStats();
+            for (int iw=wakelocks.size()-1; iw>=0; iw--) {
+                final Uid.Wakelock wl = wakelocks.valueAt(iw);
+                String linePrefix = "";
+                sb.setLength(0);
+                linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_FULL),
+                        rawRealtime, "f", which, linePrefix);
+                linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_PARTIAL),
+                        rawRealtime, "p", which, linePrefix);
+                linePrefix = printWakeLockCheckin(sb, wl.getWakeTime(WAKE_TYPE_WINDOW),
+                        rawRealtime, "w", which, linePrefix);
+
+                // Only log if we had at lease one wakelock...
+                if (sb.length() > 0) {
+                    String name = wakelocks.keyAt(iw);
+                    if (name.indexOf(',') >= 0) {
+                        name = name.replace(',', '_');
                     }
+                    dumpLine(pw, uid, category, WAKELOCK_DATA, name, sb.toString());
                 }
             }
 
-            Map<String, ? extends Timer> syncs = u.getSyncStats();
-            if (syncs.size() > 0) {
-                for (Map.Entry<String, ? extends Timer> ent : syncs.entrySet()) {
-                    Timer timer = ent.getValue();
-                    // Convert from microseconds to milliseconds with rounding
-                    long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
-                    int count = timer.getCountLocked(which);
-                    if (totalTime != 0) {
-                        dumpLine(pw, uid, category, SYNC_DATA, ent.getKey(), totalTime, count);
-                    }
+            final ArrayMap<String, ? extends Timer> syncs = u.getSyncStats();
+            for (int isy=syncs.size()-1; isy>=0; isy--) {
+                final Timer timer = syncs.valueAt(isy);
+                // Convert from microseconds to milliseconds with rounding
+                final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                final int count = timer.getCountLocked(which);
+                if (totalTime != 0) {
+                    dumpLine(pw, uid, category, SYNC_DATA, syncs.keyAt(isy), totalTime, count);
                 }
             }
 
-            Map<String, ? extends Timer> jobs = u.getJobStats();
-            if (jobs.size() > 0) {
-                for (Map.Entry<String, ? extends Timer> ent : jobs.entrySet()) {
-                    Timer timer = ent.getValue();
-                    // Convert from microseconds to milliseconds with rounding
-                    long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
-                    int count = timer.getCountLocked(which);
-                    if (totalTime != 0) {
-                        dumpLine(pw, uid, category, JOB_DATA, ent.getKey(), totalTime, count);
-                    }
+            final ArrayMap<String, ? extends Timer> jobs = u.getJobStats();
+            for (int ij=jobs.size()-1; ij>=0; ij--) {
+                final Timer timer = jobs.valueAt(ij);
+                // Convert from microseconds to milliseconds with rounding
+                final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                final int count = timer.getCountLocked(which);
+                if (totalTime != 0) {
+                    dumpLine(pw, uid, category, JOB_DATA, jobs.keyAt(ij), totalTime, count);
                 }
             }
 
-            SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
-            int NSE = sensors.size();
+            final SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
+            final int NSE = sensors.size();
             for (int ise=0; ise<NSE; ise++) {
-                Uid.Sensor se = sensors.valueAt(ise);
-                int sensorNumber = sensors.keyAt(ise);
-                Timer timer = se.getSensorTime();
+                final Uid.Sensor se = sensors.valueAt(ise);
+                final int sensorNumber = sensors.keyAt(ise);
+                final Timer timer = se.getSensorTime();
                 if (timer != null) {
                     // Convert from microseconds to milliseconds with rounding
-                    long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
-                    int count = timer.getCountLocked(which);
+                    final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500)
+                            / 1000;
+                    final int count = timer.getCountLocked(which);
                     if (totalTime != 0) {
                         dumpLine(pw, uid, category, SENSOR_DATA, sensorNumber, totalTime, count);
                     }
                 }
             }
 
-            Timer vibTimer = u.getVibratorOnTimer();
+            final Timer vibTimer = u.getVibratorOnTimer();
             if (vibTimer != null) {
                 // Convert from microseconds to milliseconds with rounding
-                long totalTime = (vibTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
-                int count = vibTimer.getCountLocked(which);
+                final long totalTime = (vibTimer.getTotalTimeLocked(rawRealtime, which) + 500)
+                        / 1000;
+                final int count = vibTimer.getCountLocked(which);
                 if (totalTime != 0) {
                     dumpLine(pw, uid, category, VIBRATOR_DATA, totalTime, count);
                 }
             }
 
-            Timer fgTimer = u.getForegroundActivityTimer();
+            final Timer fgTimer = u.getForegroundActivityTimer();
             if (fgTimer != null) {
                 // Convert from microseconds to milliseconds with rounding
-                long totalTime = (fgTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
-                int count = fgTimer.getCountLocked(which);
+                final long totalTime = (fgTimer.getTotalTimeLocked(rawRealtime, which) + 500)
+                        / 1000;
+                final int count = fgTimer.getCountLocked(which);
                 if (totalTime != 0) {
                     dumpLine(pw, uid, category, FOREGROUND_DATA, totalTime, count);
                 }
             }
 
-            Object[] stateTimes = new Object[Uid.NUM_PROCESS_STATE];
+            final Object[] stateTimes = new Object[Uid.NUM_PROCESS_STATE];
             long totalStateTime = 0;
             for (int ips=0; ips<Uid.NUM_PROCESS_STATE; ips++) {
                 totalStateTime += u.getProcessStateTime(ips, rawRealtime, which);
@@ -2732,50 +2766,48 @@
                 dumpLine(pw, uid, category, STATE_TIME_DATA, stateTimes);
             }
 
-            Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
-            if (processStats.size() > 0) {
-                for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
-                        : processStats.entrySet()) {
-                    Uid.Proc ps = ent.getValue();
+            final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats
+                    = u.getProcessStats();
+            for (int ipr=processStats.size()-1; ipr>=0; ipr--) {
+                final Uid.Proc ps = processStats.valueAt(ipr);
 
-                    final long userMillis = ps.getUserTime(which);
-                    final long systemMillis = ps.getSystemTime(which);
-                    final long foregroundMillis = ps.getForegroundTime(which);
-                    final int starts = ps.getStarts(which);
-                    final int numCrashes = ps.getNumCrashes(which);
-                    final int numAnrs = ps.getNumAnrs(which);
+                final long userMillis = ps.getUserTime(which);
+                final long systemMillis = ps.getSystemTime(which);
+                final long foregroundMillis = ps.getForegroundTime(which);
+                final int starts = ps.getStarts(which);
+                final int numCrashes = ps.getNumCrashes(which);
+                final int numAnrs = ps.getNumAnrs(which);
 
-                    if (userMillis != 0 || systemMillis != 0 || foregroundMillis != 0
-                            || starts != 0 || numAnrs != 0 || numCrashes != 0) {
-                        dumpLine(pw, uid, category, PROCESS_DATA, ent.getKey(), userMillis,
-                                systemMillis, foregroundMillis, starts, numAnrs, numCrashes);
-                    }
+                if (userMillis != 0 || systemMillis != 0 || foregroundMillis != 0
+                        || starts != 0 || numAnrs != 0 || numCrashes != 0) {
+                    dumpLine(pw, uid, category, PROCESS_DATA, processStats.keyAt(ipr), userMillis,
+                            systemMillis, foregroundMillis, starts, numAnrs, numCrashes);
                 }
             }
 
-            Map<String, ? extends BatteryStats.Uid.Pkg> packageStats = u.getPackageStats();
-            if (packageStats.size() > 0) {
-                for (Map.Entry<String, ? extends BatteryStats.Uid.Pkg> ent
-                        : packageStats.entrySet()) {
-              
-                    Uid.Pkg ps = ent.getValue();
-                    int wakeups = ps.getWakeups(which);
-                    Map<String, ? extends  Uid.Pkg.Serv> serviceStats = ps.getServiceStats();
-                    for (Map.Entry<String, ? extends BatteryStats.Uid.Pkg.Serv> sent
-                            : serviceStats.entrySet()) {
-                        BatteryStats.Uid.Pkg.Serv ss = sent.getValue();
-                        long startTime = ss.getStartTime(batteryUptime, which);
-                        int starts = ss.getStarts(which);
-                        int launches = ss.getLaunches(which);
-                        if (startTime != 0 || starts != 0 || launches != 0) {
-                            dumpLine(pw, uid, category, APK_DATA, 
-                                    wakeups, // wakeup alarms
-                                    ent.getKey(), // Apk
-                                    sent.getKey(), // service
-                                    startTime / 1000, // time spent started, in ms
-                                    starts,
-                                    launches);
-                        }
+            final ArrayMap<String, ? extends BatteryStats.Uid.Pkg> packageStats
+                    = u.getPackageStats();
+            for (int ipkg=packageStats.size()-1; ipkg>=0; ipkg--) {
+                final Uid.Pkg ps = packageStats.valueAt(ipkg);
+                int wakeups = 0;
+                final ArrayMap<String, ? extends Counter> alarms = ps.getWakeupAlarmStats();
+                for (int iwa=alarms.size()-1; iwa>=0; iwa--) {
+                    wakeups += alarms.valueAt(iwa).getCountLocked(which);
+                }
+                final ArrayMap<String, ? extends  Uid.Pkg.Serv> serviceStats = ps.getServiceStats();
+                for (int isvc=serviceStats.size()-1; isvc>=0; isvc--) {
+                    final BatteryStats.Uid.Pkg.Serv ss = serviceStats.valueAt(isvc);
+                    final long startTime = ss.getStartTime(batteryUptime, which);
+                    final int starts = ss.getStarts(which);
+                    final int launches = ss.getLaunches(which);
+                    if (startTime != 0 || starts != 0 || launches != 0) {
+                        dumpLine(pw, uid, category, APK_DATA,
+                                wakeups, // wakeup alarms
+                                packageStats.keyAt(ipkg), // Apk
+                                serviceStats.keyAt(isvc), // service
+                                startTime / 1000, // time spent started, in ms
+                                starts,
+                                launches);
                     }
                 }
             }
@@ -2824,9 +2856,9 @@
         final long batteryTimeRemaining = computeBatteryTimeRemaining(rawRealtime);
         final long chargeTimeRemaining = computeChargeTimeRemaining(rawRealtime);
 
-        StringBuilder sb = new StringBuilder(128);
+        final StringBuilder sb = new StringBuilder(128);
         
-        SparseArray<? extends Uid> uidStats = getUidStats();
+        final SparseArray<? extends Uid> uidStats = getUidStats();
         final int NU = uidStats.size();
 
         sb.setLength(0);
@@ -2879,6 +2911,7 @@
         final long interactiveTime = getInteractiveTime(rawRealtime, which);
         final long powerSaveModeEnabledTime = getPowerSaveModeEnabledTime(rawRealtime, which);
         final long deviceIdleModeEnabledTime = getDeviceIdleModeEnabledTime(rawRealtime, which);
+        final long deviceIdlingTime = getDeviceIdlingTime(rawRealtime, which);
         final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
         final long wifiRunningTime = getGlobalWifiRunningTime(rawRealtime, which);
         final long wifiOnTime = getWifiOnTime(rawRealtime, which);
@@ -2923,10 +2956,21 @@
                     sb.append(")");
             pw.println(sb.toString());
         }
-        if (deviceIdleModeEnabledTime != 0) {
+        if (deviceIdlingTime != 0) {
             sb.setLength(0);
             sb.append(prefix);
                     sb.append("  Device idling: ");
+                    formatTimeMs(sb, deviceIdlingTime / 1000);
+                    sb.append("(");
+                    sb.append(formatRatioLocked(deviceIdlingTime, whichBatteryRealtime));
+                    sb.append(") "); sb.append(getDeviceIdlingCount(which));
+                    sb.append("x");
+            pw.println(sb.toString());
+        }
+        if (deviceIdleModeEnabledTime != 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+                    sb.append("  Idle mode time: ");
                     formatTimeMs(sb, deviceIdleModeEnabledTime / 1000);
                     sb.append("(");
                     sb.append(formatRatioLocked(deviceIdleModeEnabledTime, whichBatteryRealtime));
@@ -2941,7 +2985,7 @@
                     sb.append("("); sb.append(formatRatioLocked(phoneOnTime, whichBatteryRealtime));
                     sb.append(") "); sb.append(getPhoneOnCount(which)); sb.append("x");
         }
-        int connChanges = getNumConnectivityChange(which);
+        final int connChanges = getNumConnectivityChange(which);
         if (connChanges != 0) {
             pw.print(prefix);
             pw.print("  Connectivity changes: "); pw.println(connChanges);
@@ -2951,50 +2995,48 @@
         long fullWakeLockTimeTotalMicros = 0;
         long partialWakeLockTimeTotalMicros = 0;
 
-        final ArrayList<TimerEntry> timers = new ArrayList<TimerEntry>();
+        final ArrayList<TimerEntry> timers = new ArrayList<>();
 
         for (int iu = 0; iu < NU; iu++) {
-            Uid u = uidStats.valueAt(iu);
+            final Uid u = uidStats.valueAt(iu);
 
-            Map<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats();
-            if (wakelocks.size() > 0) {
-                for (Map.Entry<String, ? extends BatteryStats.Uid.Wakelock> ent 
-                        : wakelocks.entrySet()) {
-                    Uid.Wakelock wl = ent.getValue();
-                    
-                    Timer fullWakeTimer = wl.getWakeTime(WAKE_TYPE_FULL);
-                    if (fullWakeTimer != null) {
-                        fullWakeLockTimeTotalMicros += fullWakeTimer.getTotalTimeLocked(
-                                rawRealtime, which);
-                    }
+            final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks
+                    = u.getWakelockStats();
+            for (int iw=wakelocks.size()-1; iw>=0; iw--) {
+                final Uid.Wakelock wl = wakelocks.valueAt(iw);
 
-                    Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
-                    if (partialWakeTimer != null) {
-                        long totalTimeMicros = partialWakeTimer.getTotalTimeLocked(
-                                rawRealtime, which);
-                        if (totalTimeMicros > 0) {
-                            if (reqUid < 0) {
-                                // Only show the ordered list of all wake
-                                // locks if the caller is not asking for data
-                                // about a specific uid.
-                                timers.add(new TimerEntry(ent.getKey(), u.getUid(),
-                                        partialWakeTimer, totalTimeMicros));
-                            }
-                            partialWakeLockTimeTotalMicros += totalTimeMicros;
+                final Timer fullWakeTimer = wl.getWakeTime(WAKE_TYPE_FULL);
+                if (fullWakeTimer != null) {
+                    fullWakeLockTimeTotalMicros += fullWakeTimer.getTotalTimeLocked(
+                            rawRealtime, which);
+                }
+
+                final Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
+                if (partialWakeTimer != null) {
+                    final long totalTimeMicros = partialWakeTimer.getTotalTimeLocked(
+                            rawRealtime, which);
+                    if (totalTimeMicros > 0) {
+                        if (reqUid < 0) {
+                            // Only show the ordered list of all wake
+                            // locks if the caller is not asking for data
+                            // about a specific uid.
+                            timers.add(new TimerEntry(wakelocks.keyAt(iw), u.getUid(),
+                                    partialWakeTimer, totalTimeMicros));
                         }
+                        partialWakeLockTimeTotalMicros += totalTimeMicros;
                     }
                 }
             }
         }
         
-        long mobileRxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
-        long mobileTxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
-        long wifiRxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
-        long wifiTxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
-        long mobileRxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
-        long mobileTxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
-        long wifiRxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
-        long wifiTxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
+        final long mobileRxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
+        final long mobileTxTotalBytes = getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
+        final long wifiRxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
+        final long wifiTxTotalBytes = getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
+        final long mobileRxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
+        final long mobileTxTotalPackets = getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
+        final long wifiRxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
+        final long wifiTxTotalPackets = getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
 
         if (fullWakeLockTimeTotalMicros != 0) {
             sb.setLength(0);
@@ -3191,9 +3233,9 @@
         if (!didOne) sb.append(" (no activity)");
         pw.println(sb.toString());
 
-        final long wifiIdleTimeMs = getBluetoothControllerActivity(CONTROLLER_IDLE_TIME, which);
-        final long wifiRxTimeMs = getBluetoothControllerActivity(CONTROLLER_RX_TIME, which);
-        final long wifiTxTimeMs = getBluetoothControllerActivity(CONTROLLER_TX_TIME, which);
+        final long wifiIdleTimeMs = getWifiControllerActivity(CONTROLLER_IDLE_TIME, which);
+        final long wifiRxTimeMs = getWifiControllerActivity(CONTROLLER_RX_TIME, which);
+        final long wifiTxTimeMs = getWifiControllerActivity(CONTROLLER_TX_TIME, which);
         final long wifiTotalTimeMs = wifiIdleTimeMs + wifiRxTimeMs + wifiTxTimeMs;
 
         sb.setLength(0);
@@ -3316,7 +3358,7 @@
             pw.println();
         }
 
-        BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
+        final BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
         helper.create(this);
         helper.refreshStats(which, UserHandle.USER_ALL);
         List<BatterySipper> sippers = helper.getUsageList();
@@ -3331,7 +3373,7 @@
                     }
                     pw.println();
             for (int i=0; i<sippers.size(); i++) {
-                BatterySipper bs = sippers.get(i);
+                final BatterySipper bs = sippers.get(i);
                 switch (bs.drainType) {
                     case IDLE:
                         pw.print(prefix); pw.print("    Idle: "); printmAh(pw, bs.value);
@@ -3388,7 +3430,7 @@
             pw.print(prefix); pw.println("  Per-app mobile ms per packet:");
             long totalTime = 0;
             for (int i=0; i<sippers.size(); i++) {
-                BatterySipper bs = sippers.get(i);
+                final BatterySipper bs = sippers.get(i);
                 sb.setLength(0);
                 sb.append(prefix); sb.append("    Uid ");
                 UserHandle.formatUid(sb, bs.uidObj.getUid());
@@ -3425,12 +3467,14 @@
         };
 
         if (reqUid < 0) {
-            Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
+            final Map<String, ? extends BatteryStats.Timer> kernelWakelocks
+                    = getKernelWakelockStats();
             if (kernelWakelocks.size() > 0) {
-                final ArrayList<TimerEntry> ktimers = new ArrayList<TimerEntry>();
-                for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
-                    BatteryStats.Timer timer = ent.getValue();
-                    long totalTimeMillis = computeWakeLock(timer, rawRealtime, which);
+                final ArrayList<TimerEntry> ktimers = new ArrayList<>();
+                for (Map.Entry<String, ? extends BatteryStats.Timer> ent
+                        : kernelWakelocks.entrySet()) {
+                    final BatteryStats.Timer timer = ent.getValue();
+                    final long totalTimeMillis = computeWakeLock(timer, rawRealtime, which);
                     if (totalTimeMillis > 0) {
                         ktimers.add(new TimerEntry(ent.getKey(), 0, timer, totalTimeMillis));
                     }
@@ -3439,7 +3483,7 @@
                     Collections.sort(ktimers, timerComparator);
                     pw.print(prefix); pw.println("  All kernel wake locks:");
                     for (int i=0; i<ktimers.size(); i++) {
-                        TimerEntry timer = ktimers.get(i);
+                        final TimerEntry timer = ktimers.get(i);
                         String linePrefix = ": ";
                         sb.setLength(0);
                         sb.append(prefix);
@@ -3475,12 +3519,12 @@
                 pw.println();
             }
 
-            Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
+            final Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
             if (wakeupReasons.size() > 0) {
                 pw.print(prefix); pw.println("  All wakeup reasons:");
-                final ArrayList<TimerEntry> reasons = new ArrayList<TimerEntry>();
+                final ArrayList<TimerEntry> reasons = new ArrayList<>();
                 for (Map.Entry<String, ? extends Timer> ent : wakeupReasons.entrySet()) {
-                    Timer timer = ent.getValue();
+                    final Timer timer = ent.getValue();
                     reasons.add(new TimerEntry(ent.getKey(), 0, timer,
                             timer.getCountLocked(which)));
                 }
@@ -3506,7 +3550,7 @@
                 continue;
             }
             
-            Uid u = uidStats.valueAt(iu);
+            final Uid u = uidStats.valueAt(iu);
 
             pw.print(prefix);
             pw.print("  ");
@@ -3514,20 +3558,20 @@
             pw.println(":");
             boolean uidActivity = false;
 
-            long mobileRxBytes = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
-            long mobileTxBytes = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
-            long wifiRxBytes = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
-            long wifiTxBytes = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
-            long mobileRxPackets = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
-            long mobileTxPackets = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
-            long uidMobileActiveTime = u.getMobileRadioActiveTime(which);
-            int uidMobileActiveCount = u.getMobileRadioActiveCount(which);
-            long wifiRxPackets = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
-            long wifiTxPackets = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
-            long fullWifiLockOnTime = u.getFullWifiLockTime(rawRealtime, which);
-            long wifiScanTime = u.getWifiScanTime(rawRealtime, which);
-            int wifiScanCount = u.getWifiScanCount(which);
-            long uidWifiRunningTime = u.getWifiRunningTime(rawRealtime, which);
+            final long mobileRxBytes = u.getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which);
+            final long mobileTxBytes = u.getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which);
+            final long wifiRxBytes = u.getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which);
+            final long wifiTxBytes = u.getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which);
+            final long mobileRxPackets = u.getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which);
+            final long mobileTxPackets = u.getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which);
+            final long uidMobileActiveTime = u.getMobileRadioActiveTime(which);
+            final int uidMobileActiveCount = u.getMobileRadioActiveCount(which);
+            final long wifiRxPackets = u.getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which);
+            final long wifiTxPackets = u.getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which);
+            final long fullWifiLockOnTime = u.getFullWifiLockTime(rawRealtime, which);
+            final long wifiScanTime = u.getWifiScanTime(rawRealtime, which);
+            final int wifiScanCount = u.getWifiScanCount(which);
+            final long uidWifiRunningTime = u.getWifiRunningTime(rawRealtime, which);
 
             if (mobileRxBytes > 0 || mobileTxBytes > 0
                     || mobileRxPackets > 0 || mobileTxPackets > 0) {
@@ -3585,7 +3629,7 @@
             if (u.hasUserActivity()) {
                 boolean hasData = false;
                 for (int i=0; i<Uid.NUM_USER_ACTIVITY_TYPES; i++) {
-                    int val = u.getUserActivityCount(i, which);
+                    final int val = u.getUserActivityCount(i, which);
                     if (val != 0) {
                         if (!hasData) {
                             sb.setLength(0);
@@ -3604,125 +3648,121 @@
                 }
             }
 
-            Map<String, ? extends BatteryStats.Uid.Wakelock> wakelocks = u.getWakelockStats();
-            if (wakelocks.size() > 0) {
-                long totalFull = 0, totalPartial = 0, totalWindow = 0;
-                int count = 0;
-                for (Map.Entry<String, ? extends Uid.Wakelock> ent : wakelocks.entrySet()) {
-                    Uid.Wakelock wl = ent.getValue();
-                    String linePrefix = ": ";
-                    sb.setLength(0);
-                    sb.append(prefix);
-                    sb.append("    Wake lock ");
-                    sb.append(ent.getKey());
-                    linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_FULL), rawRealtime,
-                            "full", which, linePrefix);
-                    linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_PARTIAL), rawRealtime,
-                            "partial", which, linePrefix);
-                    linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_WINDOW), rawRealtime,
-                            "window", which, linePrefix);
-                    if (true || !linePrefix.equals(": ")) {
-                        sb.append(" realtime");
-                        // Only print out wake locks that were held
-                        pw.println(sb.toString());
-                        uidActivity = true;
-                        count++;
-                    }
-                    totalFull += computeWakeLock(wl.getWakeTime(WAKE_TYPE_FULL),
-                            rawRealtime, which);
-                    totalPartial += computeWakeLock(wl.getWakeTime(WAKE_TYPE_PARTIAL),
-                            rawRealtime, which);
-                    totalWindow += computeWakeLock(wl.getWakeTime(WAKE_TYPE_WINDOW),
-                            rawRealtime, which);
-                }
-                if (count > 1) {
-                    if (totalFull != 0 || totalPartial != 0 || totalWindow != 0) {
-                        sb.setLength(0);
-                        sb.append(prefix);
-                        sb.append("    TOTAL wake: ");
-                        boolean needComma = false;
-                        if (totalFull != 0) {
-                            needComma = true;
-                            formatTimeMs(sb, totalFull);
-                            sb.append("full");
-                        }
-                        if (totalPartial != 0) {
-                            if (needComma) {
-                                sb.append(", ");
-                            }
-                            needComma = true;
-                            formatTimeMs(sb, totalPartial);
-                            sb.append("partial");
-                        }
-                        if (totalWindow != 0) {
-                            if (needComma) {
-                                sb.append(", ");
-                            }
-                            needComma = true;
-                            formatTimeMs(sb, totalWindow);
-                            sb.append("window");
-                        }
-                        sb.append(" realtime");
-                        pw.println(sb.toString());
-                    }
-                }
-            }
-
-            Map<String, ? extends Timer> syncs = u.getSyncStats();
-            if (syncs.size() > 0) {
-                for (Map.Entry<String, ? extends Timer> ent : syncs.entrySet()) {
-                    Timer timer = ent.getValue();
-                    // Convert from microseconds to milliseconds with rounding
-                    long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
-                    int count = timer.getCountLocked(which);
-                    sb.setLength(0);
-                    sb.append(prefix);
-                    sb.append("    Sync ");
-                    sb.append(ent.getKey());
-                    sb.append(": ");
-                    if (totalTime != 0) {
-                        formatTimeMs(sb, totalTime);
-                        sb.append("realtime (");
-                        sb.append(count);
-                        sb.append(" times)");
-                    } else {
-                        sb.append("(not used)");
-                    }
+            final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks
+                    = u.getWakelockStats();
+            long totalFullWakelock = 0, totalPartialWakelock = 0, totalWindowWakelock = 0;
+            int countWakelock = 0;
+            for (int iw=wakelocks.size()-1; iw>=0; iw--) {
+                final Uid.Wakelock wl = wakelocks.valueAt(iw);
+                String linePrefix = ": ";
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    Wake lock ");
+                sb.append(wakelocks.keyAt(iw));
+                linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_FULL), rawRealtime,
+                        "full", which, linePrefix);
+                linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_PARTIAL), rawRealtime,
+                        "partial", which, linePrefix);
+                linePrefix = printWakeLock(sb, wl.getWakeTime(WAKE_TYPE_WINDOW), rawRealtime,
+                        "window", which, linePrefix);
+                if (true || !linePrefix.equals(": ")) {
+                    sb.append(" realtime");
+                    // Only print out wake locks that were held
                     pw.println(sb.toString());
                     uidActivity = true;
+                    countWakelock++;
                 }
+                totalFullWakelock += computeWakeLock(wl.getWakeTime(WAKE_TYPE_FULL),
+                        rawRealtime, which);
+                totalPartialWakelock += computeWakeLock(wl.getWakeTime(WAKE_TYPE_PARTIAL),
+                        rawRealtime, which);
+                totalWindowWakelock += computeWakeLock(wl.getWakeTime(WAKE_TYPE_WINDOW),
+                        rawRealtime, which);
             }
-
-            Map<String, ? extends Timer> jobs = u.getJobStats();
-            if (jobs.size() > 0) {
-                for (Map.Entry<String, ? extends Timer> ent : jobs.entrySet()) {
-                    Timer timer = ent.getValue();
-                    // Convert from microseconds to milliseconds with rounding
-                    long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
-                    int count = timer.getCountLocked(which);
+            if (countWakelock > 1) {
+                if (totalFullWakelock != 0 || totalPartialWakelock != 0
+                        || totalWindowWakelock != 0) {
                     sb.setLength(0);
                     sb.append(prefix);
-                    sb.append("    Job ");
-                    sb.append(ent.getKey());
-                    sb.append(": ");
-                    if (totalTime != 0) {
-                        formatTimeMs(sb, totalTime);
-                        sb.append("realtime (");
-                        sb.append(count);
-                        sb.append(" times)");
-                    } else {
-                        sb.append("(not used)");
+                    sb.append("    TOTAL wake: ");
+                    boolean needComma = false;
+                    if (totalFullWakelock != 0) {
+                        needComma = true;
+                        formatTimeMs(sb, totalFullWakelock);
+                        sb.append("full");
                     }
+                    if (totalPartialWakelock != 0) {
+                        if (needComma) {
+                            sb.append(", ");
+                        }
+                        needComma = true;
+                        formatTimeMs(sb, totalPartialWakelock);
+                        sb.append("partial");
+                    }
+                    if (totalWindowWakelock != 0) {
+                        if (needComma) {
+                            sb.append(", ");
+                        }
+                        needComma = true;
+                        formatTimeMs(sb, totalWindowWakelock);
+                        sb.append("window");
+                    }
+                    sb.append(" realtime");
                     pw.println(sb.toString());
-                    uidActivity = true;
                 }
             }
 
-            SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
-            int NSE = sensors.size();
+            final ArrayMap<String, ? extends Timer> syncs = u.getSyncStats();
+            for (int isy=syncs.size()-1; isy>=0; isy--) {
+                final Timer timer = syncs.valueAt(isy);
+                // Convert from microseconds to milliseconds with rounding
+                final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                final int count = timer.getCountLocked(which);
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    Sync ");
+                sb.append(syncs.keyAt(isy));
+                sb.append(": ");
+                if (totalTime != 0) {
+                    formatTimeMs(sb, totalTime);
+                    sb.append("realtime (");
+                    sb.append(count);
+                    sb.append(" times)");
+                } else {
+                    sb.append("(not used)");
+                }
+                pw.println(sb.toString());
+                uidActivity = true;
+            }
+
+            final ArrayMap<String, ? extends Timer> jobs = u.getJobStats();
+            for (int ij=jobs.size()-1; ij>=0; ij--) {
+                final Timer timer = jobs.valueAt(ij);
+                // Convert from microseconds to milliseconds with rounding
+                final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                final int count = timer.getCountLocked(which);
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    Job ");
+                sb.append(jobs.keyAt(ij));
+                sb.append(": ");
+                if (totalTime != 0) {
+                    formatTimeMs(sb, totalTime);
+                    sb.append("realtime (");
+                    sb.append(count);
+                    sb.append(" times)");
+                } else {
+                    sb.append("(not used)");
+                }
+                pw.println(sb.toString());
+                uidActivity = true;
+            }
+
+            final SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
+            final int NSE = sensors.size();
             for (int ise=0; ise<NSE; ise++) {
-                Uid.Sensor se = sensors.valueAt(ise);
-                int sensorNumber = sensors.keyAt(ise);
+                final Uid.Sensor se = sensors.valueAt(ise);
+                final int sensorNumber = sensors.keyAt(ise);
                 sb.setLength(0);
                 sb.append(prefix);
                 sb.append("    Sensor ");
@@ -3734,12 +3774,12 @@
                 }
                 sb.append(": ");
 
-                Timer timer = se.getSensorTime();
+                final Timer timer = se.getSensorTime();
                 if (timer != null) {
                     // Convert from microseconds to milliseconds with rounding
-                    long totalTime = (timer.getTotalTimeLocked(
+                    final long totalTime = (timer.getTotalTimeLocked(
                             rawRealtime, which) + 500) / 1000;
-                    int count = timer.getCountLocked(which);
+                    final int count = timer.getCountLocked(which);
                     //timer.logState();
                     if (totalTime != 0) {
                         formatTimeMs(sb, totalTime);
@@ -3757,12 +3797,12 @@
                 uidActivity = true;
             }
 
-            Timer vibTimer = u.getVibratorOnTimer();
+            final Timer vibTimer = u.getVibratorOnTimer();
             if (vibTimer != null) {
                 // Convert from microseconds to milliseconds with rounding
-                long totalTime = (vibTimer.getTotalTimeLocked(
+                final long totalTime = (vibTimer.getTotalTimeLocked(
                         rawRealtime, which) + 500) / 1000;
-                int count = vibTimer.getCountLocked(which);
+                final int count = vibTimer.getCountLocked(which);
                 //timer.logState();
                 if (totalTime != 0) {
                     sb.setLength(0);
@@ -3777,11 +3817,12 @@
                 }
             }
 
-            Timer fgTimer = u.getForegroundActivityTimer();
+            final Timer fgTimer = u.getForegroundActivityTimer();
             if (fgTimer != null) {
                 // Convert from microseconds to milliseconds with rounding
-                long totalTime = (fgTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
-                int count = fgTimer.getCountLocked(which);
+                final long totalTime = (fgTimer.getTotalTimeLocked(rawRealtime, which) + 500)
+                        / 1000;
+                final int count = fgTimer.getCountLocked(which);
                 if (totalTime != 0) {
                     sb.setLength(0);
                     sb.append(prefix);
@@ -3811,126 +3852,122 @@
                 }
             }
 
-            Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats();
-            if (processStats.size() > 0) {
-                for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent
-                    : processStats.entrySet()) {
-                    Uid.Proc ps = ent.getValue();
-                    long userTime;
-                    long systemTime;
-                    long foregroundTime;
-                    int starts;
-                    int numExcessive;
+            final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats
+                    = u.getProcessStats();
+            for (int ipr=processStats.size()-1; ipr>=0; ipr--) {
+                final Uid.Proc ps = processStats.valueAt(ipr);
+                long userTime;
+                long systemTime;
+                long foregroundTime;
+                int starts;
+                int numExcessive;
 
-                    userTime = ps.getUserTime(which);
-                    systemTime = ps.getSystemTime(which);
-                    foregroundTime = ps.getForegroundTime(which);
-                    starts = ps.getStarts(which);
-                    final int numCrashes = ps.getNumCrashes(which);
-                    final int numAnrs = ps.getNumAnrs(which);
-                    numExcessive = which == STATS_SINCE_CHARGED
-                            ? ps.countExcessivePowers() : 0;
+                userTime = ps.getUserTime(which);
+                systemTime = ps.getSystemTime(which);
+                foregroundTime = ps.getForegroundTime(which);
+                starts = ps.getStarts(which);
+                final int numCrashes = ps.getNumCrashes(which);
+                final int numAnrs = ps.getNumAnrs(which);
+                numExcessive = which == STATS_SINCE_CHARGED
+                        ? ps.countExcessivePowers() : 0;
 
-                    if (userTime != 0 || systemTime != 0 || foregroundTime != 0 || starts != 0
-                            || numExcessive != 0 || numCrashes != 0 || numAnrs != 0) {
-                        sb.setLength(0);
-                        sb.append(prefix); sb.append("    Proc ");
-                                sb.append(ent.getKey()); sb.append(":\n");
-                        sb.append(prefix); sb.append("      CPU: ");
-                                formatTimeMs(sb, userTime); sb.append("usr + ");
-                                formatTimeMs(sb, systemTime); sb.append("krn ; ");
-                                formatTimeMs(sb, foregroundTime); sb.append("fg");
-                        if (starts != 0 || numCrashes != 0 || numAnrs != 0) {
-                            sb.append("\n"); sb.append(prefix); sb.append("      ");
-                            boolean hasOne = false;
-                            if (starts != 0) {
-                                hasOne = true;
-                                sb.append(starts); sb.append(" starts");
-                            }
-                            if (numCrashes != 0) {
-                                if (hasOne) {
-                                    sb.append(", ");
-                                }
-                                hasOne = true;
-                                sb.append(numCrashes); sb.append(" crashes");
-                            }
-                            if (numAnrs != 0) {
-                                if (hasOne) {
-                                    sb.append(", ");
-                                }
-                                sb.append(numAnrs); sb.append(" anrs");
-                            }
+                if (userTime != 0 || systemTime != 0 || foregroundTime != 0 || starts != 0
+                        || numExcessive != 0 || numCrashes != 0 || numAnrs != 0) {
+                    sb.setLength(0);
+                    sb.append(prefix); sb.append("    Proc ");
+                            sb.append(processStats.keyAt(ipr)); sb.append(":\n");
+                    sb.append(prefix); sb.append("      CPU: ");
+                            formatTimeMs(sb, userTime); sb.append("usr + ");
+                            formatTimeMs(sb, systemTime); sb.append("krn ; ");
+                            formatTimeMs(sb, foregroundTime); sb.append("fg");
+                    if (starts != 0 || numCrashes != 0 || numAnrs != 0) {
+                        sb.append("\n"); sb.append(prefix); sb.append("      ");
+                        boolean hasOne = false;
+                        if (starts != 0) {
+                            hasOne = true;
+                            sb.append(starts); sb.append(" starts");
                         }
-                        pw.println(sb.toString());
-                        for (int e=0; e<numExcessive; e++) {
-                            Uid.Proc.ExcessivePower ew = ps.getExcessivePower(e);
-                            if (ew != null) {
-                                pw.print(prefix); pw.print("      * Killed for ");
-                                        if (ew.type == Uid.Proc.ExcessivePower.TYPE_WAKE) {
-                                            pw.print("wake lock");
-                                        } else if (ew.type == Uid.Proc.ExcessivePower.TYPE_CPU) {
-                                            pw.print("cpu");
-                                        } else {
-                                            pw.print("unknown");
-                                        }
-                                        pw.print(" use: ");
-                                        TimeUtils.formatDuration(ew.usedTime, pw);
-                                        pw.print(" over ");
-                                        TimeUtils.formatDuration(ew.overTime, pw);
-                                        if (ew.overTime != 0) {
-                                            pw.print(" (");
-                                            pw.print((ew.usedTime*100)/ew.overTime);
-                                            pw.println("%)");
-                                        }
+                        if (numCrashes != 0) {
+                            if (hasOne) {
+                                sb.append(", ");
                             }
+                            hasOne = true;
+                            sb.append(numCrashes); sb.append(" crashes");
                         }
-                        uidActivity = true;
-                    }
-                }
-            }
-
-            Map<String, ? extends BatteryStats.Uid.Pkg> packageStats = u.getPackageStats();
-            if (packageStats.size() > 0) {
-                for (Map.Entry<String, ? extends BatteryStats.Uid.Pkg> ent
-                    : packageStats.entrySet()) {
-                    pw.print(prefix); pw.print("    Apk "); pw.print(ent.getKey()); pw.println(":");
-                    boolean apkActivity = false;
-                    Uid.Pkg ps = ent.getValue();
-                    int wakeups = ps.getWakeups(which);
-                    if (wakeups != 0) {
-                        pw.print(prefix); pw.print("      ");
-                                pw.print(wakeups); pw.println(" wakeup alarms");
-                        apkActivity = true;
-                    }
-                    Map<String, ? extends  Uid.Pkg.Serv> serviceStats = ps.getServiceStats();
-                    if (serviceStats.size() > 0) {
-                        for (Map.Entry<String, ? extends BatteryStats.Uid.Pkg.Serv> sent
-                                : serviceStats.entrySet()) {
-                            BatteryStats.Uid.Pkg.Serv ss = sent.getValue();
-                            long startTime = ss.getStartTime(batteryUptime, which);
-                            int starts = ss.getStarts(which);
-                            int launches = ss.getLaunches(which);
-                            if (startTime != 0 || starts != 0 || launches != 0) {
-                                sb.setLength(0);
-                                sb.append(prefix); sb.append("      Service ");
-                                        sb.append(sent.getKey()); sb.append(":\n");
-                                sb.append(prefix); sb.append("        Created for: ");
-                                        formatTimeMs(sb, startTime / 1000);
-                                        sb.append("uptime\n");
-                                sb.append(prefix); sb.append("        Starts: ");
-                                        sb.append(starts);
-                                        sb.append(", launches: "); sb.append(launches);
-                                pw.println(sb.toString());
-                                apkActivity = true;
+                        if (numAnrs != 0) {
+                            if (hasOne) {
+                                sb.append(", ");
                             }
+                            sb.append(numAnrs); sb.append(" anrs");
                         }
                     }
-                    if (!apkActivity) {
-                        pw.print(prefix); pw.println("      (nothing executed)");
+                    pw.println(sb.toString());
+                    for (int e=0; e<numExcessive; e++) {
+                        Uid.Proc.ExcessivePower ew = ps.getExcessivePower(e);
+                        if (ew != null) {
+                            pw.print(prefix); pw.print("      * Killed for ");
+                                    if (ew.type == Uid.Proc.ExcessivePower.TYPE_WAKE) {
+                                        pw.print("wake lock");
+                                    } else if (ew.type == Uid.Proc.ExcessivePower.TYPE_CPU) {
+                                        pw.print("cpu");
+                                    } else {
+                                        pw.print("unknown");
+                                    }
+                                    pw.print(" use: ");
+                                    TimeUtils.formatDuration(ew.usedTime, pw);
+                                    pw.print(" over ");
+                                    TimeUtils.formatDuration(ew.overTime, pw);
+                                    if (ew.overTime != 0) {
+                                        pw.print(" (");
+                                        pw.print((ew.usedTime*100)/ew.overTime);
+                                        pw.println("%)");
+                                    }
+                        }
                     }
                     uidActivity = true;
                 }
             }
+
+            final ArrayMap<String, ? extends BatteryStats.Uid.Pkg> packageStats
+                    = u.getPackageStats();
+            for (int ipkg=packageStats.size()-1; ipkg>=0; ipkg--) {
+                pw.print(prefix); pw.print("    Apk "); pw.print(packageStats.keyAt(ipkg));
+                pw.println(":");
+                boolean apkActivity = false;
+                final Uid.Pkg ps = packageStats.valueAt(ipkg);
+                final ArrayMap<String, ? extends Counter> alarms = ps.getWakeupAlarmStats();
+                for (int iwa=alarms.size()-1; iwa>=0; iwa--) {
+                    pw.print(prefix); pw.print("      Wakeup alarm ");
+                            pw.print(alarms.keyAt(iwa)); pw.print(": ");
+                            pw.print(alarms.valueAt(iwa).getCountLocked(which));
+                            pw.println(" times");
+                    apkActivity = true;
+                }
+                final ArrayMap<String, ? extends  Uid.Pkg.Serv> serviceStats = ps.getServiceStats();
+                for (int isvc=serviceStats.size()-1; isvc>=0; isvc--) {
+                    final BatteryStats.Uid.Pkg.Serv ss = serviceStats.valueAt(isvc);
+                    final long startTime = ss.getStartTime(batteryUptime, which);
+                    final int starts = ss.getStarts(which);
+                    final int launches = ss.getLaunches(which);
+                    if (startTime != 0 || starts != 0 || launches != 0) {
+                        sb.setLength(0);
+                        sb.append(prefix); sb.append("      Service ");
+                                sb.append(serviceStats.keyAt(isvc)); sb.append(":\n");
+                        sb.append(prefix); sb.append("        Created for: ");
+                                formatTimeMs(sb, startTime / 1000);
+                                sb.append("uptime\n");
+                        sb.append(prefix); sb.append("        Starts: ");
+                                sb.append(starts);
+                                sb.append(", launches: "); sb.append(launches);
+                        pw.println(sb.toString());
+                        apkActivity = true;
+                    }
+                }
+                if (!apkActivity) {
+                    pw.print(prefix); pw.println("      (nothing executed)");
+                }
+                uidActivity = true;
+            }
             if (!uidActivity) {
                 pw.print(prefix); pw.println("    (nothing executed)");
             }
@@ -4378,7 +4415,7 @@
         if (!checkin) {
             pw.println(header);
         }
-        String[] lineArgs = new String[4];
+        String[] lineArgs = new String[5];
         for (int i=0; i<count; i++) {
             long duration = steps.getDurationAt(i);
             int level = steps.getLevelAt(i);
@@ -4393,7 +4430,7 @@
                         case Display.STATE_ON: lineArgs[2] = "s+"; break;
                         case Display.STATE_DOZE: lineArgs[2] = "sd"; break;
                         case Display.STATE_DOZE_SUSPEND: lineArgs[2] = "sds"; break;
-                        default: lineArgs[1] = "?"; break;
+                        default: lineArgs[2] = "?"; break;
                     }
                 } else {
                     lineArgs[2] = "";
@@ -4403,6 +4440,11 @@
                 } else {
                     lineArgs[3] = "";
                 }
+                if ((modMode&STEP_LEVEL_MODE_DEVICE_IDLE) == 0) {
+                    lineArgs[4] = (initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0 ? "i+" : "i-";
+                } else {
+                    lineArgs[4] = "";
+                }
                 dumpLine(pw, 0 /* uid */, "i" /* category */, header, (Object[])lineArgs);
             } else {
                 pw.print(prefix);
@@ -4417,7 +4459,7 @@
                         case Display.STATE_ON: pw.print("screen-on"); break;
                         case Display.STATE_DOZE: pw.print("screen-doze"); break;
                         case Display.STATE_DOZE_SUSPEND: pw.print("screen-doze-suspend"); break;
-                        default: lineArgs[1] = "screen-?"; break;
+                        default: pw.print("screen-?"); break;
                     }
                     haveModes = true;
                 }
@@ -4427,6 +4469,12 @@
                             ? "power-save-on" : "power-save-off");
                     haveModes = true;
                 }
+                if ((modMode&STEP_LEVEL_MODE_DEVICE_IDLE) == 0) {
+                    pw.print(haveModes ? ", " : " (");
+                    pw.print((initMode&STEP_LEVEL_MODE_DEVICE_IDLE) != 0
+                            ? "device-idle-on" : "device-idle-off");
+                    haveModes = true;
+                }
                 if (haveModes) {
                     pw.print(")");
                 }
@@ -4436,7 +4484,6 @@
         return true;
     }
 
-    public static final int DUMP_UNPLUGGED_ONLY = 1<<0;
     public static final int DUMP_CHARGED_ONLY = 1<<1;
     public static final int DUMP_DAILY_ONLY = 1<<2;
     public static final int DUMP_HISTORY_ONLY = 1<<3;
@@ -4558,6 +4605,23 @@
         }
     }
 
+    private void dumpDailyPackageChanges(PrintWriter pw, String prefix,
+            ArrayList<PackageChange> changes) {
+        if (changes == null) {
+            return;
+        }
+        pw.print(prefix); pw.println("Package changes:");
+        for (int i=0; i<changes.size(); i++) {
+            PackageChange pc = changes.get(i);
+            if (pc.mUpdate) {
+                pw.print(prefix); pw.print("  Update "); pw.print(pc.mPackageName);
+                pw.print(" vers="); pw.println(pc.mVersionCode);
+            } else {
+                pw.print(prefix); pw.print("  Uninstall "); pw.println(pc.mPackageName);
+            }
+        }
+    }
+
     /**
      * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
      *
@@ -4568,7 +4632,7 @@
         prepareForDumpLocked();
 
         final boolean filtering = (flags
-                & (DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) != 0;
+                & (DUMP_HISTORY_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) != 0;
 
         if ((flags&DUMP_HISTORY_ONLY) != 0 || !filtering) {
             final long historyTotalSize = getHistoryTotalSize();
@@ -4612,7 +4676,7 @@
             }
         }
 
-        if (filtering && (flags&(DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) == 0) {
+        if (filtering && (flags&(DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) == 0) {
             return;
         }
 
@@ -4688,8 +4752,9 @@
             int[] outInt = new int[1];
             LevelStepTracker dsteps = getDailyDischargeLevelStepTracker();
             LevelStepTracker csteps = getDailyChargeLevelStepTracker();
-            if (dsteps.mNumStepDurations > 0 || csteps.mNumStepDurations > 0) {
-                if ((flags&DUMP_DAILY_ONLY) != 0) {
+            ArrayList<PackageChange> pkgc = getDailyPackageChanges();
+            if (dsteps.mNumStepDurations > 0 || csteps.mNumStepDurations > 0 || pkgc != null) {
+                if ((flags&DUMP_DAILY_ONLY) != 0 || !filtering) {
                     if (dumpDurationSteps(pw, "    ", "  Current daily discharge step durations:",
                             dsteps, false)) {
                         dumpDailyLevelStepSummary(pw, "      ", "Discharge", dsteps,
@@ -4700,6 +4765,7 @@
                         dumpDailyLevelStepSummary(pw, "      ", "Charge", csteps,
                                 sb, outInt);
                     }
+                    dumpDailyPackageChanges(pw, "    ", pkgc);
                 } else {
                     pw.println("  Current daily steps:");
                     dumpDailyLevelStepSummary(pw, "    ", "Discharge", dsteps,
@@ -4720,7 +4786,7 @@
                 pw.print(" to ");
                 pw.print(DateFormat.format("yyyy-MM-dd-HH-mm-ss", dit.mEndTime).toString());
                 pw.println(":");
-                if ((flags&DUMP_DAILY_ONLY) != 0) {
+                if ((flags&DUMP_DAILY_ONLY) != 0 || !filtering) {
                     if (dumpDurationSteps(pw, "      ",
                             "    Discharge step durations:", dit.mDischargeSteps, false)) {
                         dumpDailyLevelStepSummary(pw, "        ", "Discharge", dit.mDischargeSteps,
@@ -4731,6 +4797,7 @@
                         dumpDailyLevelStepSummary(pw, "        ", "Charge", dit.mChargeSteps,
                                 sb, outInt);
                     }
+                    dumpDailyPackageChanges(pw, "    ", dit.mPackageChanges);
                 } else {
                     dumpDailyLevelStepSummary(pw, "    ", "Discharge", dit.mDischargeSteps,
                             sb, outInt);
@@ -4748,11 +4815,6 @@
                     (flags&DUMP_DEVICE_WIFI_ONLY) != 0);
             pw.println();
         }
-        if (!filtering || (flags&DUMP_UNPLUGGED_ONLY) != 0) {
-            pw.println("Statistics since last unplugged:");
-            dumpLocked(context, pw, "", STATS_SINCE_UNPLUGGED, reqUid,
-                    (flags&DUMP_DEVICE_WIFI_ONLY) != 0);
-        }
     }
     
     @SuppressWarnings("unused")
@@ -4766,7 +4828,7 @@
         long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
 
         final boolean filtering = (flags &
-                (DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) != 0;
+                (DUMP_HISTORY_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) != 0;
 
         if ((flags&DUMP_INCLUDE_HISTORY) != 0 || (flags&DUMP_HISTORY_ONLY) != 0) {
             if (startIteratingHistoryLocked()) {
@@ -4792,7 +4854,7 @@
             }
         }
 
-        if (filtering && (flags&(DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) == 0) {
+        if (filtering && (flags&(DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) == 0) {
             return;
         }
 
@@ -4842,9 +4904,5 @@
             dumpCheckinLocked(context, pw, STATS_SINCE_CHARGED, -1,
                     (flags&DUMP_DEVICE_WIFI_ONLY) != 0);
         }
-        if (!filtering || (flags&DUMP_UNPLUGGED_ONLY) != 0) {
-            dumpCheckinLocked(context, pw, STATS_SINCE_UNPLUGGED, -1,
-                    (flags&DUMP_DEVICE_WIFI_ONLY) != 0);
-        }
     }
 }
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 975bfc2..2db976e 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -18,16 +18,11 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
-import android.os.storage.IMountService;
+import android.os.storage.StorageManager;
 import android.os.storage.StorageVolume;
-import android.text.TextUtils;
 import android.util.Log;
 
-import com.google.android.collect.Lists;
-
 import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
 
 /**
  * Provides access to environment variables.
@@ -36,11 +31,9 @@
     private static final String TAG = "Environment";
 
     private static final String ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE";
-    private static final String ENV_EMULATED_STORAGE_SOURCE = "EMULATED_STORAGE_SOURCE";
-    private static final String ENV_EMULATED_STORAGE_TARGET = "EMULATED_STORAGE_TARGET";
-    private static final String ENV_MEDIA_STORAGE = "MEDIA_STORAGE";
-    private static final String ENV_SECONDARY_STORAGE = "SECONDARY_STORAGE";
     private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
+    private static final String ENV_ANDROID_DATA = "ANDROID_DATA";
+    private static final String ENV_ANDROID_STORAGE = "ANDROID_STORAGE";
     private static final String ENV_OEM_ROOT = "OEM_ROOT";
     private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT";
 
@@ -57,12 +50,10 @@
     public static final String DIRECTORY_ANDROID = DIR_ANDROID;
 
     private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
+    private static final File DIR_ANDROID_DATA = getDirectory(ENV_ANDROID_DATA, "/data");
+    private static final File DIR_ANDROID_STORAGE = getDirectory(ENV_ANDROID_STORAGE, "/storage");
     private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem");
     private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor");
-    private static final File DIR_MEDIA_STORAGE = getDirectory(ENV_MEDIA_STORAGE, "/data/media");
-
-    private static final String CANONCIAL_EMULATED_STORAGE_TARGET = getCanonicalPathOrNull(
-            ENV_EMULATED_STORAGE_TARGET);
 
     private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
 
@@ -81,73 +72,24 @@
 
     /** {@hide} */
     public static class UserEnvironment {
-        // TODO: generalize further to create package-specific environment
-
-        /** External storage dirs, as visible to vold */
-        private final File[] mExternalDirsForVold;
-        /** External storage dirs, as visible to apps */
-        private final File[] mExternalDirsForApp;
-        /** Primary emulated storage dir for direct access */
-        private final File mEmulatedDirForDirect;
+        private final int mUserId;
 
         public UserEnvironment(int userId) {
-            // See storage config details at http://source.android.com/tech/storage/
-            String rawExternalStorage = System.getenv(ENV_EXTERNAL_STORAGE);
-            String rawEmulatedSource = System.getenv(ENV_EMULATED_STORAGE_SOURCE);
-            String rawEmulatedTarget = System.getenv(ENV_EMULATED_STORAGE_TARGET);
+            mUserId = userId;
+        }
 
-            String rawMediaStorage = System.getenv(ENV_MEDIA_STORAGE);
-            if (TextUtils.isEmpty(rawMediaStorage)) {
-                rawMediaStorage = "/data/media";
+        public File[] getExternalDirs() {
+            final StorageVolume[] volumes = StorageManager.getVolumeList(mUserId);
+            final File[] dirs = new File[volumes.length];
+            for (int i = 0; i < volumes.length; i++) {
+                dirs[i] = volumes[i].getPathFile();
             }
-
-            ArrayList<File> externalForVold = Lists.newArrayList();
-            ArrayList<File> externalForApp = Lists.newArrayList();
-
-            if (!TextUtils.isEmpty(rawEmulatedTarget)) {
-                // Device has emulated storage; external storage paths should have
-                // userId burned into them.
-                final String rawUserId = Integer.toString(userId);
-                final File emulatedSourceBase = new File(rawEmulatedSource);
-                final File emulatedTargetBase = new File(rawEmulatedTarget);
-                final File mediaBase = new File(rawMediaStorage);
-
-                // /storage/emulated/0
-                externalForVold.add(buildPath(emulatedSourceBase, rawUserId));
-                externalForApp.add(buildPath(emulatedTargetBase, rawUserId));
-                // /data/media/0
-                mEmulatedDirForDirect = buildPath(mediaBase, rawUserId);
-
-            } else {
-                // Device has physical external storage; use plain paths.
-                if (TextUtils.isEmpty(rawExternalStorage)) {
-                    Log.w(TAG, "EXTERNAL_STORAGE undefined; falling back to default");
-                    rawExternalStorage = "/storage/sdcard0";
-                }
-
-                // /storage/sdcard0
-                externalForVold.add(new File(rawExternalStorage));
-                externalForApp.add(new File(rawExternalStorage));
-                // /data/media
-                mEmulatedDirForDirect = new File(rawMediaStorage);
-            }
-
-            // Splice in any secondary storage paths, but only for owner
-            final String rawSecondaryStorage = System.getenv(ENV_SECONDARY_STORAGE);
-            if (!TextUtils.isEmpty(rawSecondaryStorage) && userId == UserHandle.USER_OWNER) {
-                for (String secondaryPath : rawSecondaryStorage.split(":")) {
-                    externalForVold.add(new File(secondaryPath));
-                    externalForApp.add(new File(secondaryPath));
-                }
-            }
-
-            mExternalDirsForVold = externalForVold.toArray(new File[externalForVold.size()]);
-            mExternalDirsForApp = externalForApp.toArray(new File[externalForApp.size()]);
+            return dirs;
         }
 
         @Deprecated
         public File getExternalStorageDirectory() {
-            return mExternalDirsForApp[0];
+            return getExternalDirs()[0];
         }
 
         @Deprecated
@@ -155,60 +97,36 @@
             return buildExternalStoragePublicDirs(type)[0];
         }
 
-        public File[] getExternalDirsForVold() {
-            return mExternalDirsForVold;
-        }
-
-        public File[] getExternalDirsForApp() {
-            return mExternalDirsForApp;
-        }
-
-        public File getMediaDir() {
-            return mEmulatedDirForDirect;
-        }
-
         public File[] buildExternalStoragePublicDirs(String type) {
-            return buildPaths(mExternalDirsForApp, type);
+            return buildPaths(getExternalDirs(), type);
         }
 
         public File[] buildExternalStorageAndroidDataDirs() {
-            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA);
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA);
         }
 
         public File[] buildExternalStorageAndroidObbDirs() {
-            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_OBB);
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_OBB);
         }
 
         public File[] buildExternalStorageAppDataDirs(String packageName) {
-            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA, packageName);
-        }
-
-        public File[] buildExternalStorageAppDataDirsForVold(String packageName) {
-            return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_DATA, packageName);
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA, packageName);
         }
 
         public File[] buildExternalStorageAppMediaDirs(String packageName) {
-            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_MEDIA, packageName);
-        }
-
-        public File[] buildExternalStorageAppMediaDirsForVold(String packageName) {
-            return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_MEDIA, packageName);
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_MEDIA, packageName);
         }
 
         public File[] buildExternalStorageAppObbDirs(String packageName) {
-            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_OBB, packageName);
-        }
-
-        public File[] buildExternalStorageAppObbDirsForVold(String packageName) {
-            return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_OBB, packageName);
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_OBB, packageName);
         }
 
         public File[] buildExternalStorageAppFilesDirs(String packageName) {
-            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA, packageName, DIR_FILES);
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA, packageName, DIR_FILES);
         }
 
         public File[] buildExternalStorageAppCacheDirs(String packageName) {
-            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA, packageName, DIR_CACHE);
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA, packageName, DIR_CACHE);
         }
     }
 
@@ -220,6 +138,11 @@
         return DIR_ANDROID_ROOT;
     }
 
+    /** {@hide} */
+    public static File getStorageDirectory() {
+        return DIR_ANDROID_STORAGE;
+    }
+
     /**
      * Return root directory of the "oem" partition holding OEM customizations,
      * if any. If present, the partition is mounted read-only.
@@ -270,17 +193,6 @@
     }
 
     /**
-     * Return directory used for internal media storage, which is protected by
-     * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE}.
-     *
-     * @hide
-     */
-    public static File getMediaStorageDirectory() {
-        throwIfUserRequired();
-        return sCurrentUser.getMediaDir();
-    }
-
-    /**
      * Return the system directory for a user. This is for use by system services to store
      * files relating to the user. This directory will be automatically deleted when the user
      * is removed.
@@ -389,7 +301,7 @@
      */
     public static File getExternalStorageDirectory() {
         throwIfUserRequired();
-        return sCurrentUser.getExternalDirsForApp()[0];
+        return sCurrentUser.getExternalDirs()[0];
     }
 
     /** {@hide} */
@@ -402,18 +314,6 @@
         return buildPath(getLegacyExternalStorageDirectory(), DIR_ANDROID, DIR_OBB);
     }
 
-    /** {@hide} */
-    public static File getEmulatedStorageSource(int userId) {
-        // /mnt/shell/emulated/0
-        return new File(System.getenv(ENV_EMULATED_STORAGE_SOURCE), String.valueOf(userId));
-    }
-
-    /** {@hide} */
-    public static File getEmulatedStorageObbSource() {
-        // /mnt/shell/emulated/obb
-        return new File(System.getenv(ENV_EMULATED_STORAGE_SOURCE), DIR_OBB);
-    }
-
     /**
      * Standard directory in which to place any audio files that should be
      * in the regular list of music for the user.
@@ -683,6 +583,13 @@
     public static final String MEDIA_UNMOUNTABLE = "unmountable";
 
     /**
+     * Storage state if the media is in the process of being ejected.
+     *
+     * @see #getExternalStorageState(File)
+     */
+    public static final String MEDIA_EJECTING = "ejecting";
+
+    /**
      * Returns the current state of the primary "external" storage device.
      * 
      * @see #getExternalStorageDirectory()
@@ -693,7 +600,7 @@
      *         {@link #MEDIA_BAD_REMOVAL}, or {@link #MEDIA_UNMOUNTABLE}.
      */
     public static String getExternalStorageState() {
-        final File externalDir = sCurrentUser.getExternalDirsForApp()[0];
+        final File externalDir = sCurrentUser.getExternalDirs()[0];
         return getExternalStorageState(externalDir);
     }
 
@@ -716,17 +623,12 @@
      *         {@link #MEDIA_BAD_REMOVAL}, or {@link #MEDIA_UNMOUNTABLE}.
      */
     public static String getExternalStorageState(File path) {
-        final StorageVolume volume = getStorageVolume(path);
+        final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId());
         if (volume != null) {
-            final IMountService mountService = IMountService.Stub.asInterface(
-                    ServiceManager.getService("mount"));
-            try {
-                return mountService.getVolumeState(volume.getPath());
-            } catch (RemoteException e) {
-            }
+            return volume.getState();
+        } else {
+            return MEDIA_UNKNOWN;
         }
-
-        return Environment.MEDIA_UNKNOWN;
     }
 
     /**
@@ -738,7 +640,7 @@
      */
     public static boolean isExternalStorageRemovable() {
         if (isStorageDisabled()) return false;
-        final File externalDir = sCurrentUser.getExternalDirsForApp()[0];
+        final File externalDir = sCurrentUser.getExternalDirs()[0];
         return isExternalStorageRemovable(externalDir);
     }
 
@@ -753,7 +655,7 @@
      *             device.
      */
     public static boolean isExternalStorageRemovable(File path) {
-        final StorageVolume volume = getStorageVolume(path);
+        final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId());
         if (volume != null) {
             return volume.isRemovable();
         } else {
@@ -771,7 +673,7 @@
      */
     public static boolean isExternalStorageEmulated() {
         if (isStorageDisabled()) return false;
-        final File externalDir = sCurrentUser.getExternalDirsForApp()[0];
+        final File externalDir = sCurrentUser.getExternalDirs()[0];
         return isExternalStorageEmulated(externalDir);
     }
 
@@ -784,7 +686,7 @@
      *             device.
      */
     public static boolean isExternalStorageEmulated(File path) {
-        final StorageVolume volume = getStorageVolume(path);
+        final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId());
         if (volume != null) {
             return volume.isEmulated();
         } else {
@@ -797,19 +699,6 @@
         return path == null ? new File(defaultPath) : new File(path);
     }
 
-    private static String getCanonicalPathOrNull(String variableName) {
-        String path = System.getenv(variableName);
-        if (path == null) {
-            return null;
-        }
-        try {
-            return new File(path).getCanonicalPath();
-        } catch (IOException e) {
-            Log.w(TAG, "Unable to resolve canonical path for " + path);
-            return null;
-        }
-    }
-
     /** {@hide} */
     public static void setUserRequired(boolean userRequired) {
         sUserRequired = userRequired;
@@ -856,28 +745,6 @@
         return SystemProperties.getBoolean("config.disable_storage", false);
     }
 
-    private static StorageVolume getStorageVolume(File path) {
-        try {
-            path = path.getCanonicalFile();
-        } catch (IOException e) {
-            return null;
-        }
-
-        try {
-            final IMountService mountService = IMountService.Stub.asInterface(
-                    ServiceManager.getService("mount"));
-            final StorageVolume[] volumes = mountService.getVolumeList();
-            for (StorageVolume volume : volumes) {
-                if (FileUtils.contains(volume.getPathFile(), path)) {
-                    return volume;
-                }
-            }
-        } catch (RemoteException e) {
-        }
-
-        return null;
-    }
-
     /**
      * If the given path exists on emulated external storage, return the
      * translated backing path hosted on internal storage. This bypasses any
@@ -891,26 +758,7 @@
      * @hide
      */
     public static File maybeTranslateEmulatedPathToInternal(File path) {
-        // Fast return if not emulated, or missing variables
-        if (!Environment.isExternalStorageEmulated()
-                || CANONCIAL_EMULATED_STORAGE_TARGET == null) {
-            return path;
-        }
-
-        try {
-            final String rawPath = path.getCanonicalPath();
-            if (rawPath.startsWith(CANONCIAL_EMULATED_STORAGE_TARGET)) {
-                final File internalPath = new File(DIR_MEDIA_STORAGE,
-                        rawPath.substring(CANONCIAL_EMULATED_STORAGE_TARGET.length()));
-                if (internalPath.exists()) {
-                    return internalPath;
-                }
-            }
-        } catch (IOException e) {
-            Log.w(TAG, "Failed to resolve canonical path for " + path);
-        }
-
-        // Unable to translate to internal path; use original
+        // TODO: bring back this optimization
         return path;
     }
 }
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 0a724a1..b302f95 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -369,6 +369,23 @@
      * {@link File#getCanonicalFile()} to avoid symlink or path traversal
      * attacks.
      */
+    public static boolean contains(File[] dirs, File file) {
+        for (File dir : dirs) {
+            if (contains(dir, file)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Test if a file lives under the given directory, either as a direct child
+     * or a distant grandchild.
+     * <p>
+     * Both files <em>must</em> have been resolved using
+     * {@link File#getCanonicalFile()} to avoid symlink or path traversal
+     * attacks.
+     */
     public static boolean contains(File dir, File file) {
         if (file == null) return false;
 
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 16dac7d..804d3d0 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -43,6 +43,7 @@
     boolean isInteractive();
     boolean isPowerSaveMode();
     boolean setPowerSaveMode(boolean mode);
+    boolean isDeviceIdleMode();
 
     void reboot(boolean confirm, String reason, boolean wait);
     void shutdown(boolean confirm, boolean wait);
@@ -50,6 +51,7 @@
 
     void setStayOnSetting(int val);
     void boostScreenBrightness(long time);
+    boolean isScreenBrightnessBoosted();
 
     // temporarily overrides the screen brightness settings to allow the user to
     // see the effect of a settings change without applying it immediately
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index de970cb..01c9a21 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -712,6 +712,22 @@
     }
 
     /**
+     * Returns whether the screen brightness is currently boosted to maximum, caused by a call
+     * to {@link #boostScreenBrightness(long)}.
+     * @return {@code True} if the screen brightness is currently boosted. {@code False} otherwise.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean isScreenBrightnessBoosted() {
+        try {
+            return mService.isScreenBrightnessBoosted();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
      * Sets the brightness of the backlights (screen, keyboard, button).
      * <p>
      * Requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
@@ -856,6 +872,23 @@
     }
 
     /**
+     * Returns true if the device is currently in idle mode.  This happens when a device
+     * has been sitting unused and unmoving for a sufficiently long period of time, so that
+     * it decides to go into a lower power-use state.  This may involve things like turning
+     * off network access to apps.  You can monitor for changes to this state with
+     * {@link #ACTION_DEVICE_IDLE_MODE_CHANGED}.
+     *
+     * @return Returns true if currently in low power mode, else false.
+     */
+    public boolean isDeviceIdleMode() {
+        try {
+            return mService.isDeviceIdleMode();
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
      * Turn off the device.
      *
      * @param confirm If true, shows a shutdown confirmation dialog.
@@ -879,6 +912,14 @@
             = "android.os.action.POWER_SAVE_MODE_CHANGED";
 
     /**
+     * Intent that is broadcast when the state of {@link #isDeviceIdleMode()} changes.
+     * This broadcast is only sent to registered receivers.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_DEVICE_IDLE_MODE_CHANGED
+            = "android.os.action.DEVICE_IDLE_MODE_CHANGED";
+
+    /**
      * Intent that is broadcast when the state of {@link #isPowerSaveMode()} is about to change.
      * This broadcast is only sent to registered receivers.
      *
@@ -892,6 +933,16 @@
     public static final String EXTRA_POWER_SAVE_MODE = "mode";
 
     /**
+     * Intent that is broadcast when the state of {@link #isScreenBrightnessBoosted()} has changed.
+     * This broadcast is only sent to registered receivers.
+     *
+     * @hide
+     **/
+    @SystemApi
+    public static final String ACTION_SCREEN_BRIGHTNESS_BOOST_CHANGED
+            = "android.os.action.SCREEN_BRIGHTNESS_BOOST_CHANGED";
+
+    /**
      * A wake lock is a mechanism to indicate that your application needs
      * to have the device stay on.
      * <p>
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 6f31768..00ab262 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -117,7 +117,7 @@
     /**
      * Used by the dream manager to override certain properties while dozing.
      *
-     * @param screenState The overridden screen state, or {@link Display.STATE_UNKNOWN}
+     * @param screenState The overridden screen state, or {@link Display#STATE_UNKNOWN}
      * to disable the override.
      * @param screenBrightness The overridden screen brightness, or
      * {@link PowerManager#BRIGHTNESS_DEFAULT} to disable the override.
@@ -132,4 +132,6 @@
     public interface LowPowerModeListener {
         public void onLowPowerModeChanged(boolean enabled);
     }
+
+    public abstract void setDeviceIdleMode(boolean enabled);
 }
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 0de9c70..355ec8c 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -637,10 +637,8 @@
             if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
                 argsForZygote.add("--enable-assert");
             }
-            if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER) {
-                argsForZygote.add("--mount-external-multiuser");
-            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL) {
-                argsForZygote.add("--mount-external-multiuser-all");
+            if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
+                argsForZygote.add("--mount-external-default");
             }
             argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
 
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 6209c2a..fef12d1 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -757,12 +757,13 @@
                 return _result;
             }
 
-            public StorageVolume[] getVolumeList() throws RemoteException {
+            public StorageVolume[] getVolumeList(int userId) throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
                 StorageVolume[] _result;
                 try {
                     _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt(userId);
                     mRemote.transact(Stub.TRANSACTION_getVolumeList, _data, _reply, 0);
                     _reply.readException();
                     _result = _reply.createTypedArray(StorageVolume.CREATOR);
@@ -1308,7 +1309,8 @@
                 }
                 case TRANSACTION_getVolumeList: {
                     data.enforceInterface(DESCRIPTOR);
-                    StorageVolume[] result = getVolumeList();
+                    int userId = data.readInt();
+                    StorageVolume[] result = getVolumeList(userId);
                     reply.writeNoException();
                     reply.writeTypedArray(result, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                     return true;
@@ -1630,7 +1632,7 @@
     /**
      * Returns list of all mountable volumes.
      */
-    public StorageVolume[] getVolumeList() throws RemoteException;
+    public StorageVolume[] getVolumeList(int userId) throws RemoteException;
 
     /**
      * Gets the path on the filesystem for the ASEC container itself.
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 2785ee8..532bf2c 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -18,19 +18,23 @@
 
 import static android.net.TrafficStats.MB_IN_BYTES;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.os.Environment;
+import android.os.FileUtils;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.SparseArray;
 
+import libcore.util.EmptyArray;
+
 import com.android.internal.util.Preconditions;
 
 import java.io.File;
@@ -60,6 +64,7 @@
 public class StorageManager {
     private static final String TAG = "StorageManager";
 
+    private final Context mContext;
     private final ContentResolver mResolver;
 
     /*
@@ -311,8 +316,9 @@
      *
      * @hide
      */
-    public StorageManager(ContentResolver resolver, Looper tgtLooper) throws RemoteException {
-        mResolver = resolver;
+    public StorageManager(Context context, Looper tgtLooper) {
+        mContext = context;
+        mResolver = context.getContentResolver();
         mTgtLooper = tgtLooper;
         mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
         if (mMountService == null) {
@@ -548,17 +554,46 @@
         return null;
     }
 
+    /** {@hide} */
+    public @Nullable StorageVolume getStorageVolume(File file) {
+        return getStorageVolume(getVolumeList(), file);
+    }
+
+    /** {@hide} */
+    public static @Nullable StorageVolume getStorageVolume(File file, int userId) {
+        return getStorageVolume(getVolumeList(userId), file);
+    }
+
+    /** {@hide} */
+    private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) {
+        File canonicalFile = null;
+        try {
+            canonicalFile = file.getCanonicalFile();
+        } catch (IOException ignored) {
+            canonicalFile = null;
+        }
+        for (StorageVolume volume : volumes) {
+            if (volume.getPathFile().equals(file)) {
+                return volume;
+            }
+            if (FileUtils.contains(volume.getPathFile(), canonicalFile)) {
+                return volume;
+            }
+        }
+        return null;
+    }
+
     /**
      * Gets the state of a volume via its mountpoint.
      * @hide
      */
-    public String getVolumeState(String mountPoint) {
-         if (mMountService == null) return Environment.MEDIA_REMOVED;
-        try {
-            return mMountService.getVolumeState(mountPoint);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get volume state", e);
-            return null;
+    @Deprecated
+    public @NonNull String getVolumeState(String mountPoint) {
+        final StorageVolume vol = getStorageVolume(new File(mountPoint));
+        if (vol != null) {
+            return vol.getState();
+        } else {
+            return Environment.MEDIA_UNKNOWN;
         }
     }
 
@@ -566,20 +601,22 @@
      * Returns list of all mountable volumes.
      * @hide
      */
-    public StorageVolume[] getVolumeList() {
-        if (mMountService == null) return new StorageVolume[0];
+    public @NonNull StorageVolume[] getVolumeList() {
         try {
-            Parcelable[] list = mMountService.getVolumeList();
-            if (list == null) return new StorageVolume[0];
-            int length = list.length;
-            StorageVolume[] result = new StorageVolume[length];
-            for (int i = 0; i < length; i++) {
-                result[i] = (StorageVolume)list[i];
-            }
-            return result;
+            return mMountService.getVolumeList(mContext.getUserId());
         } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get volume list", e);
-            return null;
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /** {@hide} */
+    public static @NonNull StorageVolume[] getVolumeList(int userId) {
+        final IMountService mountService = IMountService.Stub.asInterface(
+                ServiceManager.getService("mount"));
+        try {
+            return mountService.getVolumeList(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -587,9 +624,9 @@
      * Returns list of paths for all mountable volumes.
      * @hide
      */
-    public String[] getVolumePaths() {
+    @Deprecated
+    public @NonNull String[] getVolumePaths() {
         StorageVolume[] volumes = getVolumeList();
-        if (volumes == null) return null;
         int count = volumes.length;
         String[] paths = new String[count];
         for (int i = 0; i < count; i++) {
@@ -599,21 +636,21 @@
     }
 
     /** {@hide} */
-    public StorageVolume getPrimaryVolume() {
+    public @NonNull StorageVolume getPrimaryVolume() {
         return getPrimaryVolume(getVolumeList());
     }
 
     /** {@hide} */
-    public static StorageVolume getPrimaryVolume(StorageVolume[] volumes) {
+    public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) {
         for (StorageVolume volume : volumes) {
             if (volume.isPrimary()) {
                 return volume;
             }
         }
-        Log.w(TAG, "No primary storage defined");
-        return null;
+        throw new IllegalStateException("Missing primary storage");
     }
 
+    /** {@hide} */
     private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
     private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES;
     private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 06565f1..0c391ca 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -17,6 +17,7 @@
 package android.os.storage;
 
 import android.content.Context;
+import android.net.TrafficStats;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
@@ -34,52 +35,58 @@
  */
 public class StorageVolume implements Parcelable {
 
-    // TODO: switch to more durable token
-    private int mStorageId;
+    private final String mId;
+    private final int mStorageId;
 
     private final File mPath;
     private final int mDescriptionId;
     private final boolean mPrimary;
     private final boolean mRemovable;
     private final boolean mEmulated;
-    private final int mMtpReserveSpace;
+    private final long mMtpReserveSize;
     private final boolean mAllowMassStorage;
     /** Maximum file size for the storage, or zero for no limit */
     private final long mMaxFileSize;
     /** When set, indicates exclusive ownership of this volume */
     private final UserHandle mOwner;
 
-    private String mUuid;
-    private String mUserLabel;
-    private String mState;
+    private final String mUuid;
+    private final String mUserLabel;
+    private final String mState;
 
     // StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING,
     // ACTION_MEDIA_NOFS, ACTION_MEDIA_MOUNTED, ACTION_MEDIA_SHARED, ACTION_MEDIA_UNSHARED,
     // ACTION_MEDIA_BAD_REMOVAL, ACTION_MEDIA_UNMOUNTABLE and ACTION_MEDIA_EJECT broadcasts.
     public static final String EXTRA_STORAGE_VOLUME = "storage_volume";
 
-    public StorageVolume(File path, int descriptionId, boolean primary, boolean removable,
-            boolean emulated, int mtpReserveSpace, boolean allowMassStorage, long maxFileSize,
-            UserHandle owner) {
+    public StorageVolume(String id, int storageId, File path, int descriptionId, boolean primary,
+            boolean removable, boolean emulated, long mtpReserveSize, boolean allowMassStorage,
+            long maxFileSize, UserHandle owner, String uuid, String userLabel, String state) {
+        mId = id;
+        mStorageId = storageId;
         mPath = path;
         mDescriptionId = descriptionId;
         mPrimary = primary;
         mRemovable = removable;
         mEmulated = emulated;
-        mMtpReserveSpace = mtpReserveSpace;
+        mMtpReserveSize = mtpReserveSize;
         mAllowMassStorage = allowMassStorage;
         mMaxFileSize = maxFileSize;
         mOwner = owner;
+        mUuid = uuid;
+        mUserLabel = userLabel;
+        mState = state;
     }
 
     private StorageVolume(Parcel in) {
+        mId = in.readString();
         mStorageId = in.readInt();
         mPath = new File(in.readString());
         mDescriptionId = in.readInt();
         mPrimary = in.readInt() != 0;
         mRemovable = in.readInt() != 0;
         mEmulated = in.readInt() != 0;
-        mMtpReserveSpace = in.readInt();
+        mMtpReserveSize = in.readLong();
         mAllowMassStorage = in.readInt() != 0;
         mMaxFileSize = in.readLong();
         mOwner = in.readParcelable(null);
@@ -88,10 +95,8 @@
         mState = in.readString();
     }
 
-    public static StorageVolume fromTemplate(StorageVolume template, File path, UserHandle owner) {
-        return new StorageVolume(path, template.mDescriptionId, template.mPrimary,
-                template.mRemovable, template.mEmulated, template.mMtpReserveSpace,
-                template.mAllowMassStorage, template.mMaxFileSize, owner);
+    public String getId() {
+        return mId;
     }
 
     /**
@@ -153,15 +158,6 @@
     }
 
     /**
-     * Do not call this unless you are MountService
-     */
-    public void setStorageId(int index) {
-        // storage ID is 0x00010001 for primary storage,
-        // then 0x00020001, 0x00030001, etc. for secondary storages
-        mStorageId = ((index + 1) << 16) + 1;
-    }
-
-    /**
      * Number of megabytes of space to leave unallocated by MTP.
      * MTP will subtract this value from the free space it reports back
      * to the host via GetStorageInfo, and will not allow new files to
@@ -174,7 +170,7 @@
      * @return MTP reserve space
      */
     public int getMtpReserveSpace() {
-        return mMtpReserveSpace;
+        return (int) (mMtpReserveSize / TrafficStats.MB_IN_BYTES);
     }
 
     /**
@@ -199,10 +195,6 @@
         return mOwner;
     }
 
-    public void setUuid(String uuid) {
-        mUuid = uuid;
-    }
-
     public String getUuid() {
         return mUuid;
     }
@@ -222,18 +214,10 @@
         }
     }
 
-    public void setUserLabel(String userLabel) {
-        mUserLabel = userLabel;
-    }
-
     public String getUserLabel() {
         return mUserLabel;
     }
 
-    public void setState(String state) {
-        mState = state;
-    }
-
     public String getState() {
         return mState;
     }
@@ -262,13 +246,14 @@
     public void dump(IndentingPrintWriter pw) {
         pw.println("StorageVolume:");
         pw.increaseIndent();
+        pw.printPair("mId", mId);
         pw.printPair("mStorageId", mStorageId);
         pw.printPair("mPath", mPath);
         pw.printPair("mDescriptionId", mDescriptionId);
         pw.printPair("mPrimary", mPrimary);
         pw.printPair("mRemovable", mRemovable);
         pw.printPair("mEmulated", mEmulated);
-        pw.printPair("mMtpReserveSpace", mMtpReserveSpace);
+        pw.printPair("mMtpReserveSize", mMtpReserveSize);
         pw.printPair("mAllowMassStorage", mAllowMassStorage);
         pw.printPair("mMaxFileSize", mMaxFileSize);
         pw.printPair("mOwner", mOwner);
@@ -297,13 +282,14 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mId);
         parcel.writeInt(mStorageId);
         parcel.writeString(mPath.toString());
         parcel.writeInt(mDescriptionId);
         parcel.writeInt(mPrimary ? 1 : 0);
         parcel.writeInt(mRemovable ? 1 : 0);
         parcel.writeInt(mEmulated ? 1 : 0);
-        parcel.writeInt(mMtpReserveSpace);
+        parcel.writeLong(mMtpReserveSize);
         parcel.writeInt(mAllowMassStorage ? 1 : 0);
         parcel.writeLong(mMaxFileSize);
         parcel.writeParcelable(mOwner, flags);
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index ccf2cfa..3b482eb 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -706,8 +706,10 @@
      * @param iconResId The icon as a resource ID.
      */
     public void setIcon(@DrawableRes int iconResId) {
-        mIconResId = iconResId;
-        setIcon(mContext.getDrawable(iconResId));
+        if (mIconResId != iconResId) {
+            mIconResId = iconResId;
+            setIcon(mContext.getDrawable(iconResId));
+        }
     }
 
     /**
@@ -1436,7 +1438,7 @@
     protected boolean persistString(String value) {
         if (shouldPersist()) {
             // Shouldn't store null
-            if (value == getPersistedString(null)) {
+            if (TextUtils.equals(value, getPersistedString(null))) {
                 // It's already there, so the same as persisting
                 return true;
             }
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 6517f35..7d57233 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -454,7 +454,6 @@
                 long start, int duration, Long dataUsage, boolean addForAllUsers) {
             final ContentResolver resolver = context.getContentResolver();
             int numberPresentation = PRESENTATION_ALLOWED;
-            boolean isHidden = false;
 
             TelecomManager tm = null;
             try {
@@ -469,12 +468,6 @@
                     if (address != null) {
                         accountAddress = address.getSchemeSpecificPart();
                     }
-                } else {
-                    // We could not find the account through telecom. For call log entries that
-                    // are added with a phone account which is not registered, we automatically
-                    // mark them as hidden. They are unhidden once the account is registered.
-                    Log.i(LOG_TAG, "Marking call log entry as hidden.");
-                    isHidden = true;
                 }
             }
 
@@ -520,7 +513,6 @@
             values.put(PHONE_ACCOUNT_COMPONENT_NAME, accountComponentString);
             values.put(PHONE_ACCOUNT_ID, accountId);
             values.put(PHONE_ACCOUNT_ADDRESS, accountAddress);
-            values.put(PHONE_ACCOUNT_HIDDEN, Integer.valueOf(isHidden ? 1 : 0));
             values.put(NEW, Integer.valueOf(1));
 
             if (callType == MISSED_TYPE) {
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 06862d7..bf7f3cb 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -18,6 +18,7 @@
 
 import android.accounts.Account;
 import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
 import android.content.ActivityNotFoundException;
 import android.content.ContentProviderClient;
 import android.content.ContentProviderOperation;
@@ -1516,8 +1517,14 @@
         /**
          * Build a {@link #CONTENT_LOOKUP_URI} lookup {@link Uri} using the
          * given {@link ContactsContract.Contacts#_ID} and {@link #LOOKUP_KEY}.
+         * <p>
+         * Returns null if unable to construct a valid lookup URI from the
+         * provided parameters.
          */
         public static Uri getLookupUri(long contactId, String lookupKey) {
+            if (TextUtils.isEmpty(lookupKey)) {
+                return null;
+            }
             return ContentUris.withAppendedId(Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI,
                     lookupKey), contactId);
         }
@@ -1622,7 +1629,6 @@
          */
         public static final String CONTENT_VCARD_TYPE = "text/x-vcard";
 
-
         /**
          * Mimimal ID for corp contacts returned from
          * {@link PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI}.
@@ -1632,6 +1638,14 @@
         public static long ENTERPRISE_CONTACT_ID_BASE = 1000000000; // slightly smaller than 2 ** 30
 
         /**
+         * Prefix for corp contacts returned from
+         * {@link PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI}.
+         *
+         * @hide
+         */
+        public static String ENTERPRISE_CONTACT_LOOKUP_PREFIX = "c-";
+
+        /**
          * Return TRUE if a contact ID is from the contacts provider on the enterprise profile.
          *
          * {@link PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI} may return such a contact.
@@ -4803,6 +4817,14 @@
                 Uri.withAppendedPath(AUTHORITY_URI, "raw_contact_entities");
 
         /**
+        * The content:// style URI for this table in corp profile
+        *
+        * @hide
+        */
+        public static final Uri CORP_CONTENT_URI =
+                Uri.withAppendedPath(AUTHORITY_URI, "raw_contact_entities_corp");
+
+        /**
          * The content:// style URI for this table, specific to the user's profile.
          */
         public static final Uri PROFILE_CONTENT_URI =
@@ -5018,9 +5040,17 @@
          *     is from the corp profile, use
          *     {@link ContactsContract.Contacts#isEnterpriseContactId(long)}.
          *     </li>
+         *     <li>
+         *     Corp contacts will get artificial {@link #LOOKUP_KEY}s too.
+         *     </li>
          * </ul>
          * <p>
-         * This URI does NOT support selection nor order-by.
+         * A contact lookup URL built by
+         * {@link ContactsContract.Contacts#getLookupUri(long, String)}
+         * with an {@link #_ID} and a {@link #LOOKUP_KEY} returned by this API can be passed to
+         * {@link ContactsContract.QuickContact#showQuickContact} even if a contact is from the
+         * corp profile.
+         * </p>
          *
          * <pre>
          * Uri lookupUri = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI,
@@ -6011,10 +6041,18 @@
             *     a contact
             *     is from the corp profile, use
             *     {@link ContactsContract.Contacts#isEnterpriseContactId(long)}.
-            *     </li>
-            * </ul>
-            * <p>
-            * This URI does NOT support selection nor order-by.
+             *     </li>
+             *     <li>
+             *     Corp contacts will get artificial {@link #LOOKUP_KEY}s too.
+             *     </li>
+             * </ul>
+             * <p>
+             * A contact lookup URL built by
+             * {@link ContactsContract.Contacts#getLookupUri(long, String)}
+             * with an {@link #_ID} and a {@link #LOOKUP_KEY} returned by this API can be passed to
+             * {@link ContactsContract.QuickContact#showQuickContact} even if a contact is from the
+             * corp profile.
+             * </p>
             *
             * <pre>
             * Uri lookupUri = Uri.withAppendedPath(Email.ENTERPRISE_CONTENT_LOOKUP_URI,
@@ -8168,6 +8206,9 @@
          */
         public static final int MODE_LARGE = 3;
 
+        /** @hide */
+        public static final int MODE_DEFAULT = MODE_LARGE;
+
         /**
          * Constructs the QuickContacts intent with a view's rect.
          * @hide
@@ -8210,6 +8251,7 @@
             // Launch pivot dialog through intent for now
             final Intent intent = new Intent(ACTION_QUICK_CONTACT).addFlags(intentFlags);
 
+            // NOTE: This logic and rebuildManagedQuickContactsIntent() must be in sync.
             intent.setData(lookupUri);
             intent.setSourceBounds(target);
             intent.putExtra(EXTRA_MODE, mode);
@@ -8218,6 +8260,30 @@
         }
 
         /**
+         * Constructs a QuickContacts intent based on an incoming intent for DevicePolicyManager
+         * to strip off anything not necessary.
+         * 
+         * @hide
+         */
+        public static Intent rebuildManagedQuickContactsIntent(String lookupKey, long contactId,
+                Intent originalIntent) {
+            final Intent intent = new Intent(ACTION_QUICK_CONTACT);
+            // Rebuild the URI from a lookup key and a contact ID.
+            intent.setData(Contacts.getLookupUri(contactId, lookupKey));
+
+            // Copy flags and always set NEW_TASK because it won't have a parent activity.
+            intent.setFlags(originalIntent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
+
+            // Copy extras.
+            intent.setSourceBounds(originalIntent.getSourceBounds());
+            intent.putExtra(EXTRA_MODE, originalIntent.getIntExtra(EXTRA_MODE, MODE_DEFAULT));
+            intent.putExtra(EXTRA_EXCLUDE_MIMES,
+                    originalIntent.getStringArrayExtra(EXTRA_EXCLUDE_MIMES));
+            return intent;
+        }
+
+
+        /**
          * Trigger a dialog that lists the various methods of interacting with
          * the requested {@link Contacts} entry. This may be based on available
          * {@link ContactsContract.Data} rows under that contact, and may also
@@ -8245,7 +8311,7 @@
             // Trigger with obtained rectangle
             Intent intent = composeQuickContactsIntent(context, target, lookupUri, mode,
                     excludeMimes);
-            startActivityWithErrorToast(context, intent);
+            ContactsInternal.startQuickContactWithErrorToast(context, intent);
         }
 
         /**
@@ -8278,7 +8344,7 @@
                 String[] excludeMimes) {
             Intent intent = composeQuickContactsIntent(context, target, lookupUri, mode,
                     excludeMimes);
-            startActivityWithErrorToast(context, intent);
+            ContactsInternal.startQuickContactWithErrorToast(context, intent);
         }
 
         /**
@@ -8311,10 +8377,10 @@
             // Use MODE_LARGE instead of accepting mode as a parameter. The different mode
             // values defined in ContactsContract only affect very old implementations
             // of QuickContacts.
-            Intent intent = composeQuickContactsIntent(context, target, lookupUri, MODE_LARGE,
+            Intent intent = composeQuickContactsIntent(context, target, lookupUri, MODE_DEFAULT,
                     excludeMimes);
             intent.putExtra(EXTRA_PRIORITIZED_MIMETYPE, prioritizedMimeType);
-            startActivityWithErrorToast(context, intent);
+            ContactsInternal.startQuickContactWithErrorToast(context, intent);
         }
 
         /**
@@ -8349,19 +8415,10 @@
             // Use MODE_LARGE instead of accepting mode as a parameter. The different mode
             // values defined in ContactsContract only affect very old implementations
             // of QuickContacts.
-            Intent intent = composeQuickContactsIntent(context, target, lookupUri, MODE_LARGE,
+            Intent intent = composeQuickContactsIntent(context, target, lookupUri, MODE_DEFAULT,
                     excludeMimes);
             intent.putExtra(EXTRA_PRIORITIZED_MIMETYPE, prioritizedMimeType);
-            startActivityWithErrorToast(context, intent);
-        }
-
-        private static void startActivityWithErrorToast(Context context, Intent intent) {
-            try {
-              context.startActivity(intent);
-            } catch (ActivityNotFoundException e) {
-                Toast.makeText(context, com.android.internal.R.string.quick_contacts_not_available,
-                                Toast.LENGTH_SHORT).show();
-            }
+            ContactsInternal.startQuickContactWithErrorToast(context, intent);
         }
     }
 
diff --git a/core/java/android/provider/ContactsInternal.java b/core/java/android/provider/ContactsInternal.java
new file mode 100644
index 0000000..059a603
--- /dev/null
+++ b/core/java/android/provider/ContactsInternal.java
@@ -0,0 +1,112 @@
+/*
+ * 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 android.provider;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ActivityNotFoundException;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.content.UriMatcher;
+import android.net.Uri;
+import android.os.Process;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.widget.Toast;
+
+import java.util.List;
+
+/**
+ * Contacts related internal methods.
+ *
+ * @hide
+ */
+public class ContactsInternal {
+    private ContactsInternal() {
+    }
+
+    /** URI matcher used to parse contact URIs. */
+    private static final UriMatcher sContactsUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+
+    private static final int CONTACTS_URI_LOOKUP_ID = 1000;
+
+    static {
+        // Contacts URI matching table
+        final UriMatcher matcher = sContactsUriMatcher;
+        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_URI_LOOKUP_ID);
+    }
+
+    /**
+     * Called by {@link ContactsContract} to star Quick Contact, possibly on the managed profile.
+     */
+    public static void startQuickContactWithErrorToast(Context context, Intent intent) {
+        final Uri uri = intent.getData();
+
+        final int match = sContactsUriMatcher.match(uri);
+        switch (match) {
+            case CONTACTS_URI_LOOKUP_ID: {
+                if (maybeStartManagedQuickContact(context, intent)) {
+                    return; // Request handled by DPM.  Just return here.
+                }
+                break;
+            }
+        }
+        // Launch on the current profile.
+        startQuickContactWithErrorToastForUser(context, intent, Process.myUserHandle());
+    }
+
+    public static void startQuickContactWithErrorToastForUser(Context context, Intent intent,
+            UserHandle user) {
+        try {
+            context.startActivityAsUser(intent, user);
+        } catch (ActivityNotFoundException e) {
+            Toast.makeText(context, com.android.internal.R.string.quick_contacts_not_available,
+                    Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    /**
+     * If the URI in {@code intent} is of a corp contact, launch quick contact on the managed
+     * profile.
+     *
+     * @return the URI in {@code intent} is of a corp contact thus launched on the managed profile.
+     */
+    private static boolean maybeStartManagedQuickContact(Context context, Intent originalIntent) {
+        final Uri uri = originalIntent.getData();
+
+        // Decompose into an ID and a lookup key.
+        final List<String> pathSegments = uri.getPathSegments();
+        final long contactId = ContentUris.parseId(uri);
+        final String lookupKey = pathSegments.get(2);
+
+        // See if it has a corp lookupkey.
+        if (TextUtils.isEmpty(lookupKey)
+                || !lookupKey.startsWith(
+                        ContactsContract.Contacts.ENTERPRISE_CONTACT_LOOKUP_PREFIX)) {
+            return false; // It's not a corp lookup key.
+        }
+
+        // Launch Quick Contact on the managed profile, if the policy allows.
+        final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+        final String actualLookupKey = lookupKey.substring(
+                ContactsContract.Contacts.ENTERPRISE_CONTACT_LOOKUP_PREFIX.length());
+        final long actualContactId =
+                (contactId - ContactsContract.Contacts.ENTERPRISE_CONTACT_ID_BASE);
+
+        dpm.startManagedQuickContact(actualLookupKey, actualContactId, originalIntent);
+        return true;
+    }
+}
diff --git a/core/java/android/provider/SearchIndexableData.java b/core/java/android/provider/SearchIndexableData.java
index 7b9d1ea..5e0a76d 100644
--- a/core/java/android/provider/SearchIndexableData.java
+++ b/core/java/android/provider/SearchIndexableData.java
@@ -16,6 +16,7 @@
 
 package android.provider;
 
+import android.annotation.SystemApi;
 import android.content.Context;
 
 import java.util.Locale;
@@ -27,6 +28,7 @@
  *
  * @hide
  */
+@SystemApi
 public abstract class SearchIndexableData {
 
     /**
diff --git a/core/java/android/provider/SearchIndexableResource.java b/core/java/android/provider/SearchIndexableResource.java
index c807df2..1eb1734 100644
--- a/core/java/android/provider/SearchIndexableResource.java
+++ b/core/java/android/provider/SearchIndexableResource.java
@@ -16,6 +16,7 @@
 
 package android.provider;
 
+import android.annotation.SystemApi;
 import android.content.Context;
 
 /**
@@ -31,6 +32,7 @@
  *
  * @hide
  */
+@SystemApi
 public class SearchIndexableResource extends SearchIndexableData {
 
     /**
diff --git a/core/java/android/provider/SearchIndexablesContract.java b/core/java/android/provider/SearchIndexablesContract.java
index 1b5f72a..93ac7f6 100644
--- a/core/java/android/provider/SearchIndexablesContract.java
+++ b/core/java/android/provider/SearchIndexablesContract.java
@@ -16,6 +16,7 @@
 
 package android.provider;
 
+import android.annotation.SystemApi;
 import android.content.ContentResolver;
 
 /**
@@ -23,6 +24,7 @@
  *
  * @hide
  */
+@SystemApi
 public class SearchIndexablesContract {
 
     /**
@@ -234,7 +236,7 @@
     /**
      * The base columns.
      */
-    private static class BaseColumns {
+    public static class BaseColumns {
         private BaseColumns() {
         }
 
diff --git a/core/java/android/provider/SearchIndexablesProvider.java b/core/java/android/provider/SearchIndexablesProvider.java
index 9c8f6d0..3120e54 100644
--- a/core/java/android/provider/SearchIndexablesProvider.java
+++ b/core/java/android/provider/SearchIndexablesProvider.java
@@ -16,6 +16,7 @@
 
 package android.provider;
 
+import android.annotation.SystemApi;
 import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.content.Context;
@@ -61,6 +62,7 @@
  *
  * @hide
  */
+@SystemApi
 public abstract class SearchIndexablesProvider extends ContentProvider {
     private static final String TAG = "IndexablesProvider";
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e6b39b5..f79ef35 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5255,6 +5255,15 @@
         public static final String SMS_DEFAULT_APPLICATION = "sms_default_application";
 
         /**
+         * Specifies the package name currently configured to be the emergency assistance application
+         *
+         * @see android.telephony.TelephonyManager#ACTION_EMERGENCY_ASSISTANCE
+         *
+         * @hide
+         */
+        public static final String EMERGENCY_ASSISTANCE_APPLICATION = "emergency_assistance_application";
+
+        /**
          * Names of the packages that the current user has explicitly allowed to
          * see all of the user's notifications, separated by ':'.
          *
@@ -5375,6 +5384,7 @@
             BACKUP_AUTO_RESTORE,
             ENABLED_ACCESSIBILITY_SERVICES,
             ENABLED_NOTIFICATION_LISTENERS,
+            ENABLED_INPUT_METHODS,
             TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
             TOUCH_EXPLORATION_ENABLED,
             ACCESSIBILITY_ENABLED,
@@ -6087,7 +6097,7 @@
        public static final String PACKAGE_VERIFIER_SETTING_VISIBLE = "verifier_setting_visible";
 
        /**
-        * Run package verificaiton on apps installed through ADB/ADT/USB
+        * Run package verification on apps installed through ADB/ADT/USB
         * 1 = perform package verification on ADB installs (default)
         * 0 = bypass package verification on ADB installs
         * @hide
@@ -6365,6 +6375,14 @@
                 "wifi_scan_always_enabled";
 
        /**
+        * Settings to allow BLE scans to be enabled even when Bluetooth is turned off for
+        * connectivity.
+        * @hide
+        */
+       public static final String BLE_SCAN_ALWAYS_AVAILABLE =
+               "ble_scan_always_enabled";
+
+       /**
         * Used to save the Wifi_ON state prior to tethering.
         * This state will be checked to restore Wifi after
         * the user turns off tethering.
diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl
index ac6bbb7..579cdbe 100644
--- a/core/java/android/security/IKeystoreService.aidl
+++ b/core/java/android/security/IKeystoreService.aidl
@@ -19,6 +19,7 @@
 import android.security.keymaster.ExportResult;
 import android.security.keymaster.KeyCharacteristics;
 import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterBlob;
 import android.security.keymaster.OperationResult;
 import android.security.KeystoreArguments;
 
@@ -59,16 +60,19 @@
 
     // Keymaster 0.4 methods
     int addRngEntropy(in byte[] data);
-    int generateKey(String alias, in KeymasterArguments arguments, int uid, int flags,
+    int generateKey(String alias, in KeymasterArguments arguments, in byte[] entropy, int uid,
+        int flags, out KeyCharacteristics characteristics);
+    int getKeyCharacteristics(String alias, in KeymasterBlob clientId, in KeymasterBlob appId,
         out KeyCharacteristics characteristics);
-    int getKeyCharacteristics(String alias, in byte[] clientId,
-        in byte[] appId, out KeyCharacteristics characteristics);
     int importKey(String alias, in KeymasterArguments arguments, int format,
         in byte[] keyData, int uid, int flags, out KeyCharacteristics characteristics);
-    ExportResult exportKey(String alias, int format, in byte[] clientId, in byte[] appId);
+    ExportResult exportKey(String alias, int format, in KeymasterBlob clientId,
+        in KeymasterBlob appId);
     OperationResult begin(IBinder appToken, String alias, int purpose, boolean pruneable,
-        in KeymasterArguments params, out KeymasterArguments operationParams);
+        in KeymasterArguments params, in byte[] entropy, out KeymasterArguments operationParams);
     OperationResult update(IBinder token, in KeymasterArguments params, in byte[] input);
     OperationResult finish(IBinder token, in KeymasterArguments params, in byte[] signature);
     int abort(IBinder handle);
+    boolean isOperationAuthorized(IBinder token);
+    int addAuthToken(in byte[] authToken);
 }
diff --git a/core/java/android/security/NetworkSecurityPolicy.java b/core/java/android/security/NetworkSecurityPolicy.java
index c7274e8..0b3bf453 100644
--- a/core/java/android/security/NetworkSecurityPolicy.java
+++ b/core/java/android/security/NetworkSecurityPolicy.java
@@ -19,48 +19,57 @@
 /**
  * Network security policy.
  *
- * @hide
+ * <p>Network stacks/components should honor this policy to make it possible to centrally control
+ * the relevant aspects of network security behavior.
+ *
+ * <p>The policy currently consists of a single flag: whether cleartext network traffic is
+ * permitted. See {@link #isCleartextTrafficPermitted()}.
  */
 public class NetworkSecurityPolicy {
 
-  private static final NetworkSecurityPolicy INSTANCE = new NetworkSecurityPolicy();
+    private static final NetworkSecurityPolicy INSTANCE = new NetworkSecurityPolicy();
 
-  private boolean mCleartextTrafficPermitted = true;
+    private NetworkSecurityPolicy() {}
 
-  private NetworkSecurityPolicy() {}
-
-  /**
-   * Gets the policy.
-   */
-  public static NetworkSecurityPolicy getInstance() {
-    return INSTANCE;
-  }
-
-  /**
-   * Checks whether cleartext network traffic (e.g., HTTP, WebSockets, XMPP, IMAP, SMTP -- without
-   * TLS or STARTTLS) is permitted for this process.
-   *
-   * <p>When cleartext network traffic is not permitted, the platform's components (e.g., HTTP
-   * stacks, {@code WebView}, {@code MediaPlayer}) will refuse this process's requests to use
-   * cleartext traffic. Third-party libraries are encouraged to honor this setting as well.
-   */
-  public boolean isCleartextTrafficPermitted() {
-    synchronized (this) {
-      return mCleartextTrafficPermitted;
+    /**
+     * Gets the policy for this process.
+     *
+     * <p>It's fine to cache this reference. Any changes to the policy will be immediately visible
+     * through the reference.
+     */
+    public static NetworkSecurityPolicy getInstance() {
+        return INSTANCE;
     }
-  }
 
-  /**
-   * Sets whether cleartext network traffic is permitted for this process.
-   *
-   * <p>This method is used by the platform early on in the application's initialization to set the
-   * policy.
-   *
-   * @hide
-   */
-  public void setCleartextTrafficPermitted(boolean permitted) {
-    synchronized (this) {
-      mCleartextTrafficPermitted = permitted;
+    /**
+     * Returns whether cleartext network traffic (e.g. HTTP, FTP, WebSockets, XMPP, IMAP, SMTP --
+     * without TLS or STARTTLS) is permitted for this process.
+     *
+     * <p>When cleartext network traffic is not permitted, the platform's components (e.g. HTTP and
+     * FTP stacks, {@link android.webkit.WebView}, {@link android.media.MediaPlayer}) will refuse
+     * this process's requests to use cleartext traffic. Third-party libraries are strongly
+     * encouraged to honor this setting as well.
+     *
+     * <p>This flag is honored on a best effort basis because it's impossible to prevent all
+     * cleartext traffic from Android applications given the level of access provided to them. For
+     * example, there's no expectation that the {@link java.net.Socket} API will honor this flag
+     * because it cannot determine whether its traffic is in cleartext. However, most network
+     * traffic from applications is handled by higher-level network stacks/components which can
+     * honor this aspect of the policy.
+     */
+    public boolean isCleartextTrafficPermitted() {
+        return libcore.net.NetworkSecurityPolicy.isCleartextTrafficPermitted();
     }
-  }
+
+    /**
+     * Sets whether cleartext network traffic is permitted for this process.
+     *
+     * <p>This method is used by the platform early on in the application's initialization to set
+     * the policy.
+     *
+     * @hide
+     */
+    public void setCleartextTrafficPermitted(boolean permitted) {
+        libcore.net.NetworkSecurityPolicy.setCleartextTrafficPermitted(permitted);
+    }
 }
diff --git a/core/java/android/security/keymaster/KeymasterBlob.aidl b/core/java/android/security/keymaster/KeymasterBlob.aidl
new file mode 100644
index 0000000..8f70f7c
--- /dev/null
+++ b/core/java/android/security/keymaster/KeymasterBlob.aidl
@@ -0,0 +1,20 @@
+/**
+ * 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 android.security.keymaster;
+
+/* @hide */
+parcelable KeymasterBlob;
diff --git a/core/java/android/security/keymaster/KeymasterBlob.java b/core/java/android/security/keymaster/KeymasterBlob.java
new file mode 100644
index 0000000..cb95604
--- /dev/null
+++ b/core/java/android/security/keymaster/KeymasterBlob.java
@@ -0,0 +1,55 @@
+/**
+ * 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 android.security.keymaster;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * @hide
+ */
+public class KeymasterBlob implements Parcelable {
+    public byte[] blob;
+
+    public KeymasterBlob(byte[] blob) {
+        this.blob = blob;
+    }
+    public static final Parcelable.Creator<KeymasterBlob> CREATOR = new
+            Parcelable.Creator<KeymasterBlob>() {
+                public KeymasterBlob createFromParcel(Parcel in) {
+                    return new KeymasterBlob(in);
+                }
+
+                public KeymasterBlob[] newArray(int length) {
+                    return new KeymasterBlob[length];
+                }
+            };
+
+    protected KeymasterBlob(Parcel in) {
+        blob = in.createByteArray();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeByteArray(blob);
+    }
+}
diff --git a/core/java/android/security/keymaster/KeymasterBlobArgument.java b/core/java/android/security/keymaster/KeymasterBlobArgument.java
index 9af4445..7d587bf 100644
--- a/core/java/android/security/keymaster/KeymasterBlobArgument.java
+++ b/core/java/android/security/keymaster/KeymasterBlobArgument.java
@@ -26,6 +26,13 @@
 
     public KeymasterBlobArgument(int tag, byte[] blob) {
         super(tag);
+        switch (KeymasterDefs.getTagType(tag)) {
+            case KeymasterDefs.KM_BIGNUM:
+            case KeymasterDefs.KM_BYTES:
+                break; // OK.
+            default:
+                throw new IllegalArgumentException("Bad blob tag " + tag);
+        }
         this.blob = blob;
     }
 
diff --git a/core/java/android/security/keymaster/KeymasterBooleanArgument.java b/core/java/android/security/keymaster/KeymasterBooleanArgument.java
index 5481e8f..9c03674 100644
--- a/core/java/android/security/keymaster/KeymasterBooleanArgument.java
+++ b/core/java/android/security/keymaster/KeymasterBooleanArgument.java
@@ -28,6 +28,12 @@
 
     public KeymasterBooleanArgument(int tag) {
         super(tag);
+        switch (KeymasterDefs.getTagType(tag)) {
+            case KeymasterDefs.KM_BOOL:
+                break; // OK.
+            default:
+                throw new IllegalArgumentException("Bad bool tag " + tag);
+        }
     }
 
     public KeymasterBooleanArgument(int tag, Parcel in) {
diff --git a/core/java/android/security/keymaster/KeymasterDateArgument.java b/core/java/android/security/keymaster/KeymasterDateArgument.java
index 310f546..bffd24d 100644
--- a/core/java/android/security/keymaster/KeymasterDateArgument.java
+++ b/core/java/android/security/keymaster/KeymasterDateArgument.java
@@ -27,6 +27,12 @@
 
     public KeymasterDateArgument(int tag, Date date) {
         super(tag);
+        switch (KeymasterDefs.getTagType(tag)) {
+            case KeymasterDefs.KM_DATE:
+                break; // OK.
+            default:
+                throw new IllegalArgumentException("Bad date tag " + tag);
+        }
         this.date = date;
     }
 
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index e653b74..e94a312 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -16,6 +16,9 @@
 
 package android.security.keymaster;
 
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * Class tracking all the keymaster enum values needed for the binder API to keystore.
  * This must be kept in sync with hardware/libhardware/include/hardware/keymaster_defs.h
@@ -178,7 +181,7 @@
     public static final int KM_ERROR_UNSUPPORTED_KEY_SIZE = -6;
     public static final int KM_ERROR_UNSUPPORTED_BLOCK_MODE = -7;
     public static final int KM_ERROR_INCOMPATIBLE_BLOCK_MODE = -8;
-    public static final int KM_ERROR_UNSUPPORTED_TAG_LENGTH = -9;
+    public static final int KM_ERROR_UNSUPPORTED_MAC_LENGTH = -9;
     public static final int KM_ERROR_UNSUPPORTED_PADDING_MODE = -10;
     public static final int KM_ERROR_INCOMPATIBLE_PADDING_MODE = -11;
     public static final int KM_ERROR_UNSUPPORTED_DIGEST = -12;
@@ -224,7 +227,54 @@
     public static final int KM_ERROR_VERSION_MISMATCH = -101;
     public static final int KM_ERROR_UNKNOWN_ERROR = -1000;
 
+    public static final Map<Integer, String> sErrorCodeToString = new HashMap<Integer, String>();
+    static {
+        sErrorCodeToString.put(KM_ERROR_OK, "OK");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_PURPOSE, "Unsupported purpose");
+        sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_PURPOSE, "Incompatible purpose");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_ALGORITHM, "Unsupported algorithm");
+        sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_ALGORITHM, "Incompatible algorithm");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_KEY_SIZE, "Unsupported key size");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_BLOCK_MODE, "Unsupported block mode");
+        sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_BLOCK_MODE, "Incompatible block mode");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_MAC_LENGTH,
+                "Unsupported MAC or authentication tag length");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_PADDING_MODE, "Unsupported padding mode");
+        sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_PADDING_MODE, "Incompatible padding mode");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_DIGEST, "Unsupported digest");
+        sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_DIGEST, "Incompatible digest");
+        sErrorCodeToString.put(KM_ERROR_INVALID_EXPIRATION_TIME, "Invalid expiration time");
+        sErrorCodeToString.put(KM_ERROR_INVALID_USER_ID, "Invalid user ID");
+        sErrorCodeToString.put(KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT,
+                "Invalid user authorization timeout");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_KEY_FORMAT, "Unsupported key format");
+        sErrorCodeToString.put(KM_ERROR_INCOMPATIBLE_KEY_FORMAT, "Incompatible key format");
+        sErrorCodeToString.put(KM_ERROR_INVALID_INPUT_LENGTH, "Invalid input length");
+        sErrorCodeToString.put(KM_ERROR_KEY_NOT_YET_VALID, "Key not yet valid");
+        sErrorCodeToString.put(KM_ERROR_KEY_EXPIRED, "Key expired");
+        sErrorCodeToString.put(KM_ERROR_KEY_USER_NOT_AUTHENTICATED, "Key user not authenticated");
+        sErrorCodeToString.put(KM_ERROR_INVALID_OPERATION_HANDLE, "Invalid operation handle");
+        sErrorCodeToString.put(KM_ERROR_VERIFICATION_FAILED, "Signature/MAC verification failed");
+        sErrorCodeToString.put(KM_ERROR_TOO_MANY_OPERATIONS, "Too many operations");
+        sErrorCodeToString.put(KM_ERROR_INVALID_KEY_BLOB, "Invalid key blob");
+        sErrorCodeToString.put(KM_ERROR_INVALID_ARGUMENT, "Invalid argument");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_TAG, "Unsupported tag");
+        sErrorCodeToString.put(KM_ERROR_INVALID_TAG, "Invalid tag");
+        sErrorCodeToString.put(KM_ERROR_MEMORY_ALLOCATION_FAILED, "Memory allocation failed");
+        sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_EC_FIELD, "Unsupported EC field");
+        sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented");
+        sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error");
+    }
+
     public static int getTagType(int tag) {
         return tag & (0xF << 28);
     }
+
+    public static String getErrorMessage(int errorCode) {
+        String result = sErrorCodeToString.get(errorCode);
+        if (result != null) {
+            return result;
+        }
+        return String.valueOf(errorCode);
+    }
 }
diff --git a/core/java/android/security/keymaster/KeymasterIntArgument.java b/core/java/android/security/keymaster/KeymasterIntArgument.java
index c3738d7..da81715 100644
--- a/core/java/android/security/keymaster/KeymasterIntArgument.java
+++ b/core/java/android/security/keymaster/KeymasterIntArgument.java
@@ -26,6 +26,15 @@
 
     public KeymasterIntArgument(int tag, int value) {
         super(tag);
+        switch (KeymasterDefs.getTagType(tag)) {
+            case KeymasterDefs.KM_INT:
+            case KeymasterDefs.KM_INT_REP:
+            case KeymasterDefs.KM_ENUM:
+            case KeymasterDefs.KM_ENUM_REP:
+                break; // OK.
+            default:
+                throw new IllegalArgumentException("Bad int tag " + tag);
+        }
         this.value = value;
     }
 
diff --git a/core/java/android/security/keymaster/KeymasterLongArgument.java b/core/java/android/security/keymaster/KeymasterLongArgument.java
index 3c565b8..9d2be09 100644
--- a/core/java/android/security/keymaster/KeymasterLongArgument.java
+++ b/core/java/android/security/keymaster/KeymasterLongArgument.java
@@ -26,6 +26,12 @@
 
     public KeymasterLongArgument(int tag, long value) {
         super(tag);
+        switch (KeymasterDefs.getTagType(tag)) {
+            case KeymasterDefs.KM_LONG:
+                break; // OK.
+            default:
+                throw new IllegalArgumentException("Bad long tag " + tag);
+        }
         this.value = value;
     }
 
diff --git a/core/java/android/security/keymaster/OperationResult.java b/core/java/android/security/keymaster/OperationResult.java
index 4fc9d24..9b46ad3 100644
--- a/core/java/android/security/keymaster/OperationResult.java
+++ b/core/java/android/security/keymaster/OperationResult.java
@@ -28,6 +28,7 @@
 public class OperationResult implements Parcelable {
     public final int resultCode;
     public final IBinder token;
+    public final long operationHandle;
     public final int inputConsumed;
     public final byte[] output;
 
@@ -45,6 +46,7 @@
     protected OperationResult(Parcel in) {
         resultCode = in.readInt();
         token = in.readStrongBinder();
+        operationHandle = in.readLong();
         inputConsumed = in.readInt();
         output = in.createByteArray();
     }
@@ -58,6 +60,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(resultCode);
         out.writeStrongBinder(token);
+        out.writeLong(operationHandle);
         out.writeInt(inputConsumed);
         out.writeByteArray(output);
     }
diff --git a/core/java/android/service/fingerprint/FingerprintManager.java b/core/java/android/service/fingerprint/FingerprintManager.java
deleted file mode 100644
index 6375668..0000000
--- a/core/java/android/service/fingerprint/FingerprintManager.java
+++ /dev/null
@@ -1,313 +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 android.service.fingerprint;
-
-import android.app.ActivityManagerNative;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.util.Log;
-import android.util.Slog;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A class that coordinates access to the fingerprint hardware.
- * @hide
- */
-
-public class FingerprintManager {
-    private static final String TAG = "FingerprintManager";
-    private static final boolean DEBUG = true;
-    private static final int MSG_ENROLL_RESULT = 100;
-    private static final int MSG_ACQUIRED = 101;
-    private static final int MSG_PROCESSED = 102;
-    private static final int MSG_ERROR = 103;
-    private static final int MSG_REMOVED = 104;
-
-    // Errors generated by layers above HAL
-    public static final int FINGERPRINT_ERROR_NO_RECEIVER = -10;
-
-    // Message types.  Must agree with HAL (fingerprint.h)
-    public static final int FINGERPRINT_ERROR = -1;
-    public static final int FINGERPRINT_ACQUIRED = 1;
-    public static final int FINGERPRINT_PROCESSED = 2;
-    public static final int FINGERPRINT_TEMPLATE_ENROLLING = 3;
-    public static final int FINGERPRINT_TEMPLATE_REMOVED = 4;
-
-    // Error messages. Must agree with HAL (fingerprint.h)
-    public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
-    public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
-    public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
-    public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
-
-    // FINGERPRINT_ACQUIRED messages.  Must agree with HAL (fingerprint.h)
-    public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
-    public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
-    public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
-    public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 4;
-    public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 8;
-    public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 16;
-
-    private IFingerprintService mService;
-    private FingerprintManagerReceiver mClientReceiver;
-    private Context mContext;
-    private IBinder mToken = new Binder();
-
-    private Handler mHandler = new Handler() {
-        public void handleMessage(android.os.Message msg) {
-            if (mClientReceiver != null) {
-                switch(msg.what) {
-                    case MSG_ENROLL_RESULT:
-                        mClientReceiver.onEnrollResult(msg.arg1, msg.arg2);
-                        break;
-                    case MSG_ACQUIRED:
-                        mClientReceiver.onAcquired(msg.arg1);
-                        break;
-                    case MSG_PROCESSED:
-                        mClientReceiver.onProcessed(msg.arg1);
-                        break;
-                    case MSG_ERROR:
-                        mClientReceiver.onError(msg.arg1);
-                        break;
-                    case MSG_REMOVED:
-                        mClientReceiver.onRemoved(msg.arg1);
-                }
-            }
-        }
-    };
-
-    public static final class FingerprintItem {
-        public CharSequence name;
-        public int id;
-        FingerprintItem(CharSequence name, int id) {
-            this.name = name;
-            this.id = id;
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public FingerprintManager(Context context, IFingerprintService service) {
-        mContext = context;
-        mService = service;
-        if (mService == null) {
-            Slog.v(TAG, "FingerprintManagerService was null");
-        }
-    }
-
-    private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
-
-        public void onEnrollResult(int fingerprintId,  int remaining) {
-            mHandler.obtainMessage(MSG_ENROLL_RESULT, fingerprintId, remaining).sendToTarget();
-        }
-
-        public void onAcquired(int acquireInfo) {
-            mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, 0).sendToTarget();
-        }
-
-        public void onProcessed(int fingerprintId) {
-            mHandler.obtainMessage(MSG_PROCESSED, fingerprintId, 0).sendToTarget();
-        }
-
-        public void onError(int error) {
-            mHandler.obtainMessage(MSG_ERROR, error, 0).sendToTarget();
-        }
-
-        public void onRemoved(int fingerprintId) {
-            mHandler.obtainMessage(MSG_REMOVED, fingerprintId, 0).sendToTarget();
-        }
-    };
-
-    /**
-     * Determine whether the user has at least one fingerprint enrolled and enabled.
-     *
-     * @return true if at least one is enrolled and enabled
-     */
-    public boolean enrolledAndEnabled() {
-        ContentResolver res = mContext.getContentResolver();
-        return Settings.Secure.getInt(res, "fingerprint_enabled", 0) != 0
-                && FingerprintUtils.getFingerprintIdsForUser(res, getCurrentUserId()).length > 0;
-    }
-
-    /**
-     * Start the enrollment process.  Timeout dictates how long to wait for the user to
-     * enroll a fingerprint.
-     *
-     * @param timeout
-     */
-    public void enroll(long timeout) {
-        if (mServiceReceiver == null) {
-            sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
-            return;
-        }
-        if (mService != null) try {
-            mService.enroll(mToken, timeout, getCurrentUserId());
-        } catch (RemoteException e) {
-            Log.v(TAG, "Remote exception while enrolling: ", e);
-            sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
-        }
-    }
-
-    /**
-     * Remove the given fingerprintId from the system.  FingerprintId of 0 has special meaning
-     * which is to delete all fingerprint data for the current user. Use with caution.
-     * @param fingerprintId
-     */
-    public void remove(int fingerprintId) {
-        if (mServiceReceiver == null) {
-            sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
-            return;
-        }
-        if (mService != null) {
-            try {
-                mService.remove(mToken, fingerprintId, getCurrentUserId());
-            } catch (RemoteException e) {
-                Log.v(TAG, "Remote exception during remove of fingerprintId: " + fingerprintId, e);
-            }
-        } else {
-            Log.w(TAG, "remove(): Service not connected!");
-            sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
-        }
-    }
-
-    /**
-     * Starts listening for fingerprint events.  When a finger is scanned or recognized, the
-     * client will be notified via the callback.
-     */
-    public void startListening(FingerprintManagerReceiver receiver) {
-        mClientReceiver = receiver;
-        if (mService != null) {
-            try {
-                mService.startListening(mToken, mServiceReceiver, getCurrentUserId());
-            } catch (RemoteException e) {
-                Log.v(TAG, "Remote exception in startListening(): ", e);
-            }
-        } else {
-            Log.w(TAG, "startListening(): Service not connected!");
-            sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
-        }
-    }
-
-    private int getCurrentUserId() {
-        try {
-            return ActivityManagerNative.getDefault().getCurrentUser().id;
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed to get current user id\n");
-            return UserHandle.USER_NULL;
-        }
-    }
-
-    /**
-     * Stops the client from listening to fingerprint events.
-     */
-    public void stopListening() {
-        if (mService != null) {
-            try {
-                mService.stopListening(mToken, getCurrentUserId());
-                mClientReceiver = null;
-            } catch (RemoteException e) {
-                Log.v(TAG, "Remote exception in stopListening(): ", e);
-            }
-        } else {
-            Log.w(TAG, "stopListening(): Service not connected!");
-            sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
-        }
-    }
-
-    public void enrollCancel() {
-        if (mServiceReceiver == null) {
-            sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0);
-            return;
-        }
-        if (mService != null) {
-            try {
-                mService.enrollCancel(mToken, getCurrentUserId());
-                mClientReceiver = null;
-            } catch (RemoteException e) {
-                Log.v(TAG, "Remote exception in enrollCancel(): ", e);
-                sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0);
-            }
-        } else {
-            Log.w(TAG, "enrollCancel(): Service not connected!");
-        }
-    }
-
-    private void sendError(int msg, int arg1, int arg2) {
-        mHandler.obtainMessage(msg, arg1, arg2);
-    }
-
-    /**
-     * @return list of current fingerprint items
-     * @hide
-     */
-    public List<FingerprintItem> getEnrolledFingerprints() {
-        int[] ids = FingerprintUtils.getFingerprintIdsForUser(mContext.getContentResolver(),
-                getCurrentUserId());
-        List<FingerprintItem> result = new ArrayList<FingerprintItem>();
-        for (int i = 0; i < ids.length; i++) {
-            // TODO: persist names in Settings
-            FingerprintItem item = new FingerprintItem("Finger" + ids[i], ids[i]);
-            result.add(item);
-        }
-        return result;
-    }
-
-    /**
-     * Determine if fingerprint hardware is present and functional.
-     * @return true if hardware is present and functional, false otherwise.
-     * @hide
-     */
-    public boolean isHardwareDetected() {
-        if (mService != null) {
-            try {
-                return mService.isHardwareDetected();
-            } catch (RemoteException e) {
-                Log.v(TAG, "Remote exception in isFingerprintHardwareDetected(): ", e);
-            }
-        } else {
-            Log.w(TAG, "isFingerprintHardwareDetected(): Service not connected!");
-        }
-        return false;
-    }
-
-    /**
-     * Renames the given fingerprint template
-     * @param fpId the fingerprint id
-     * @param newName the new name
-     * @hide
-     */
-    public void rename(int fpId, String newName) {
-        // Renames the given fpId
-        if (mService != null) {
-            try {
-                mService.rename(fpId, newName);
-            } catch (RemoteException e) {
-                Log.v(TAG, "Remote exception in rename(): ", e);
-            }
-        } else {
-            Log.w(TAG, "rename(): Service not connected!");
-        }
-    }
-}
\ No newline at end of file
diff --git a/core/java/android/service/fingerprint/FingerprintManagerReceiver.java b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
deleted file mode 100644
index 85677ba..0000000
--- a/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package android.service.fingerprint;
-/**
- * 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.
- */
-
-/**
- * @hide
- */
-public class FingerprintManagerReceiver {
-    /**
-     * Fingerprint enrollment progress update. Enrollment is considered complete if
-     * remaining hits 0 without {@link #onError(int)} being called.
-     *
-     * @param fingerprintId the fingerprint we're currently enrolling
-     * @param remaining the number of samples required to complete enrollment. It's up to
-     * the hardware to define what each step in enrollment means. Some hardware
-     * requires multiple samples of the same part of the finger.  Others require sampling of
-     * different parts of the finger.  The enrollment flow can use remaining to
-     * mean "step x" of the process or "just need another sample."
-     */
-    public void onEnrollResult(int fingerprintId,  int remaining) { }
-
-    /**
-     * Fingerprint touch detected, but not processed yet. Clients will use this message to
-     * determine a good or bad scan before the fingerprint is processed.  This is meant for the
-     * client to provide feedback about the scan or alert the user that recognition is to follow.
-     *
-     * @param acquiredInfo one of:
-     * {@link FingerprintManager#FINGERPRINT_ACQUIRED_GOOD},
-     * {@link FingerprintManager#FINGERPRINT_ACQUIRED_PARTIAL},
-     * {@link FingerprintManager#FINGERPRINT_ACQUIRED_INSUFFICIENT},
-     * {@link FingerprintManager#FINGERPRINT_ACQUIRED_IMAGER_DIRTY},
-     * {@link FingerprintManager#FINGERPRINT_ACQUIRED_TOO_SLOW},
-     * {@link FingerprintManager#FINGERPRINT_ACQUIRED_TOO_FAST}
-     */
-    public void onAcquired(int acquiredInfo) { }
-
-    /**
-     * Fingerprint has been detected and processed.  A non-zero return indicates a valid
-     * fingerprint was detected.
-     *
-     * @param fingerprintId the finger id, or 0 if not recognized.
-     */
-    public void onProcessed(int fingerprintId) { }
-
-    /**
-     * An error was detected during scan or enrollment.  One of
-     * {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE},
-     * {@link FingerprintManager#FINGERPRINT_ERROR_UNABLE_TO_PROCESS} or
-     * {@link FingerprintManager#FINGERPRINT_ERROR_TIMEOUT}
-     * {@link FingerprintManager#FINGERPRINT_ERROR_NO_SPACE}
-     *
-     * @param error one of the above error codes
-     */
-    public void onError(int error) { }
-
-    /**
-     * The given fingerprint template was successfully removed by the driver.
-     * See {@link FingerprintManager#remove(int)}
-     *
-     * @param fingerprintId id of template to remove.
-     */
-    public void onRemoved(int fingerprintId) { }
-}
\ No newline at end of file
diff --git a/core/java/android/service/fingerprint/IFingerprintService.aidl b/core/java/android/service/fingerprint/IFingerprintService.aidl
deleted file mode 100644
index 9b4750b..0000000
--- a/core/java/android/service/fingerprint/IFingerprintService.aidl
+++ /dev/null
@@ -1,47 +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 android.service.fingerprint;
-
-import android.os.Bundle;
-import android.service.fingerprint.IFingerprintServiceReceiver;
-
-/**
- * Communication channel from client to the fingerprint service.
- * @hide
- */
-interface IFingerprintService {
-    // Any errors resulting from this call will be returned to the listener
-    void enroll(IBinder token, long timeout, int userId);
-
-    // Any errors resulting from this call will be returned to the listener
-    void enrollCancel(IBinder token, int userId);
-
-    // Any errors resulting from this call will be returned to the listener
-    void remove(IBinder token, int fingerprintId, int userId);
-
-    // Start listening for fingerprint events.  This has the side effect of starting
-    // the hardware if not already started.
-    void startListening(IBinder token, IFingerprintServiceReceiver receiver, int userId);
-
-    // Stops listening for fingerprints
-    void stopListening(IBinder token, int userId);
-
-    // Determine if HAL is loaded and ready
-    boolean isHardwareDetected();
-
-    // Rename the given fingerprint id
-    void rename(int fpId, String name);
-}
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 7a5bb90..3245f55 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -512,11 +512,7 @@
 
     Request removeRequest(IBinder reqInterface) {
         synchronized (this) {
-            Request req = mActiveRequests.get(reqInterface);
-            if (req != null) {
-                mActiveRequests.remove(req);
-            }
-            return req;
+            return mActiveRequests.remove(reqInterface);
         }
     }
 
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 4902a71..1674950 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -17,15 +17,12 @@
 package android.service.wallpaper;
 
 import android.content.res.TypedArray;
-import android.os.Build;
 import android.os.SystemProperties;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
-import android.view.ViewRootImpl;
 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;
 
@@ -158,7 +155,6 @@
                 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
         int mCurWindowFlags = mWindowFlags;
         int mCurWindowPrivateFlags = mWindowPrivateFlags;
-        TypedValue mOutsetBottom;
         final Rect mVisibleInsets = new Rect();
         final Rect mWinFrame = new Rect();
         final Rect mOverscanInsets = new Rect();
@@ -171,8 +167,6 @@
         final Rect mFinalStableInsets = new Rect();
         final Configuration mConfiguration = new Configuration();
 
-        private boolean mIsEmulator;
-        private boolean mIsCircularEmulator;
         private boolean mWindowIsRound;
 
         final WindowManager.LayoutParams mLayout
@@ -629,31 +623,12 @@
                     mLayout.token = mWindowToken;
 
                     if (!mCreated) {
-                        // Retrieve watch round and outset info
-                        final WindowManager windowService = (WindowManager)getSystemService(
-                                Context.WINDOW_SERVICE);
+                        // Retrieve watch round info
                         TypedArray windowStyle = obtainStyledAttributes(
                                 com.android.internal.R.styleable.Window);
-                        final Display display = windowService.getDefaultDisplay();
-                        final boolean shouldUseBottomOutset =
-                                display.getDisplayId() == Display.DEFAULT_DISPLAY;
-                        if (shouldUseBottomOutset && windowStyle.hasValue(
-                                R.styleable.Window_windowOutsetBottom)) {
-                            if (mOutsetBottom == null) mOutsetBottom = new TypedValue();
-                            windowStyle.getValue(R.styleable.Window_windowOutsetBottom,
-                                    mOutsetBottom);
-                        } else {
-                            mOutsetBottom = null;
-                        }
-                        mWindowIsRound = getResources().getBoolean(
-                                com.android.internal.R.bool.config_windowIsRound);
+                        mWindowIsRound = ScreenShapeHelper.getWindowIsRound(getResources());
                         windowStyle.recycle();
 
-                        // detect emulator
-                        mIsEmulator = Build.HARDWARE.contains("goldfish");
-                        mIsCircularEmulator = SystemProperties.getBoolean(
-                                ViewRootImpl.PROPERTY_EMULATOR_CIRCULAR, false);
-
                         // Add window
                         mLayout.type = mIWallpaperEngine.mWindowType;
                         mLayout.gravity = Gravity.START|Gravity.TOP;
@@ -783,18 +758,11 @@
                             mDispatchedOverscanInsets.set(mOverscanInsets);
                             mDispatchedContentInsets.set(mContentInsets);
                             mDispatchedStableInsets.set(mStableInsets);
-                            final boolean isRound = (mIsEmulator && mIsCircularEmulator)
-                                    || mWindowIsRound;
                             mFinalSystemInsets.set(mDispatchedOverscanInsets);
                             mFinalStableInsets.set(mDispatchedStableInsets);
-                            if (mOutsetBottom != null) {
-                                final DisplayMetrics metrics = getResources().getDisplayMetrics();
-                                mFinalSystemInsets.bottom =
-                                        ( (int) mOutsetBottom.getDimension(metrics) )
-                                        + mIWallpaperEngine.mDisplayPadding.bottom;
-                            }
+                            mFinalSystemInsets.bottom = mIWallpaperEngine.mDisplayPadding.bottom;
                             WindowInsets insets = new WindowInsets(mFinalSystemInsets,
-                                    null, mFinalStableInsets, isRound);
+                                    null, mFinalStableInsets, mWindowIsRound);
                             onApplyWindowInsets(insets);
                         }
 
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 1bdaef0..239b386 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -79,7 +79,8 @@
                          boolean includepad,
                          TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
         this(base, display, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR,
-                spacingmult, spacingadd, includepad, ellipsize, ellipsizedWidth);
+                spacingmult, spacingadd, includepad, StaticLayout.BREAK_STRATEGY_SIMPLE,
+                ellipsize, ellipsizedWidth);
     }
 
     /**
@@ -95,7 +96,7 @@
                          TextPaint paint,
                          int width, Alignment align, TextDirectionHeuristic textDir,
                          float spacingmult, float spacingadd,
-                         boolean includepad,
+                         boolean includepad, int breakStrategy,
                          TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
         super((ellipsize == null)
                 ? display
@@ -120,6 +121,7 @@
         mObjects = new PackedObjectVector<Directions>(1);
 
         mIncludePad = includepad;
+        mBreakStrategy = breakStrategy;
 
         /*
          * This is annoying, but we can't refer to the layout until
@@ -279,10 +281,9 @@
             sBuilder = null;
         }
 
-        // TODO: make sure reflowed is properly initialized
         if (reflowed == null) {
             reflowed = new StaticLayout(null);
-            b = StaticLayout.Builder.obtain();
+            b = StaticLayout.Builder.obtain(text, where, where + after, getWidth());
         }
 
         b.setText(text, where, where + after)
@@ -292,7 +293,8 @@
                 .setSpacingMult(getSpacingMultiplier())
                 .setSpacingAdd(getSpacingAdd())
                 .setEllipsizedWidth(mEllipsizedWidth)
-                .setEllipsize(mEllipsizeAt);
+                .setEllipsize(mEllipsizeAt)
+                .setBreakStrategy(mBreakStrategy);
         reflowed.generate(b, false, true);
         int n = reflowed.getLineCount();
 
@@ -356,6 +358,8 @@
             ints[DESCENT] = desc;
             objects[0] = reflowed.getLineDirections(i);
 
+            ints[HYPHEN] = reflowed.getHyphen(i);
+
             if (mEllipsize) {
                 ints[ELLIPSIS_START] = reflowed.getEllipsisStart(i);
                 ints[ELLIPSIS_COUNT] = reflowed.getEllipsisCount(i);
@@ -631,6 +635,14 @@
         return mBottomPadding;
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public int getHyphen(int line) {
+        return mInts.getValue(line, HYPHEN);
+    }
+
     @Override
     public int getEllipsizedWidth() {
         return mEllipsizedWidth;
@@ -707,6 +719,7 @@
     private boolean mEllipsize;
     private int mEllipsizedWidth;
     private TextUtils.TruncateAt mEllipsizeAt;
+    private int mBreakStrategy;
 
     private PackedIntVector mInts;
     private PackedObjectVector<Directions> mObjects;
@@ -739,11 +752,12 @@
     private static final int TAB = START;
     private static final int TOP = 1;
     private static final int DESCENT = 2;
-    private static final int COLUMNS_NORMAL = 3;
+    private static final int HYPHEN = 3;
+    private static final int COLUMNS_NORMAL = 4;
 
-    private static final int ELLIPSIS_START = 3;
-    private static final int ELLIPSIS_COUNT = 4;
-    private static final int COLUMNS_ELLIPSIZE = 5;
+    private static final int ELLIPSIS_START = 4;
+    private static final int ELLIPSIS_COUNT = 5;
+    private static final int COLUMNS_ELLIPSIZE = 6;
 
     private static final int START_MASK = 0x1FFFFFFF;
     private static final int DIR_SHIFT  = 30;
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index 8cf1b4b..7bebbfb 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -244,13 +244,18 @@
                 next++;
             }
 
-            withinParagraph(out, text, i, next - nl, nl, next == end);
+            if (withinParagraph(out, text, i, next - nl, nl, next == end)) {
+                /* Paragraph should be closed */
+                out.append("</p>\n");
+                out.append(getOpenParaTagWithDirection(text, next, end));
+            }
         }
 
         out.append("</p>\n");
     }
 
-    private static void withinParagraph(StringBuilder out, Spanned text,
+    /* Returns true if the caller should close and reopen the paragraph. */
+    private static boolean withinParagraph(StringBuilder out, Spanned text,
                                         int start, int end, int nl,
                                         boolean last) {
         int next;
@@ -363,17 +368,14 @@
             }
         }
 
-        String p = last ? "" : "</p>\n" + getOpenParaTagWithDirection(text, start, end);
-
         if (nl == 1) {
             out.append("<br>\n");
-        } else if (nl == 2) {
-            out.append(p);
+            return false;
         } else {
             for (int i = 2; i < nl; i++) {
                 out.append("<br>");
             }
-            out.append(p);
+            return !last;
         }
     }
 
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
new file mode 100644
index 0000000..f4dff9b
--- /dev/null
+++ b/core/java/android/text/Hyphenator.java
@@ -0,0 +1,76 @@
+/*
+ * 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 android.text;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.HashMap;
+import java.util.Locale;
+
+/**
+ * Hyphenator is a wrapper class for a native implementation of automatic hyphenation,
+ * in essence finding valid hyphenation opportunities in a word.
+ *
+ * @hide
+ */
+/* package */ class Hyphenator {
+    // This class has deliberately simple lifetime management (no finalizer) because in
+    // the common case a process will use a very small number of locales.
+
+    private static String TAG = "Hyphenator";
+
+    static HashMap<Locale, Hyphenator> sMap = new HashMap<Locale, Hyphenator>();
+
+    private long mNativePtr;
+
+    private Hyphenator(long nativePtr) {
+        mNativePtr = nativePtr;
+    }
+
+    public static long get(Locale locale) {
+        synchronized (sMap) {
+            Hyphenator result = sMap.get(locale);
+            if (result == null) {
+                result = loadHyphenator(locale);
+                sMap.put(locale, result);
+            }
+            return result == null ? 0 : result.mNativePtr;
+        }
+    }
+
+    private static Hyphenator loadHyphenator(Locale locale) {
+        // TODO: find pattern dictionary (from system location) that best matches locale
+        if (Locale.US.equals(locale)) {
+            File f = new File("/data/local/tmp/hyph-en-us.pat.txt");
+            try {
+                RandomAccessFile rf = new RandomAccessFile(f, "r");
+                byte[] buf = new byte[(int)rf.length()];
+                rf.read(buf);
+                rf.close();
+                String patternData = new String(buf);
+                long nativePtr = StaticLayout.nLoadHyphenator(patternData);
+                return new Hyphenator(nativePtr);
+            } catch (IOException e) {
+                Log.e(TAG, "error loading hyphenation " + f);
+            }
+        }
+        return null;
+    }
+}
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index fcf1828..16ae5e2 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -16,6 +16,7 @@
 
 package android.text;
 
+import android.annotation.IntDef;
 import android.emoji.EmojiFactory;
 import android.graphics.Canvas;
 import android.graphics.Paint;
@@ -33,6 +34,8 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.GrowingArrayUtils;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 
 /**
@@ -43,6 +46,31 @@
  * For text that will not change, use a {@link StaticLayout}.
  */
 public abstract class Layout {
+    /** @hide */
+    @IntDef({BREAK_STRATEGY_SIMPLE, BREAK_STRATEGY_HIGH_QUALITY, BREAK_STRATEGY_BALANCED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BreakStrategy {}
+
+    /**
+     * Value for break strategy indicating simple line breaking. Automatic hyphens are not added
+     * (though soft hyphens are respected), and modifying text generally doesn't affect the layout
+     * before it (which yields a more consistent user experience when editing), but layout may not
+     * be the highest quality.
+     */
+    public static final int BREAK_STRATEGY_SIMPLE = 0;
+
+    /**
+     * Value for break strategy indicating high quality line breaking, including automatic
+     * hyphenation and doing whole-paragraph optimization of line breaks.
+     */
+    public static final int BREAK_STRATEGY_HIGH_QUALITY = 1;
+
+    /**
+     * Value for break strategy indicating balanced line breaking. The breaks are chosen to
+     * make all lines as close to the same length as possible, including automatic hyphenation.
+     */
+    public static final int BREAK_STRATEGY_BALANCED = 2;
+
     private static final ParagraphStyle[] NO_PARA_SPANS =
         ArrayUtils.emptyArray(ParagraphStyle.class);
 
@@ -225,17 +253,17 @@
 
         // Draw the lines, one at a time.
         // The baseline is the top of the following line minus the current line's descent.
-        for (int i = firstLine; i <= lastLine; i++) {
+        for (int lineNum = firstLine; lineNum <= lastLine; lineNum++) {
             int start = previousLineEnd;
-            previousLineEnd = getLineStart(i + 1);
-            int end = getLineVisibleEnd(i, start, previousLineEnd);
+            previousLineEnd = getLineStart(lineNum + 1);
+            int end = getLineVisibleEnd(lineNum, start, previousLineEnd);
 
             int ltop = previousLineBottom;
-            int lbottom = getLineTop(i+1);
+            int lbottom = getLineTop(lineNum + 1);
             previousLineBottom = lbottom;
-            int lbaseline = lbottom - getLineDescent(i);
+            int lbaseline = lbottom - getLineDescent(lineNum);
 
-            int dir = getParagraphDirection(i);
+            int dir = getParagraphDirection(lineNum);
             int left = 0;
             int right = mWidth;
 
@@ -254,7 +282,7 @@
                 // just collect the ones present at the start of the paragraph.
                 // If spanEnd is before the end of the paragraph, that's not
                 // our problem.
-                if (start >= spanEnd && (i == firstLine || isFirstParaLine)) {
+                if (start >= spanEnd && (lineNum == firstLine || isFirstParaLine)) {
                     spanEnd = sp.nextSpanTransition(start, textLength,
                                                     ParagraphStyle.class);
                     spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class);
@@ -280,7 +308,7 @@
                         int startLine = getLineForOffset(sp.getSpanStart(spans[n]));
                         // if there is more than one LeadingMarginSpan2, use
                         // the count that is greatest
-                        if (i < startLine + count) {
+                        if (lineNum < startLine + count) {
                             useFirstLineMargin = true;
                             break;
                         }
@@ -304,7 +332,7 @@
                 }
             }
 
-            boolean hasTabOrEmoji = getLineContainsTab(i);
+            boolean hasTabOrEmoji = getLineContainsTab(lineNum);
             // Can't tell if we have tabs for sure, currently
             if (hasTabOrEmoji && !tabStopsIsInitialized) {
                 if (tabStops == null) {
@@ -333,7 +361,7 @@
                     x = right;
                 }
             } else {
-                int max = (int)getLineExtent(i, tabStops, false);
+                int max = (int)getLineExtent(lineNum, tabStops, false);
                 if (align == Alignment.ALIGN_OPPOSITE) {
                     if (dir == DIR_LEFT_TO_RIGHT) {
                         x = right - max;
@@ -346,7 +374,8 @@
                 }
             }
 
-            Directions directions = getLineDirections(i);
+            paint.setHyphenEdit(getHyphen(lineNum));
+            Directions directions = getLineDirections(lineNum);
             if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTabOrEmoji) {
                 // XXX: assumes there's nothing additional to be done
                 canvas.drawText(buf, start, end, x, lbaseline, paint);
@@ -677,6 +706,15 @@
      */
     public abstract int getBottomPadding();
 
+    /**
+     * Returns the hyphen edit for a line.
+     *
+     * @hide
+     */
+    public int getHyphen(int line) {
+        return 0;
+    }
+
 
     /**
      * Returns true if the character at offset and the preceding character
@@ -1153,7 +1191,10 @@
                 return end - 1;
             }
 
-            if (ch != ' ' && ch != '\t') {
+            // Note: keep this in sync with Minikin LineBreaker::isLineEndSpace()
+            if (!(ch == ' ' || ch == '\t' || ch == 0x1680 ||
+                    (0x2000 <= ch && ch <= 0x200A && ch != 0x2007) ||
+                    ch == 0x205F || ch == 0x3000)) {
                 break;
             }
 
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index ee39e27..2bcb352 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -23,6 +23,7 @@
 import android.text.style.MetricAffectingSpan;
 import android.text.style.TabStopSpan;
 import android.util.Log;
+import android.util.Pools.SynchronizedPool;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.GrowingArrayUtils;
@@ -56,28 +57,23 @@
             mNativePtr = nNewBuilder();
         }
 
-        static Builder obtain() {
-            Builder b = null;
-            synchronized (sLock) {
-                for (int i = 0; i < sCached.length; i++) {
-                    if (sCached[i] != null) {
-                        b = sCached[i];
-                        sCached[i] = null;
-                        break;
-                    }
-                }
-            }
+        public static Builder obtain(CharSequence source, int start, int end, int width) {
+            Builder b = sPool.acquire();
             if (b == null) {
                 b = new Builder();
             }
 
             // set default initial values
-            b.mWidth = 0;
+            b.mText = source;
+            b.mStart = start;
+            b.mEnd = end;
+            b.mWidth = width;
+            b.mAlignment = Alignment.ALIGN_NORMAL;
             b.mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
             b.mSpacingMult = 1.0f;
             b.mSpacingAdd = 0.0f;
             b.mIncludePad = true;
-            b.mEllipsizedWidth = 0;
+            b.mEllipsizedWidth = width;
             b.mEllipsize = null;
             b.mMaxLines = Integer.MAX_VALUE;
 
@@ -85,18 +81,11 @@
             return b;
         }
 
-        static void recycle(Builder b) {
+        private static void recycle(Builder b) {
             b.mPaint = null;
             b.mText = null;
             MeasuredText.recycle(b.mMeasuredText);
-            synchronized (sLock) {
-                for (int i = 0; i < sCached.length; i++) {
-                    if (sCached[i] == null) {
-                        sCached[i] = b;
-                        break;
-                    }
-                }
-            }
+            sPool.release(b);
         }
 
         // release any expensive state
@@ -129,6 +118,11 @@
             return this;
         }
 
+        public Builder setAlignment(Alignment alignment) {
+            mAlignment = alignment;
+            return this;
+        }
+
         public Builder setTextDir(TextDirectionHeuristic textDir) {
             mTextDir = textDir;
             return this;
@@ -166,12 +160,19 @@
             return this;
         }
 
+        public Builder setBreakStrategy(@BreakStrategy int breakStrategy) {
+            mBreakStrategy = breakStrategy;
+            return this;
+        }
+
         /**
          * Measurement and break iteration is done in native code. The protocol for using
          * the native code is as follows.
          *
-         * For each paragraph, do a nSetText of the paragraph text. Then, for each run within the
-         * paragraph:
+         * For each paragraph, do a nSetupParagraph, which sets paragraph text, line width, tab
+         * stops, break strategy (and possibly other parameters in the future).
+         *
+         * Then, for each run within the paragraph:
          *  - setLocale (this must be done at least for the first run, optional afterwards)
          *  - one of the following, depending on the type of run:
          *    + addStyleRun (a text run, to be measured in native code)
@@ -186,7 +187,7 @@
 
         private void setLocale(Locale locale) {
             if (!locale.equals(mLocale)) {
-                nSetLocale(mNativePtr, locale.toLanguageTag());
+                nSetLocale(mNativePtr, locale.toLanguageTag(), Hyphenator.get(locale));
                 mLocale = locale;
             }
         }
@@ -205,10 +206,8 @@
         }
 
         public StaticLayout build() {
-            // TODO: can optimize based on whether ellipsis is needed
-            StaticLayout result = new StaticLayout(mText);
-            result.generate(this, this.mIncludePad, this.mIncludePad);
-            recycle(this);
+            StaticLayout result = new StaticLayout(this);
+            Builder.recycle(this);
             return result;
         }
 
@@ -228,6 +227,7 @@
         int mEnd;
         TextPaint mPaint;
         int mWidth;
+        Alignment mAlignment;
         TextDirectionHeuristic mTextDir;
         float mSpacingMult;
         float mSpacingAdd;
@@ -235,6 +235,7 @@
         int mEllipsizedWidth;
         TextUtils.TruncateAt mEllipsize;
         int mMaxLines;
+        int mBreakStrategy;
 
         Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
 
@@ -243,8 +244,7 @@
 
         Locale mLocale;
 
-        private static final Object sLock = new Object();
-        private static final Builder[] sCached = new Builder[3];
+        private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<Builder>(3);
     }
 
     public StaticLayout(CharSequence source, TextPaint paint,
@@ -314,10 +314,9 @@
                     : new Ellipsizer(source),
               paint, outerwidth, align, textDir, spacingmult, spacingadd);
 
-        Builder b = Builder.obtain();
-        b.setText(source, bufstart, bufend)
+        Builder b = Builder.obtain(source, bufstart, bufend, outerwidth)
             .setPaint(paint)
-            .setWidth(outerwidth)
+            .setAlignment(align)
             .setTextDir(textDir)
             .setSpacingMult(spacingmult)
             .setSpacingAdd(spacingadd)
@@ -364,6 +363,35 @@
         mLines = new int[mLineDirections.length];
     }
 
+    private StaticLayout(Builder b) {
+        super((b.mEllipsize == null)
+                ? b.mText
+                : (b.mText instanceof Spanned)
+                    ? new SpannedEllipsizer(b.mText)
+                    : new Ellipsizer(b.mText),
+                b.mPaint, b.mWidth, b.mAlignment, b.mSpacingMult, b.mSpacingAdd);
+
+        if (b.mEllipsize != null) {
+            Ellipsizer e = (Ellipsizer) getText();
+
+            e.mLayout = this;
+            e.mWidth = b.mEllipsizedWidth;
+            e.mMethod = b.mEllipsize;
+            mEllipsizedWidth = b.mEllipsizedWidth;
+
+            mColumns = COLUMNS_ELLIPSIZE;
+        } else {
+            mColumns = COLUMNS_NORMAL;
+            mEllipsizedWidth = b.mWidth;
+        }
+
+        mLineDirections = ArrayUtils.newUnpaddedArray(Directions.class, 2 * mColumns);
+        mLines = new int[mLineDirections.length];
+        mMaximumVisibleLineCount = b.mMaxLines;
+
+        generate(b, b.mIncludePad, b.mIncludePad);
+    }
+
     /* package */ void generate(Builder b, boolean includepad, boolean trackpad) {
         CharSequence source = b.mText;
         int bufStart = b.mStart;
@@ -459,7 +487,25 @@
             byte[] chdirs = measured.mLevels;
             int dir = measured.mDir;
             boolean easy = measured.mEasy;
-            nSetText(b.mNativePtr, chs, paraEnd - paraStart);
+
+            // tab stop locations
+            int[] variableTabStops = null;
+            if (spanned != null) {
+                TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
+                        paraEnd, TabStopSpan.class);
+                if (spans.length > 0) {
+                    int[] stops = new int[spans.length];
+                    for (int i = 0; i < spans.length; i++) {
+                        stops[i] = spans[i].getTabStop();
+                    }
+                    Arrays.sort(stops, 0, stops.length);
+                    variableTabStops = stops;
+                }
+            }
+
+            nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart,
+                    firstWidth, firstWidthLineCount, restWidth,
+                    variableTabStops, TAB_INCREMENT, b.mBreakStrategy);
 
             // measurement has to be done before performing line breaking
             // but we don't want to recompute fontmetrics or span ranges the
@@ -505,29 +551,13 @@
                 spanEndCacheCount++;
             }
 
-            // tab stop locations
-            int[] variableTabStops = null;
-            if (spanned != null) {
-                TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
-                        paraEnd, TabStopSpan.class);
-                if (spans.length > 0) {
-                    int[] stops = new int[spans.length];
-                    for (int i = 0; i < spans.length; i++) {
-                        stops[i] = spans[i].getTabStop();
-                    }
-                    Arrays.sort(stops, 0, stops.length);
-                    variableTabStops = stops;
-                }
-            }
-
             nGetWidths(b.mNativePtr, widths);
-            int breakCount = nComputeLineBreaks(b.mNativePtr, paraEnd - paraStart, firstWidth,
-                    firstWidthLineCount, restWidth, variableTabStops, TAB_INCREMENT, false, lineBreaks,
-                    lineBreaks.breaks, lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length);
+            int breakCount = nComputeLineBreaks(b.mNativePtr, lineBreaks, lineBreaks.breaks,
+                    lineBreaks.widths, lineBreaks.flags, lineBreaks.breaks.length);
 
             int[] breaks = lineBreaks.breaks;
             float[] lineWidths = lineBreaks.widths;
-            boolean[] flags = lineBreaks.flags;
+            int[] flags = lineBreaks.flags;
 
             // here is the offset of the starting character of the line we are currently measuring
             int here = paraStart;
@@ -613,7 +643,7 @@
                     fm.top, fm.bottom,
                     v,
                     spacingmult, spacingadd, null,
-                    null, fm, false,
+                    null, fm, 0,
                     needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd,
                     includepad, trackpad, null,
                     null, bufStart, ellipsize,
@@ -625,7 +655,7 @@
                       int above, int below, int top, int bottom, int v,
                       float spacingmult, float spacingadd,
                       LineHeightSpan[] chooseHt, int[] chooseHtv,
-                      Paint.FontMetricsInt fm, boolean hasTabOrEmoji,
+                      Paint.FontMetricsInt fm, int flags,
                       boolean needMultiply, byte[] chdirs, int dir,
                       boolean easy, int bufEnd, boolean includePad,
                       boolean trackPad, char[] chs,
@@ -718,8 +748,10 @@
         lines[off + mColumns + START] = end;
         lines[off + mColumns + TOP] = v;
 
-        if (hasTabOrEmoji)
-            lines[off + TAB] |= TAB_MASK;
+        // TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining
+        // one bit for start field
+        lines[off + TAB] |= flags & TAB_MASK;
+        lines[off + HYPHEN] = flags;
 
         lines[off + DIR] |= dir << DIR_SHIFT;
         Directions linedirs = DIRS_ALL_LEFT_TO_RIGHT;
@@ -938,6 +970,14 @@
         return mBottomPadding;
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public int getHyphen(int line) {
+        return mLines[mColumns * line + HYPHEN] & 0xff;
+    }
+
     @Override
     public int getEllipsisCount(int line) {
         if (mColumns < COLUMNS_ELLIPSIZE) {
@@ -964,9 +1004,15 @@
     private static native long nNewBuilder();
     private static native void nFreeBuilder(long nativePtr);
     private static native void nFinishBuilder(long nativePtr);
-    private static native void nSetLocale(long nativePtr, String locale);
 
-    private static native void nSetText(long nativePtr, char[] text, int length);
+    /* package */ static native long nLoadHyphenator(String patternData);
+
+    private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator);
+
+    // Set up paragraph text and settings; done as one big method to minimize jni crossings
+    private static native void nSetupParagraph(long nativePtr, char[] text, int length,
+            float firstWidth, int firstWidthLineCount, float restWidth,
+            int[] variableTabStops, int defaultTabStop, int breakStrategy);
 
     private static native float nAddStyleRun(long nativePtr, long nativePaint,
             long nativeTypeface, int start, int end, boolean isRtl);
@@ -983,25 +1029,24 @@
     // the arrays inside the LineBreaks objects are passed in as well
     // to reduce the number of JNI calls in the common case where the
     // arrays do not have to be resized
-    private static native int nComputeLineBreaks(long nativePtr,
-            int length, float firstWidth, int firstWidthLineCount, float restWidth,
-            int[] variableTabStops, int defaultTabStop, boolean optimize, LineBreaks recycle,
-            int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength);
+    private static native int nComputeLineBreaks(long nativePtr, LineBreaks recycle,
+            int[] recycleBreaks, float[] recycleWidths, int[] recycleFlags, int recycleLength);
 
     private int mLineCount;
     private int mTopPadding, mBottomPadding;
     private int mColumns;
     private int mEllipsizedWidth;
 
-    private static final int COLUMNS_NORMAL = 3;
-    private static final int COLUMNS_ELLIPSIZE = 5;
+    private static final int COLUMNS_NORMAL = 4;
+    private static final int COLUMNS_ELLIPSIZE = 6;
     private static final int START = 0;
     private static final int DIR = START;
     private static final int TAB = START;
     private static final int TOP = 1;
     private static final int DESCENT = 2;
-    private static final int ELLIPSIS_START = 3;
-    private static final int ELLIPSIS_COUNT = 4;
+    private static final int HYPHEN = 3;
+    private static final int ELLIPSIS_START = 4;
+    private static final int ELLIPSIS_COUNT = 5;
 
     private int[] mLines;
     private Directions[] mLineDirections;
@@ -1023,7 +1068,7 @@
         private static final int INITIAL_SIZE = 16;
         public int[] breaks = new int[INITIAL_SIZE];
         public float[] widths = new float[INITIAL_SIZE];
-        public boolean[] flags = new boolean[INITIAL_SIZE]; // hasTabOrEmoji
+        public int[] flags = new int[INITIAL_SIZE]; // hasTabOrEmoji
         // breaks, widths, and flags should all have the same length
     }
 
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 4725581..479242c 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -955,6 +955,10 @@
                     span.updateDrawState(wp);
                 }
 
+                // Only draw hyphen on last run in line
+                if (jnext < mLen) {
+                    wp.setHyphenEdit(0);
+                }
                 x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x,
                         top, y, bottom, fmi, needWidth || jnext < measureLimit);
             }
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index 0c66709..d567d90 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -47,6 +47,7 @@
  *     before 1st Jan 1970 UTC).</li>
  *     <li>Much of the formatting / parsing assumes ASCII text and is therefore not suitable for
  *     use with non-ASCII scripts.</li>
+ *     <li>No support for pseudo-zones like "GMT-07:00".</li>
  * </ul>
  *
  * @deprecated Use {@link java.util.GregorianCalendar} instead.
diff --git a/core/java/android/text/style/URLSpan.java b/core/java/android/text/style/URLSpan.java
index d29bfb6..0669b6f 100644
--- a/core/java/android/text/style/URLSpan.java
+++ b/core/java/android/text/style/URLSpan.java
@@ -16,6 +16,7 @@
 
 package android.text.style;
 
+import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
@@ -23,6 +24,7 @@
 import android.provider.Browser;
 import android.text.ParcelableSpan;
 import android.text.TextUtils;
+import android.util.Log;
 import android.view.View;
 
 public class URLSpan extends ClickableSpan implements ParcelableSpan {
@@ -59,6 +61,10 @@
         Context context = widget.getContext();
         Intent intent = new Intent(Intent.ACTION_VIEW, uri);
         intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
-        context.startActivity(intent);
+        try {
+            context.startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            Log.w("URLSpan", "Actvity was not found for intent, " + intent.toString());
+        }
     }
 }
diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java
index 84d9ce8..c44f42b 100644
--- a/core/java/android/util/DebugUtils.java
+++ b/core/java/android/util/DebugUtils.java
@@ -17,8 +17,10 @@
 package android.util;
 
 import java.io.PrintWriter;
-import java.lang.reflect.Method;
+import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.util.Locale;
 
 /**
@@ -203,4 +205,57 @@
         outBuilder.append(suffix);
         return outBuilder.toString();
     }
+
+    /**
+     * Use prefixed constants (static final values) on given class to turn value
+     * into human-readable string.
+     *
+     * @hide
+     */
+    public static String valueToString(Class<?> clazz, String prefix, int value) {
+        for (Field field : clazz.getDeclaredFields()) {
+            final int modifiers = field.getModifiers();
+            if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
+                    && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
+                try {
+                    if (value == field.getInt(null)) {
+                        return field.getName().substring(prefix.length());
+                    }
+                } catch (IllegalAccessException ignored) {
+                }
+            }
+        }
+        return Integer.toString(value);
+    }
+
+    /**
+     * Use prefixed constants (static final values) on given class to turn flags
+     * into human-readable string.
+     *
+     * @hide
+     */
+    public static String flagsToString(Class<?> clazz, String prefix, int flags) {
+        final StringBuilder res = new StringBuilder();
+
+        for (Field field : clazz.getDeclaredFields()) {
+            final int modifiers = field.getModifiers();
+            if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
+                    && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
+                try {
+                    final int value = field.getInt(null);
+                    if ((flags & value) != 0) {
+                        flags &= ~value;
+                        res.append(field.getName().substring(prefix.length())).append('|');
+                    }
+                } catch (IllegalAccessException ignored) {
+                }
+            }
+        }
+        if (flags != 0 || res.length() == 0) {
+            res.append(Integer.toHexString(flags));
+        } else {
+            res.deleteCharAt(res.length() - 1);
+        }
+        return res.toString();
+    }
 }
diff --git a/core/java/android/util/IntArray.java b/core/java/android/util/IntArray.java
index e8d3947..9326203 100644
--- a/core/java/android/util/IntArray.java
+++ b/core/java/android/util/IntArray.java
@@ -18,6 +18,7 @@
 
 import com.android.internal.util.ArrayUtils;
 
+import java.util.Arrays;
 import libcore.util.EmptyArray;
 
 /**
@@ -78,6 +79,24 @@
     }
 
     /**
+     * Searches the array for the specified value using the binary search algorithm. The array must
+     * be sorted (as by the {@link Arrays#sort(int[], int, int)} method) prior to making this call.
+     * If it is not sorted, the results are undefined. If the range contains multiple elements with
+     * the specified value, there is no guarantee which one will be found.
+     *
+     * @param value The value to search for.
+     * @return index of the search key, if it is contained in the array; otherwise, <i>(-(insertion
+     *         point) - 1)</i>. The insertion point is defined as the point at which the key would
+     *         be inserted into the array: the index of the first element greater than the key, or
+     *         {@link #size()} if all elements in the array are less than the specified key.
+     *         Note that this guarantees that the return value will be >= 0 if and only if the key
+     *         is found.
+     */
+    public int binarySearch(int value) {
+        return ContainerHelpers.binarySearch(mValues, mSize, value);
+    }
+
+    /**
      * Adds the values in the specified array to this array.
      */
     public void addAll(IntArray values) {
@@ -159,4 +178,11 @@
     public int size() {
         return mSize;
     }
+
+    /**
+     * Returns a new array with the contents of this IntArray.
+     */
+    public int[] toArray() {
+        return Arrays.copyOf(mValues, mSize);
+    }
 }
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index 3caf6f0..ec8f802 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -48,7 +48,6 @@
     private int mWidth;
     private int mHeight;
 
-
     static DisplayListCanvas obtain(@NonNull RenderNode node) {
         if (node == null) throw new IllegalArgumentException("node cannot be null");
         DisplayListCanvas canvas = sPool.acquire();
diff --git a/core/java/android/view/IGraphicsStats.aidl b/core/java/android/view/IGraphicsStats.aidl
new file mode 100644
index 0000000..c235eb2
--- /dev/null
+++ b/core/java/android/view/IGraphicsStats.aidl
@@ -0,0 +1,26 @@
+/**
+ * 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 android.view;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * @hide
+ */
+interface IGraphicsStats {
+    ParcelFileDescriptor requestBufferForProcess(String packageName, IBinder token);
+}
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 1a07aee..457d6ad 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -452,9 +452,10 @@
         synchronized (mConstructorArgs) {
             Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
 
+            final Context inflaterContext = mContext;
             final AttributeSet attrs = Xml.asAttributeSet(parser);
-            Context lastContext = (Context)mConstructorArgs[0];
-            mConstructorArgs[0] = mContext;
+            Context lastContext = (Context) mConstructorArgs[0];
+            mConstructorArgs[0] = inflaterContext;
             View result = root;
 
             try {
@@ -485,10 +486,10 @@
                                 + "ViewGroup root and attachToRoot=true");
                     }
 
-                    rInflate(parser, root, attrs, false, false);
+                    rInflate(parser, root, inflaterContext, attrs, false);
                 } else {
                     // Temp is the root view that was found in the xml
-                    final View temp = createViewFromTag(root, name, attrs, false);
+                    final View temp = createViewFromTag(root, name, inflaterContext, attrs);
 
                     ViewGroup.LayoutParams params = null;
 
@@ -509,8 +510,10 @@
                     if (DEBUG) {
                         System.out.println("-----> start inflating children");
                     }
-                    // Inflate all children under temp
-                    rInflate(parser, temp, attrs, true, true);
+
+                    // Inflate all children under temp against its context.
+                    rInflateChildren(parser, temp, attrs, true);
+
                     if (DEBUG) {
                         System.out.println("-----> done inflating children");
                     }
@@ -692,59 +695,68 @@
     }
 
     /**
+     * Convenience method for calling through to the five-arg createViewFromTag
+     * method. This method passes {@code false} for the {@code ignoreThemeAttr}
+     * argument and should be used for everything except {@code &gt;include>}
+     * tag parsing.
+     */
+    private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
+        return createViewFromTag(parent, name, context, attrs, false);
+    }
+
+    /**
      * Creates a view from a tag name using the supplied attribute set.
      * <p>
-     * If {@code inheritContext} is true and the parent is non-null, the view
-     * will be inflated in parent view's context. If the view specifies a
-     * &lt;theme&gt; attribute, the inflation context will be wrapped with the
-     * specified theme.
-     * <p>
-     * Note: Default visibility so the BridgeInflater can override it.
+     * <strong>Note:</strong> Default visibility so the BridgeInflater can
+     * override it.
+     *
+     * @param parent the parent view, used to inflate layout params
+     * @param name the name of the XML tag used to define the view
+     * @param context the inflation context for the view, typically the
+     *                {@code parent} or base layout inflater context
+     * @param attrs the attribute set for the XML tag used to define the view
+     * @param ignoreThemeAttr {@code true} to ignore the {@code android:theme}
+     *                        attribute (if set) for the view being inflated,
+     *                        {@code false} otherwise
      */
-    View createViewFromTag(View parent, String name, AttributeSet attrs, boolean inheritContext) {
+    View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
+            boolean ignoreThemeAttr) {
         if (name.equals("view")) {
             name = attrs.getAttributeValue(null, "class");
         }
 
-        Context viewContext;
-        if (parent != null && inheritContext) {
-            viewContext = parent.getContext();
-        } else {
-            viewContext = mContext;
+        // Apply a theme wrapper, if allowed and one is specified.
+        if (!ignoreThemeAttr) {
+            final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
+            final int themeResId = ta.getResourceId(0, 0);
+            if (themeResId != 0) {
+                context = new ContextThemeWrapper(context, themeResId);
+            }
+            ta.recycle();
         }
 
-        // Apply a theme wrapper, if requested.
-        final TypedArray ta = viewContext.obtainStyledAttributes(attrs, ATTRS_THEME);
-        final int themeResId = ta.getResourceId(0, 0);
-        if (themeResId != 0) {
-            viewContext = new ContextThemeWrapper(viewContext, themeResId);
-        }
-        ta.recycle();
-
         if (name.equals(TAG_1995)) {
             // Let's party like it's 1995!
-            return new BlinkLayout(viewContext, attrs);
+            return new BlinkLayout(context, attrs);
         }
 
-        if (DEBUG) System.out.println("******** Creating view: " + name);
-
         try {
             View view;
             if (mFactory2 != null) {
-                view = mFactory2.onCreateView(parent, name, viewContext, attrs);
+                view = mFactory2.onCreateView(parent, name, context, attrs);
             } else if (mFactory != null) {
-                view = mFactory.onCreateView(name, viewContext, attrs);
+                view = mFactory.onCreateView(name, context, attrs);
             } else {
                 view = null;
             }
 
             if (view == null && mPrivateFactory != null) {
-                view = mPrivateFactory.onCreateView(parent, name, viewContext, attrs);
+                view = mPrivateFactory.onCreateView(parent, name, context, attrs);
             }
 
             if (view == null) {
                 final Object lastContext = mConstructorArgs[0];
-                mConstructorArgs[0] = viewContext;
+                mConstructorArgs[0] = context;
                 try {
                     if (-1 == name.indexOf('.')) {
                         view = onCreateView(parent, name, attrs);
@@ -756,20 +768,18 @@
                 }
             }
 
-            if (DEBUG) System.out.println("Created view is: " + view);
             return view;
-
         } catch (InflateException e) {
             throw e;
 
         } catch (ClassNotFoundException e) {
-            InflateException ie = new InflateException(attrs.getPositionDescription()
+            final InflateException ie = new InflateException(attrs.getPositionDescription()
                     + ": Error inflating class " + name);
             ie.initCause(e);
             throw ie;
 
         } catch (Exception e) {
-            InflateException ie = new InflateException(attrs.getPositionDescription()
+            final InflateException ie = new InflateException(attrs.getPositionDescription()
                     + ": Error inflating class " + name);
             ie.initCause(e);
             throw ie;
@@ -777,16 +787,26 @@
     }
 
     /**
+     * Recursive method used to inflate internal (non-root) children. This
+     * method calls through to {@link #rInflate} using the parent context as
+     * the inflation context.
+     * <strong>Note:</strong> Default visibility so the BridgeInflater can
+     * call it.
+     */
+    final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
+            boolean finishInflate) throws XmlPullParserException, IOException {
+        rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
+    }
+
+    /**
      * Recursive method used to descend down the xml hierarchy and instantiate
      * views, instantiate their children, and then call onFinishInflate().
-     *
-     * @param inheritContext Whether the root view should be inflated in its
-     *            parent's context. This should be true when called inflating
-     *            child views recursively, or false otherwise.
+     * <p>
+     * <strong>Note:</strong> Default visibility so the BridgeInflater can
+     * override it.
      */
-    void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
-            boolean finishInflate, boolean inheritContext) throws XmlPullParserException,
-            IOException {
+    void rInflate(XmlPullParser parser, View parent, Context context,
+            AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
 
         final int depth = parser.getDepth();
         int type;
@@ -808,19 +828,21 @@
                 if (parser.getDepth() == 0) {
                     throw new InflateException("<include /> cannot be the root element");
                 }
-                parseInclude(parser, parent, attrs, inheritContext);
+                parseInclude(parser, context, parent, attrs);
             } else if (TAG_MERGE.equals(name)) {
                 throw new InflateException("<merge /> must be the root element");
             } else {
-                final View view = createViewFromTag(parent, name, attrs, inheritContext);
+                final View view = createViewFromTag(parent, name, context, attrs);
                 final ViewGroup viewGroup = (ViewGroup) parent;
                 final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
-                rInflate(parser, view, attrs, true, true);
+                rInflateChildren(parser, view, attrs, true);
                 viewGroup.addView(view, params);
             }
         }
 
-        if (finishInflate) parent.onFinishInflate();
+        if (finishInflate) {
+            parent.onFinishInflate();
+        }
     }
 
     /**
@@ -829,13 +851,9 @@
      */
     private void parseRequestFocus(XmlPullParser parser, View view)
             throws XmlPullParserException, IOException {
-        int type;
         view.requestFocus();
-        final int currentDepth = parser.getDepth();
-        while (((type = parser.next()) != XmlPullParser.END_TAG ||
-                parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
-            // Empty
-        }
+
+        consumeChildElements(parser);
     }
 
     /**
@@ -844,33 +862,29 @@
      */
     private void parseViewTag(XmlPullParser parser, View view, AttributeSet attrs)
             throws XmlPullParserException, IOException {
-        int type;
-
-        final TypedArray ta = view.getContext().obtainStyledAttributes(
-                attrs, com.android.internal.R.styleable.ViewTag);
-        final int key = ta.getResourceId(com.android.internal.R.styleable.ViewTag_id, 0);
-        final CharSequence value = ta.getText(com.android.internal.R.styleable.ViewTag_value);
+        final Context context = view.getContext();
+        final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ViewTag);
+        final int key = ta.getResourceId(R.styleable.ViewTag_id, 0);
+        final CharSequence value = ta.getText(R.styleable.ViewTag_value);
         view.setTag(key, value);
         ta.recycle();
 
-        final int currentDepth = parser.getDepth();
-        while (((type = parser.next()) != XmlPullParser.END_TAG ||
-                parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
-            // Empty
-        }
+        consumeChildElements(parser);
     }
 
-    private void parseInclude(XmlPullParser parser, View parent, AttributeSet attrs,
-            boolean inheritContext) throws XmlPullParserException, IOException {
+    private void parseInclude(XmlPullParser parser, Context context, View parent,
+            AttributeSet attrs) throws XmlPullParserException, IOException {
         int type;
 
         if (parent instanceof ViewGroup) {
-            Context context = inheritContext ? parent.getContext() : mContext;
-
-            // Apply a theme wrapper, if requested.
+            // Apply a theme wrapper, if requested. This is sort of a weird
+            // edge case, since developers think the <include> overwrites
+            // values in the AttributeSet of the included View. So, if the
+            // included View has a theme attribute, we'll need to ignore it.
             final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
             final int themeResId = ta.getResourceId(0, 0);
-            if (themeResId != 0) {
+            final boolean hasThemeOverride = themeResId != 0;
+            if (hasThemeOverride) {
                 context = new ContextThemeWrapper(context, themeResId);
             }
             ta.recycle();
@@ -880,11 +894,12 @@
             int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0);
             if (layout == 0) {
                 final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
-                if (value == null || value.length() < 1) {
+                if (value == null || value.length() <= 0) {
                     throw new InflateException("You must specify a layout in the"
                             + " include tag: <include layout=\"@layout/layoutID\" />");
                 }
 
+                // Attempt to resolve the "?attr/name" string to an identifier.
                 layout = context.getResources().getIdentifier(value.substring(1), null, null);
             }
 
@@ -901,8 +916,7 @@
                 throw new InflateException("You must specify a valid layout "
                         + "reference. The layout ID " + value + " is not valid.");
             } else {
-                final XmlResourceParser childParser =
-                        getContext().getResources().getLayout(layout);
+                final XmlResourceParser childParser = context.getResources().getLayout(layout);
 
                 try {
                     final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
@@ -920,11 +934,12 @@
                     final String childName = childParser.getName();
 
                     if (TAG_MERGE.equals(childName)) {
-                        // Inflate all children.
-                        rInflate(childParser, parent, childAttrs, false, inheritContext);
+                        // The <merge> tag doesn't support android:theme, so
+                        // nothing special to do here.
+                        rInflate(childParser, parent, context, childAttrs, false);
                     } else {
-                        final View view = createViewFromTag(parent, childName, childAttrs,
-                                inheritContext);
+                        final View view = createViewFromTag(parent, childName,
+                                context, childAttrs, hasThemeOverride);
                         final ViewGroup group = (ViewGroup) parent;
 
                         final TypedArray a = context.obtainStyledAttributes(
@@ -957,7 +972,7 @@
                         view.setLayoutParams(params);
 
                         // Inflate all children.
-                        rInflate(childParser, view, childAttrs, true, true);
+                        rInflateChildren(childParser, view, childAttrs, true);
 
                         if (id != View.NO_ID) {
                             view.setId(id);
@@ -985,6 +1000,16 @@
             throw new InflateException("<include /> can only be used inside of a ViewGroup");
         }
 
+        LayoutInflater.consumeChildElements(parser);
+    }
+
+    /**
+     * <strong>Note:</strong> default visibility so that
+     * LayoutInflater_Delegate can call it.
+     */
+    final static void consumeChildElements(XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        int type;
         final int currentDepth = parser.getDepth();
         while (((type = parser.next()) != XmlPullParser.END_TAG ||
                 parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
diff --git a/core/java/android/view/PhoneFallbackEventHandler.java b/core/java/android/view/PhoneFallbackEventHandler.java
index fbf5732..350650d 100644
--- a/core/java/android/view/PhoneFallbackEventHandler.java
+++ b/core/java/android/view/PhoneFallbackEventHandler.java
@@ -25,8 +25,13 @@
 import android.media.AudioManager;
 import android.media.session.MediaSessionLegacyHelper;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.telephony.TelephonyManager;
-import android.util.Slog;
+import android.util.Log;
+import android.view.View;
+import android.view.HapticFeedbackConstants;
+import android.view.FallbackEventHandler;
+import android.view.KeyEvent;
 
 /**
  * @hide
@@ -112,15 +117,20 @@
                     dispatcher.startTracking(event, this);
                 } else if (event.isLongPress() && dispatcher.isTracking(event)) {
                     dispatcher.performedLongPress(event);
-                    mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-                    // launch the VoiceDialer
-                    Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
-                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    try {
-                        sendCloseSystemWindows();
-                        mContext.startActivity(intent);
-                    } catch (ActivityNotFoundException e) {
-                        startCallActivity();
+                    if (isUserSetupComplete()) {
+                        mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+                        // launch the VoiceDialer
+                        Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
+                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                        try {
+                            sendCloseSystemWindows();
+                            mContext.startActivity(intent);
+                        } catch (ActivityNotFoundException e) {
+                            startCallActivity();
+                        }
+                    } else {
+                        Log.i(TAG, "Not starting call activity because user "
+                                + "setup is in progress.");
                     }
                 }
                 return true;
@@ -134,13 +144,18 @@
                     dispatcher.startTracking(event, this);
                 } else if (event.isLongPress() && dispatcher.isTracking(event)) {
                     dispatcher.performedLongPress(event);
-                    mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-                    sendCloseSystemWindows();
-                    // Broadcast an intent that the Camera button was longpressed
-                    Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null);
-                    intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
-                    mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF,
-                            null, null, null, 0, null, null);
+                    if (isUserSetupComplete()) {
+                        mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+                        sendCloseSystemWindows();
+                        // Broadcast an intent that the Camera button was longpressed
+                        Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null);
+                        intent.putExtra(Intent.EXTRA_KEY_EVENT, event);
+                        mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF,
+                                null, null, null, 0, null, null);
+                    } else {
+                        Log.i(TAG, "Not dispatching CAMERA long press because user "
+                                + "setup is in progress.");
+                    }
                 }
                 return true;
             }
@@ -155,21 +170,26 @@
                     Configuration config = mContext.getResources().getConfiguration();
                     if (config.keyboard == Configuration.KEYBOARD_NOKEYS
                             || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
-                        // launch the search activity
-                        Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS);
-                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                        try {
-                            mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
-                            sendCloseSystemWindows();
-                            getSearchManager().stopSearch();
-                            mContext.startActivity(intent);
-                            // Only clear this if we successfully start the
-                            // activity; otherwise we will allow the normal short
-                            // press action to be performed.
-                            dispatcher.performedLongPress(event);
-                            return true;
-                        } catch (ActivityNotFoundException e) {
-                            // Ignore
+                        if (isUserSetupComplete()) {
+                            // launch the search activity
+                            Intent intent = new Intent(Intent.ACTION_SEARCH_LONG_PRESS);
+                            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                            try {
+                                mView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+                                sendCloseSystemWindows();
+                                getSearchManager().stopSearch();
+                                mContext.startActivity(intent);
+                                // Only clear this if we successfully start the
+                                // activity; otherwise we will allow the normal short
+                                // press action to be performed.
+                                dispatcher.performedLongPress(event);
+                                return true;
+                            } catch (ActivityNotFoundException e) {
+                                // Ignore
+                            }
+                        } else {
+                            Log.i(TAG, "Not dispatching SEARCH long press because user "
+                                    + "setup is in progress.");
                         }
                     }
                 }
@@ -181,7 +201,7 @@
 
     boolean onKeyUp(int keyCode, KeyEvent event) {
         if (DEBUG) {
-            Slog.d(TAG, "up " + keyCode);
+            Log.d(TAG, "up " + keyCode);
         }
         final KeyEvent.DispatcherState dispatcher = mView.getKeyDispatcherState();
         if (dispatcher != null) {
@@ -229,7 +249,12 @@
                     break;
                 }
                 if (event.isTracking() && !event.isCanceled()) {
-                    startCallActivity();
+                    if (isUserSetupComplete()) {
+                        startCallActivity();
+                    } else {
+                        Log.i(TAG, "Not starting call activity because user "
+                                + "setup is in progress.");
+                    }
                 }
                 return true;
             }
@@ -244,7 +269,7 @@
         try {
             mContext.startActivity(intent);
         } catch (ActivityNotFoundException e) {
-            Slog.w(TAG, "No activity found for android.intent.action.CALL_BUTTON.");
+            Log.w(TAG, "No activity found for android.intent.action.CALL_BUTTON.");
         }
     }
 
@@ -284,5 +309,10 @@
     private void handleMediaKeyEvent(KeyEvent keyEvent) {
         MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
     }
+
+    private boolean isUserSetupComplete() {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
+    }
 }
 
diff --git a/core/java/android/view/PhoneWindow.java b/core/java/android/view/PhoneWindow.java
index 05796bb..cb32697 100644
--- a/core/java/android/view/PhoneWindow.java
+++ b/core/java/android/view/PhoneWindow.java
@@ -28,6 +28,7 @@
 import android.os.UserHandle;
 
 import com.android.internal.R;
+import com.android.internal.util.ScreenShapeHelper;
 import com.android.internal.view.RootViewSurfaceTaker;
 import com.android.internal.view.StandaloneActionMode;
 import com.android.internal.view.menu.ContextMenuBuilder;
@@ -64,6 +65,7 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.transition.Scene;
 import android.transition.Transition;
 import android.transition.TransitionInflater;
@@ -125,7 +127,7 @@
     TypedValue mFixedWidthMinor;
     TypedValue mFixedHeightMajor;
     TypedValue mFixedHeightMinor;
-    TypedValue mOutsetBottom;
+    int mOutsetBottomPx;
 
     // This is the top-level view of the window, containing the window decor.
     private DecorView mDecor;
@@ -2368,12 +2370,10 @@
 
         @Override
         public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
-            if (mOutsetBottom != null) {
-                final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
-                int bottom = (int) mOutsetBottom.getDimension(metrics);
+            if (mOutsetBottomPx != 0) {
                 WindowInsets newInsets = insets.replaceSystemWindowInsets(
                         insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
-                        insets.getSystemWindowInsetRight(), bottom);
+                        insets.getSystemWindowInsetRight(), mOutsetBottomPx);
                 return super.dispatchApplyWindowInsets(newInsets);
             } else {
                 return super.dispatchApplyWindowInsets(insets);
@@ -2592,12 +2592,11 @@
                 }
             }
 
-            if (mOutsetBottom != null) {
+            if (mOutsetBottomPx != 0) {
                 int mode = MeasureSpec.getMode(heightMeasureSpec);
                 if (mode != MeasureSpec.UNSPECIFIED) {
-                    int outset = (int) mOutsetBottom.getDimension(metrics);
                     int height = MeasureSpec.getSize(heightMeasureSpec);
-                    heightMeasureSpec = MeasureSpec.makeMeasureSpec(height + outset, mode);
+                    heightMeasureSpec = MeasureSpec.makeMeasureSpec(height + mOutsetBottomPx, mode);
                 }
             }
 
@@ -3472,10 +3471,9 @@
             final boolean shouldUseBottomOutset =
                     display.getDisplayId() == Display.DEFAULT_DISPLAY
                             || (getForcedWindowFlags() & FLAG_FULLSCREEN) != 0;
-            if (shouldUseBottomOutset && a.hasValue(R.styleable.Window_windowOutsetBottom)) {
-                if (mOutsetBottom == null) mOutsetBottom = new TypedValue();
-                a.getValue(R.styleable.Window_windowOutsetBottom,
-                        mOutsetBottom);
+            if (shouldUseBottomOutset) {
+                mOutsetBottomPx = ScreenShapeHelper.getWindowOutsetBottomPx(
+                        getContext().getResources().getDisplayMetrics(), a);
             }
         }
 
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index ef98bbc..236cfef 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -240,12 +240,7 @@
      * @see #start(int, int)
      * @see #isValid()
      */
-    public void end(DisplayListCanvas endCanvas) {
-        if (!(endCanvas instanceof DisplayListCanvas)) {
-            throw new IllegalArgumentException("Passed an invalid canvas to end!");
-        }
-
-        DisplayListCanvas canvas = (DisplayListCanvas) endCanvas;
+    public void end(DisplayListCanvas canvas) {
         canvas.onPostDraw();
         long renderNodeData = canvas.finishRecording();
         nSetDisplayListData(mNativeRenderNode, renderNodeData);
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 6508cca..5cf2c5c 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -130,6 +130,7 @@
     private float mFocusY;
 
     private boolean mQuickScaleEnabled;
+    private boolean mButtonScaleEnabled;
 
     private float mCurrSpan;
     private float mPrevSpan;
@@ -151,14 +152,17 @@
     private int mTouchHistoryDirection;
     private long mTouchHistoryLastAcceptedTime;
     private int mTouchMinMajor;
-    private MotionEvent mDoubleTapEvent;
-    private int mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
     private final Handler mHandler;
 
+    private float mAnchoredScaleStartX;
+    private float mAnchoredScaleStartY;
+    private int mAnchoredScaleMode = ANCHORED_SCALE_MODE_NONE;
+
     private static final long TOUCH_STABILIZE_TIME = 128; // ms
-    private static final int DOUBLE_TAP_MODE_NONE = 0;
-    private static final int DOUBLE_TAP_MODE_IN_PROGRESS = 1;
     private static final float SCALE_FACTOR = .5f;
+    private static final int ANCHORED_SCALE_MODE_NONE = 0;
+    private static final int ANCHORED_SCALE_MODE_DOUBLE_TAP = 1;
+    private static final int ANCHORED_SCALE_MODE_BUTTON = 2;
 
 
     /**
@@ -310,8 +314,17 @@
             mGestureDetector.onTouchEvent(event);
         }
 
+        final int count = event.getPointerCount();
+        final int toolType = event.getToolType(0);
+        final boolean isButtonTool = toolType == MotionEvent.TOOL_TYPE_STYLUS
+                || toolType == MotionEvent.TOOL_TYPE_MOUSE;
+        final boolean isAnchoredScaleButtonDown = isButtonTool && (count == 1)
+                && (event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0;
+
+        final boolean anchoredScaleCancelled =
+                mAnchoredScaleMode == ANCHORED_SCALE_MODE_BUTTON && !isAnchoredScaleButtonDown;
         final boolean streamComplete = action == MotionEvent.ACTION_UP ||
-                action == MotionEvent.ACTION_CANCEL;
+                action == MotionEvent.ACTION_CANCEL || anchoredScaleCancelled;
 
         if (action == MotionEvent.ACTION_DOWN || streamComplete) {
             // Reset any scale in progress with the listener.
@@ -321,11 +334,11 @@
                 mListener.onScaleEnd(this);
                 mInProgress = false;
                 mInitialSpan = 0;
-                mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
-            } else if (mDoubleTapMode == DOUBLE_TAP_MODE_IN_PROGRESS && streamComplete) {
+                mAnchoredScaleMode = ANCHORED_SCALE_MODE_NONE;
+            } else if (inAnchoredScaleMode() && streamComplete) {
                 mInProgress = false;
                 mInitialSpan = 0;
-                mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
+                mAnchoredScaleMode = ANCHORED_SCALE_MODE_NONE;
             }
 
             if (streamComplete) {
@@ -334,25 +347,32 @@
             }
         }
 
+        if (!mInProgress && mButtonScaleEnabled && !inAnchoredScaleMode()
+                && !streamComplete && isAnchoredScaleButtonDown) {
+            // Start of a button scale gesture
+            mAnchoredScaleStartX = event.getX();
+            mAnchoredScaleStartY = event.getY();
+            mAnchoredScaleMode = ANCHORED_SCALE_MODE_BUTTON;
+            mInitialSpan = 0;
+        }
+
         final boolean configChanged = action == MotionEvent.ACTION_DOWN ||
                 action == MotionEvent.ACTION_POINTER_UP ||
-                action == MotionEvent.ACTION_POINTER_DOWN;
-
+                action == MotionEvent.ACTION_POINTER_DOWN || anchoredScaleCancelled;
 
         final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
         final int skipIndex = pointerUp ? event.getActionIndex() : -1;
 
         // Determine focal point
         float sumX = 0, sumY = 0;
-        final int count = event.getPointerCount();
         final int div = pointerUp ? count - 1 : count;
         final float focusX;
         final float focusY;
-        if (mDoubleTapMode == DOUBLE_TAP_MODE_IN_PROGRESS) {
-            // In double tap mode, the focal pt is always where the double tap
-            // gesture started
-            focusX = mDoubleTapEvent.getX();
-            focusY = mDoubleTapEvent.getY();
+        if (inAnchoredScaleMode()) {
+            // In anchored scale mode, the focal pt is always where the double tap
+            // or button down gesture started
+            focusX = mAnchoredScaleStartX;
+            focusY = mAnchoredScaleStartY;
             if (event.getY() < focusY) {
                 mEventBeforeOrAboveStartingGestureEvent = true;
             } else {
@@ -390,7 +410,7 @@
         final float spanX = devX * 2;
         final float spanY = devY * 2;
         final float span;
-        if (inDoubleTapMode()) {
+        if (inAnchoredScaleMode()) {
             span = spanY;
         } else {
             span = (float) Math.hypot(spanX, spanY);
@@ -402,11 +422,10 @@
         final boolean wasInProgress = mInProgress;
         mFocusX = focusX;
         mFocusY = focusY;
-        if (!inDoubleTapMode() && mInProgress && (span < mMinSpan || configChanged)) {
+        if (!inAnchoredScaleMode() && mInProgress && (span < mMinSpan || configChanged)) {
             mListener.onScaleEnd(this);
             mInProgress = false;
             mInitialSpan = span;
-            mDoubleTapMode = DOUBLE_TAP_MODE_NONE;
         }
         if (configChanged) {
             mPrevSpanX = mCurrSpanX = spanX;
@@ -414,7 +433,7 @@
             mInitialSpan = mPrevSpan = mCurrSpan = span;
         }
 
-        final int minSpan = inDoubleTapMode() ? mSpanSlop : mMinSpan;
+        final int minSpan = inAnchoredScaleMode() ? mSpanSlop : mMinSpan;
         if (!mInProgress && span >=  minSpan &&
                 (wasInProgress || Math.abs(span - mInitialSpan) > mSpanSlop)) {
             mPrevSpanX = mCurrSpanX = spanX;
@@ -447,9 +466,8 @@
         return true;
     }
 
-
-    private boolean inDoubleTapMode() {
-        return mDoubleTapMode == DOUBLE_TAP_MODE_IN_PROGRESS;
+    private boolean inAnchoredScaleMode() {
+        return mAnchoredScaleMode != ANCHORED_SCALE_MODE_NONE;
     }
 
     /**
@@ -466,8 +484,9 @@
                         @Override
                         public boolean onDoubleTap(MotionEvent e) {
                             // Double tap: start watching for a swipe
-                            mDoubleTapEvent = e;
-                            mDoubleTapMode = DOUBLE_TAP_MODE_IN_PROGRESS;
+                            mAnchoredScaleStartX = e.getX();
+                            mAnchoredScaleStartY = e.getY();
+                            mAnchoredScaleMode = ANCHORED_SCALE_MODE_DOUBLE_TAP;
                             return true;
                         }
                     };
@@ -484,6 +503,27 @@
     }
 
     /**
+     * Sets whether the associates {@link OnScaleGestureListener} should receive onScale callbacks
+     * when the user presses a {@value MotionEvent#BUTTON_SECONDARY} (right mouse button, stylus
+     * first button) and drags the pointer on the screen. Note that this is enabled by default if
+     * the app targets API 23 and newer.
+     *
+     * @param scales true to enable stylus or mouse scaling, false to disable.
+     */
+    public void setSecondaryButtonScaleEnabled(boolean scales) {
+        mButtonScaleEnabled = scales;
+    }
+
+    /**
+     * Return whether the button scale gesture, in which the user presses a
+     * {@value MotionEvent#BUTTON_SECONDARY} (right mouse button, stylus first button) and drags the
+     * pointer on the screen, should perform scaling. {@see #setButtonScaleEnabled(boolean)}.
+     */
+    public boolean isSecondaryButtonScaleEnabled() {
+        return mButtonScaleEnabled;
+    }
+
+    /**
      * Returns {@code true} if a scale gesture is in progress.
      */
     public boolean isInProgress() {
@@ -586,7 +626,7 @@
      * @return The current scaling factor.
      */
     public float getScaleFactor() {
-        if (inDoubleTapMode()) {
+        if (inAnchoredScaleMode()) {
             // Drag is moving up; the further away from the gesture
             // start, the smaller the span should be, the closer,
             // the larger the span, and therefore the larger the scale
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 59ec058..ad34f02 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -723,6 +723,12 @@
             mSurface.release();
         }
         mSurface = surfaceTexture;
+
+        // If the view is visible, update the listener in the new surface to use
+        // the existing listener in the view.
+        if (((mViewFlags & VISIBILITY_MASK) == VISIBLE)) {
+            mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
+        }
         mUpdateSurface = true;
         invalidateParentIfNeeded();
     }
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 031be07..87d5d9a 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -23,7 +23,9 @@
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Binder;
 import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.Trace;
@@ -124,7 +126,7 @@
         mRootNode.setClipToBounds(false);
         mNativeProxy = nCreateProxy(translucent, rootNodePtr);
 
-        AtlasInitializer.sInstance.init(context, mNativeProxy);
+        ProcessInitializer.sInstance.init(context, mNativeProxy);
 
         loadSystemProperties();
     }
@@ -410,15 +412,44 @@
         nTrimMemory(level);
     }
 
-    private static class AtlasInitializer {
-        static AtlasInitializer sInstance = new AtlasInitializer();
+    public static void dumpProfileData(byte[] data, FileDescriptor fd) {
+        nDumpProfileData(data, fd);
+    }
+
+    private static class ProcessInitializer {
+        static ProcessInitializer sInstance = new ProcessInitializer();
+        static IGraphicsStats sGraphicsStatsService;
+        private static IBinder sProcToken;
 
         private boolean mInitialized = false;
 
-        private AtlasInitializer() {}
+        private ProcessInitializer() {}
 
         synchronized void init(Context context, long renderProxy) {
             if (mInitialized) return;
+            mInitialized = true;
+            initGraphicsStats(context, renderProxy);
+            initAssetAtlas(context, renderProxy);
+        }
+
+        private static void initGraphicsStats(Context context, long renderProxy) {
+            IBinder binder = ServiceManager.getService("graphicsstats");
+            if (binder == null) return;
+
+            sGraphicsStatsService = IGraphicsStats.Stub.asInterface(binder);
+            sProcToken = new Binder();
+            try {
+                final String pkg = context.getApplicationInfo().packageName;
+                ParcelFileDescriptor pfd = sGraphicsStatsService.
+                        requestBufferForProcess(pkg, sProcToken);
+                nSetProcessStatsBuffer(renderProxy, pfd.getFd());
+                pfd.close();
+            } catch (Exception e) {
+                Log.w(LOG_TAG, "Could not acquire gfx stats buffer", e);
+            }
+        }
+
+        private static void initAssetAtlas(Context context, long renderProxy) {
             IBinder binder = ServiceManager.getService("assetatlas");
             if (binder == null) return;
 
@@ -432,7 +463,6 @@
                             // TODO Remove after fixing b/15425820
                             validateMap(context, map);
                             nSetAtlas(renderProxy, buffer, map);
-                            mInitialized = true;
                         }
                         // If IAssetAtlas is not the same class as the IBinder
                         // we are using a remote service and we can safely
@@ -477,6 +507,7 @@
     static native void setupShadersDiskCache(String cacheFile);
 
     private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map);
+    private static native void nSetProcessStatsBuffer(long nativeProxy, int fd);
 
     private static native long nCreateRootRenderNode();
     private static native long nCreateProxy(boolean translucent, long rootRenderNode);
@@ -514,4 +545,5 @@
 
     private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd,
             @DumpFlags int dumpFlags);
+    private static native void nDumpProfileData(byte[] data, FileDescriptor fd);
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f5de8e3..384bd2c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -83,6 +83,7 @@
 import android.view.accessibility.AccessibilityEventSource;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.accessibility.AccessibilityNodeProvider;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
@@ -90,6 +91,7 @@
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
+import android.widget.Checkable;
 import android.widget.ScrollBarDrawable;
 
 import static android.os.Build.VERSION_CODES.*;
@@ -3168,6 +3170,9 @@
     private Drawable mBackground;
     private TintInfo mBackgroundTint;
 
+    @ViewDebug.ExportedProperty(deepExport = true, prefix = "fg_")
+    private ForegroundInfo mForegroundInfo;
+
     /**
      * RenderNode used for backgrounds.
      * <p>
@@ -3182,13 +3187,23 @@
 
     private String mTransitionName;
 
-    private static class TintInfo {
+    static class TintInfo {
         ColorStateList mTintList;
         PorterDuff.Mode mTintMode;
         boolean mHasTintMode;
         boolean mHasTintList;
     }
 
+    private static class ForegroundInfo {
+        private Drawable mDrawable;
+        private TintInfo mTintInfo;
+        private int mGravity = Gravity.FILL;
+        private boolean mInsidePadding = true;
+        private boolean mBoundsChanged = true;
+        private final Rect mSelfBounds = new Rect();
+        private final Rect mOverlayBounds = new Rect();
+    }
+
     static class ListenerInfo {
         /**
          * Listener used to dispatch focus change events.
@@ -3568,6 +3583,8 @@
             // of whether a layout was requested on that View.
             sIgnoreMeasureCache = targetSdkVersion < KITKAT;
 
+            Canvas.sCompatibilityRestore = targetSdkVersion < MNC;
+
             sCompatibilityDone = true;
         }
     }
@@ -4056,6 +4073,25 @@
                     setOutlineProviderFromAttribute(a.getInt(R.styleable.View_outlineProvider,
                             PROVIDER_BACKGROUND));
                     break;
+                case R.styleable.View_foreground:
+                    setForeground(a.getDrawable(attr));
+                    break;
+                case R.styleable.View_foregroundGravity:
+                    setForegroundGravity(a.getInt(attr, Gravity.NO_GRAVITY));
+                    break;
+                case R.styleable.View_foregroundTintMode:
+                    setForegroundTintMode(Drawable.parseTintMode(a.getInt(attr, -1), null));
+                    break;
+                case R.styleable.View_foregroundTint:
+                    setForegroundTintList(a.getColorStateList(attr));
+                    break;
+                case R.styleable.View_foregroundInsidePadding:
+                    if (mForegroundInfo == null) {
+                        mForegroundInfo = new ForegroundInfo();
+                    }
+                    mForegroundInfo.mInsidePadding = a.getBoolean(attr,
+                            mForegroundInfo.mInsidePadding);
+                    break;
             }
         }
 
@@ -4813,10 +4849,11 @@
      * @hide
      */
     protected boolean performButtonActionOnTouchDown(MotionEvent event) {
-        if ((event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0) {
-            if (showContextMenu(event.getX(), event.getY(), event.getMetaState())) {
-                return true;
-            }
+        if (event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE &&
+            (event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0) {
+            showContextMenu(event.getX(), event.getY(), event.getMetaState());
+            mPrivateFlags |= PFLAG_CANCEL_NEXT_UP_EVENT;
+            return true;
         }
         return false;
     }
@@ -5642,10 +5679,143 @@
     /**
      * Called when assist structure is being retrieved from a view as part of
      * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
-     * @param structure Additional standard structured view structure to supply.
-     * @param extras Non-standard extensions.
+     * @param structure Fill in with structured view data.  The default implementation
+     * fills in all data that can be inferred from the view itself.
      */
-    public void onProvideAssistStructure(ViewAssistStructure structure, Bundle extras) {
+    public void onProvideAssistStructure(ViewAssistStructure structure) {
+        final int id = mID;
+        if (id > 0 && (id&0xff000000) != 0 && (id&0x00ff0000) != 0
+                && (id&0x0000ffff) != 0) {
+            String pkg, type, entry;
+            try {
+                final Resources res = getResources();
+                entry = res.getResourceEntryName(id);
+                type = res.getResourceTypeName(id);
+                pkg = res.getResourcePackageName(id);
+            } catch (Resources.NotFoundException e) {
+                entry = type = pkg = null;
+            }
+            structure.setId(id, pkg, type, entry);
+        } else {
+            structure.setId(id, null, null, null);
+        }
+        structure.setDimens(mLeft, mTop, mScrollX, mScrollY, mRight-mLeft, mBottom-mTop);
+        structure.setVisibility(getVisibility());
+        structure.setEnabled(isEnabled());
+        if (isClickable()) {
+            structure.setClickable(true);
+        }
+        if (isFocusable()) {
+            structure.setFocusable(true);
+        }
+        if (isFocused()) {
+            structure.setFocused(true);
+        }
+        if (isAccessibilityFocused()) {
+            structure.setAccessibilityFocused(true);
+        }
+        if (isSelected()) {
+            structure.setSelected(true);
+        }
+        if (isActivated()) {
+            structure.setActivated(true);
+        }
+        if (isLongClickable()) {
+            structure.setLongClickable(true);
+        }
+        if (this instanceof Checkable) {
+            structure.setCheckable(true);
+            if (((Checkable)this).isChecked()) {
+                structure.setChecked(true);
+            }
+        }
+        structure.setClassName(getAccessibilityClassName().toString());
+        structure.setContentDescription(getContentDescription());
+    }
+
+    /**
+     * Called when assist structure is being retrieved from a view as part of
+     * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData} to
+     * generate additional virtual structure under this view.  The defaullt implementation
+     * uses {@link #getAccessibilityNodeProvider()} to try to generate this from the
+     * view's virtual accessibility nodes, if any.  You can override this for a more
+     * optimal implementation providing this data.
+     */
+    public void onProvideVirtualAssistStructure(ViewAssistStructure structure) {
+        AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
+        if (provider != null) {
+            AccessibilityNodeInfo info = createAccessibilityNodeInfo();
+            Log.i("View", "Provider of " + this + ": children=" + info.getChildCount());
+            structure.setChildCount(1);
+            ViewAssistStructure root = structure.newChild(0);
+            populateVirtualAssistStructure(root, provider, info);
+            info.recycle();
+        }
+    }
+
+    private void populateVirtualAssistStructure(ViewAssistStructure structure,
+            AccessibilityNodeProvider provider, AccessibilityNodeInfo info) {
+        structure.setId(AccessibilityNodeInfo.getVirtualDescendantId(info.getSourceNodeId()),
+                null, null, null);
+        Rect rect = structure.getTempRect();
+        info.getBoundsInParent(rect);
+        structure.setDimens(rect.left, rect.top, 0, 0, rect.width(), rect.height());
+        structure.setVisibility(VISIBLE);
+        structure.setEnabled(info.isEnabled());
+        if (info.isClickable()) {
+            structure.setClickable(true);
+        }
+        if (info.isFocusable()) {
+            structure.setFocusable(true);
+        }
+        if (info.isFocused()) {
+            structure.setFocused(true);
+        }
+        if (info.isAccessibilityFocused()) {
+            structure.setAccessibilityFocused(true);
+        }
+        if (info.isSelected()) {
+            structure.setSelected(true);
+        }
+        if (info.isLongClickable()) {
+            structure.setLongClickable(true);
+        }
+        if (info.isCheckable()) {
+            structure.setCheckable(true);
+            if (info.isChecked()) {
+                structure.setChecked(true);
+            }
+        }
+        CharSequence cname = info.getClassName();
+        structure.setClassName(cname != null ? cname.toString() : null);
+        structure.setContentDescription(info.getContentDescription());
+        Log.i("View", "vassist " + cname + " @ " + rect.toShortString()
+                + " text=" + info.getText() + " cd=" + info.getContentDescription());
+        if (info.getText() != null || info.getError() != null) {
+            structure.setText(info.getText(), info.getTextSelectionStart(),
+                    info.getTextSelectionEnd());
+        }
+        final int NCHILDREN = info.getChildCount();
+        if (NCHILDREN > 0) {
+            structure.setChildCount(NCHILDREN);
+            for (int i=0; i<NCHILDREN; i++) {
+                AccessibilityNodeInfo cinfo = provider.createAccessibilityNodeInfo(
+                        AccessibilityNodeInfo.getVirtualDescendantId(info.getChildId(i)));
+                ViewAssistStructure child = structure.newChild(i);
+                populateVirtualAssistStructure(child, provider, cinfo);
+                cinfo.recycle();
+            }
+        }
+    }
+
+    /**
+     * Dispatch creation of {@link ViewAssistStructure} down the hierarchy.  The default
+     * implementation calls {@link #onProvideAssistStructure} and
+     * {@link #onProvideVirtualAssistStructure}.
+     */
+    public void dispatchProvideAssistStructure(ViewAssistStructure structure) {
+        onProvideAssistStructure(structure);
+        onProvideVirtualAssistStructure(structure);
     }
 
     /**
@@ -5781,6 +5951,8 @@
                     | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
                     | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH);
         }
+
+        info.addAction(AccessibilityAction.ACTION_SHOW_ON_SCREEN);
     }
 
     private View findLabelForView(View view, int labeledId) {
@@ -8228,6 +8400,13 @@
                     return true;
                 }
             } break;
+            case R.id.accessibility_action_show_on_screen: {
+                if (mAttachInfo != null) {
+                    final Rect r = mAttachInfo.mTmpInvalRect;
+                    getDrawingRect(r);
+                    return requestRectangleOnScreen(r, true);
+                }
+            } break;
         }
         return false;
     }
@@ -8801,6 +8980,10 @@
         if (dr != null && visible != dr.isVisible()) {
             dr.setVisible(visible, false);
         }
+        final Drawable fg = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
+        if (fg != null && visible != fg.isVisible()) {
+            fg.setVisible(visible, false);
+        }
     }
 
     /**
@@ -9912,11 +10095,16 @@
      * @param oldt Previous vertical scroll origin.
      */
     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        notifySubtreeAccessibilityStateChangedIfNeeded();
+
         if (AccessibilityManager.getInstance(mContext).isEnabled()) {
             postSendViewScrolledAccessibilityEventCallback();
         }
 
         mBackgroundSizeChanged = true;
+        if (mForegroundInfo != null) {
+            mForegroundInfo.mBoundsChanged = true;
+        }
 
         final AttachInfo ai = mAttachInfo;
         if (ai != null) {
@@ -10755,6 +10943,9 @@
                 invalidate(true);
             }
             mBackgroundSizeChanged = true;
+            if (mForegroundInfo != null) {
+                mForegroundInfo.mBoundsChanged = true;
+            }
             invalidateParentIfNeeded();
             if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) {
                 // View was rejected last time it was drawn by its parent; this may have changed
@@ -10820,6 +11011,9 @@
                 invalidate(true);
             }
             mBackgroundSizeChanged = true;
+            if (mForegroundInfo != null) {
+                mForegroundInfo.mBoundsChanged = true;
+            }
             invalidateParentIfNeeded();
             if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) {
                 // View was rejected last time it was drawn by its parent; this may have changed
@@ -10879,6 +11073,9 @@
                 invalidate(true);
             }
             mBackgroundSizeChanged = true;
+            if (mForegroundInfo != null) {
+                mForegroundInfo.mBoundsChanged = true;
+            }
             invalidateParentIfNeeded();
             if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) {
                 // View was rejected last time it was drawn by its parent; this may have changed
@@ -10935,6 +11132,9 @@
                 invalidate(true);
             }
             mBackgroundSizeChanged = true;
+            if (mForegroundInfo != null) {
+                mForegroundInfo.mBoundsChanged = true;
+            }
             invalidateParentIfNeeded();
             if ((mPrivateFlags2 & PFLAG2_VIEW_QUICK_REJECTED) == PFLAG2_VIEW_QUICK_REJECTED) {
                 // View was rejected last time it was drawn by its parent; this may have changed
@@ -11100,6 +11300,7 @@
             invalidateViewProperty(false, true);
 
             invalidateParentIfNeededAndWasQuickRejected();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
@@ -15313,13 +15514,14 @@
             // Step 4, draw the children
             dispatchDraw(canvas);
 
-            // Step 6, draw decorations (scrollbars)
-            onDrawScrollBars(canvas);
-
+            // Overlay is part of the content and draws beneath Foreground
             if (mOverlay != null && !mOverlay.isEmpty()) {
                 mOverlay.getOverlayView().dispatchDraw(canvas);
             }
 
+            // Step 6, draw decorations (foreground, scrollbars)
+            onDrawForeground(canvas);
+
             // we're done...
             return;
         }
@@ -15461,12 +15663,13 @@
 
         canvas.restoreToCount(saveCount);
 
-        // Step 6, draw decorations (scrollbars)
-        onDrawScrollBars(canvas);
-
+        // Overlay is part of the content and draws beneath Foreground
         if (mOverlay != null && !mOverlay.isEmpty()) {
             mOverlay.getOverlayView().dispatchDraw(canvas);
         }
+
+        // Step 6, draw decorations (foreground, scrollbars)
+        onDrawForeground(canvas);
     }
 
     /**
@@ -15849,6 +16052,9 @@
             mPrivateFlags |= drawn;
 
             mBackgroundSizeChanged = true;
+            if (mForegroundInfo != null) {
+                mForegroundInfo.mBoundsChanged = true;
+            }
 
             notifySubtreeAccessibilityStateChangedIfNeeded();
         }
@@ -15992,6 +16198,9 @@
         if (mBackground != null) {
             mBackground.setLayoutDirection(layoutDirection);
         }
+        if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
+            mForegroundInfo.mDrawable.setLayoutDirection(layoutDirection);
+        }
         mPrivateFlags2 |= PFLAG2_DRAWABLE_RESOLVED;
         onResolveDrawables(layoutDirection);
     }
@@ -16047,7 +16256,8 @@
      */
     @CallSuper
     protected boolean verifyDrawable(Drawable who) {
-        return who == mBackground || (mScrollCache != null && mScrollCache.scrollBar == who);
+        return who == mBackground || (mScrollCache != null && mScrollCache.scrollBar == who)
+                || (mForegroundInfo != null && mForegroundInfo.mDrawable == who);
     }
 
     /**
@@ -16065,9 +16275,14 @@
     protected void drawableStateChanged() {
         final int[] state = getDrawableState();
 
-        final Drawable d = mBackground;
-        if (d != null && d.isStateful()) {
-            d.setState(state);
+        final Drawable bg = mBackground;
+        if (bg != null && bg.isStateful()) {
+            bg.setState(state);
+        }
+
+        final Drawable fg = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
+        if (fg != null && fg.isStateful()) {
+            fg.setState(state);
         }
 
         if (mScrollCache != null) {
@@ -16099,6 +16314,9 @@
         if (mBackground != null) {
             mBackground.setHotspot(x, y);
         }
+        if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
+            mForegroundInfo.mDrawable.setHotspot(x, y);
+        }
 
         dispatchDrawableHotspotChanged(x, y);
     }
@@ -16270,6 +16488,9 @@
         if (mStateListAnimator != null) {
             mStateListAnimator.jumpToCurrentState();
         }
+        if (mForegroundInfo != null && mForegroundInfo.mDrawable != null) {
+            mForegroundInfo.mDrawable.jumpToCurrentState();
+        }
     }
 
     /**
@@ -16554,6 +16775,249 @@
     }
 
     /**
+     * Returns the drawable used as the foreground of this View. The
+     * foreground drawable, if non-null, is always drawn on top of the view's content.
+     *
+     * @return a Drawable or null if no foreground was set
+     *
+     * @see #onDrawForeground(Canvas)
+     */
+    public Drawable getForeground() {
+        return mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
+    }
+
+    /**
+     * Supply a Drawable that is to be rendered on top of all of the content in the view.
+     *
+     * @param foreground the Drawable to be drawn on top of the children
+     *
+     * @attr ref android.R.styleable#View_foreground
+     */
+    public void setForeground(Drawable foreground) {
+        if (mForegroundInfo == null) {
+            if (foreground == null) {
+                // Nothing to do.
+                return;
+            }
+            mForegroundInfo = new ForegroundInfo();
+        }
+
+        if (foreground == mForegroundInfo.mDrawable) {
+            // Nothing to do
+            return;
+        }
+
+        if (mForegroundInfo.mDrawable != null) {
+            mForegroundInfo.mDrawable.setCallback(null);
+            unscheduleDrawable(mForegroundInfo.mDrawable);
+        }
+
+        mForegroundInfo.mDrawable = foreground;
+        mForegroundInfo.mBoundsChanged = true;
+        if (foreground != null) {
+            setWillNotDraw(false);
+            foreground.setCallback(this);
+            foreground.setLayoutDirection(getLayoutDirection());
+            if (foreground.isStateful()) {
+                foreground.setState(getDrawableState());
+            }
+            applyForegroundTint();
+        }
+        requestLayout();
+        invalidate();
+    }
+
+    /**
+     * Magic bit used to support features of framework-internal window decor implementation details.
+     * This used to live exclusively in FrameLayout.
+     *
+     * @return true if the foreground should draw inside the padding region or false
+     *         if it should draw inset by the view's padding
+     * @hide internal use only; only used by FrameLayout and internal screen layouts.
+     */
+    public boolean isForegroundInsidePadding() {
+        return mForegroundInfo != null ? mForegroundInfo.mInsidePadding : true;
+    }
+
+    /**
+     * Describes how the foreground is positioned.
+     *
+     * @return foreground gravity.
+     *
+     * @see #setForegroundGravity(int)
+     *
+     * @attr ref android.R.styleable#View_foregroundGravity
+     */
+    public int getForegroundGravity() {
+        return mForegroundInfo != null ? mForegroundInfo.mGravity
+                : Gravity.START | Gravity.TOP;
+    }
+
+    /**
+     * Describes how the foreground is positioned. Defaults to START and TOP.
+     *
+     * @param gravity see {@link android.view.Gravity}
+     *
+     * @see #getForegroundGravity()
+     *
+     * @attr ref android.R.styleable#View_foregroundGravity
+     */
+    public void setForegroundGravity(int gravity) {
+        if (mForegroundInfo == null) {
+            mForegroundInfo = new ForegroundInfo();
+        }
+
+        if (mForegroundInfo.mGravity != gravity) {
+            if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
+                gravity |= Gravity.START;
+            }
+
+            if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
+                gravity |= Gravity.TOP;
+            }
+
+            mForegroundInfo.mGravity = gravity;
+            requestLayout();
+        }
+    }
+
+    /**
+     * Applies a tint to the foreground drawable. Does not modify the current tint
+     * mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
+     * <p>
+     * Subsequent calls to {@link #setForeground(Drawable)} will automatically
+     * mutate the drawable and apply the specified tint and tint mode using
+     * {@link Drawable#setTintList(ColorStateList)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#View_foregroundTint
+     * @see #getForegroundTintList()
+     * @see Drawable#setTintList(ColorStateList)
+     */
+    public void setForegroundTintList(@Nullable ColorStateList tint) {
+        if (mForegroundInfo == null) {
+            mForegroundInfo = new ForegroundInfo();
+        }
+        if (mForegroundInfo.mTintInfo == null) {
+            mForegroundInfo.mTintInfo = new TintInfo();
+        }
+        mForegroundInfo.mTintInfo.mTintList = tint;
+        mForegroundInfo.mTintInfo.mHasTintList = true;
+
+        applyForegroundTint();
+    }
+
+    /**
+     * Return the tint applied to the foreground drawable, if specified.
+     *
+     * @return the tint applied to the foreground drawable
+     * @attr ref android.R.styleable#View_foregroundTint
+     * @see #setForegroundTintList(ColorStateList)
+     */
+    @Nullable
+    public ColorStateList getForegroundTintList() {
+        return mForegroundInfo != null && mForegroundInfo.mTintInfo != null
+                ? mBackgroundTint.mTintList : null;
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setForegroundTintList(ColorStateList)}} to the background
+     * drawable. The default mode is {@link PorterDuff.Mode#SRC_IN}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     * @attr ref android.R.styleable#View_foregroundTintMode
+     * @see #getForegroundTintMode()
+     * @see Drawable#setTintMode(PorterDuff.Mode)
+     */
+    public void setForegroundTintMode(@Nullable PorterDuff.Mode tintMode) {
+        if (mBackgroundTint == null) {
+            mBackgroundTint = new TintInfo();
+        }
+        mBackgroundTint.mTintMode = tintMode;
+        mBackgroundTint.mHasTintMode = true;
+
+        applyBackgroundTint();
+    }
+
+    /**
+     * Return the blending mode used to apply the tint to the foreground
+     * drawable, if specified.
+     *
+     * @return the blending mode used to apply the tint to the foreground
+     *         drawable
+     * @attr ref android.R.styleable#View_foregroundTintMode
+     * @see #setBackgroundTintMode(PorterDuff.Mode)
+     */
+    @Nullable
+    public PorterDuff.Mode getForegroundTintMode() {
+        return mForegroundInfo != null && mForegroundInfo.mTintInfo != null
+                ? mForegroundInfo.mTintInfo.mTintMode : null;
+    }
+
+    private void applyForegroundTint() {
+        if (mForegroundInfo != null && mForegroundInfo.mDrawable != null
+                && mForegroundInfo.mTintInfo != null) {
+            final TintInfo tintInfo = mForegroundInfo.mTintInfo;
+            if (tintInfo.mHasTintList || tintInfo.mHasTintMode) {
+                mForegroundInfo.mDrawable = mForegroundInfo.mDrawable.mutate();
+
+                if (tintInfo.mHasTintList) {
+                    mForegroundInfo.mDrawable.setTintList(tintInfo.mTintList);
+                }
+
+                if (tintInfo.mHasTintMode) {
+                    mForegroundInfo.mDrawable.setTintMode(tintInfo.mTintMode);
+                }
+
+                // The drawable (or one of its children) may not have been
+                // stateful before applying the tint, so let's try again.
+                if (mForegroundInfo.mDrawable.isStateful()) {
+                    mForegroundInfo.mDrawable.setState(getDrawableState());
+                }
+            }
+        }
+    }
+
+    /**
+     * Draw any foreground content for this view.
+     *
+     * <p>Foreground content may consist of scroll bars, a {@link #setForeground foreground}
+     * drawable or other view-specific decorations. The foreground is drawn on top of the
+     * primary view content.</p>
+     *
+     * @param canvas canvas to draw into
+     */
+    public void onDrawForeground(Canvas canvas) {
+        onDrawScrollBars(canvas);
+
+        final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
+        if (foreground != null) {
+            if (mForegroundInfo.mBoundsChanged) {
+                mForegroundInfo.mBoundsChanged = false;
+                final Rect selfBounds = mForegroundInfo.mSelfBounds;
+                final Rect overlayBounds = mForegroundInfo.mOverlayBounds;
+
+                if (mForegroundInfo.mInsidePadding) {
+                    selfBounds.set(0, 0, getWidth(), getHeight());
+                } else {
+                    selfBounds.set(getPaddingLeft(), getPaddingTop(),
+                            getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
+                }
+
+                final int ld = getLayoutDirection();
+                Gravity.apply(mForegroundInfo.mGravity, foreground.getIntrinsicWidth(),
+                        foreground.getIntrinsicHeight(), selfBounds, overlayBounds, ld);
+                foreground.setBounds(overlayBounds);
+            }
+
+            foreground.draw(canvas);
+        }
+    }
+
+    /**
      * Sets the padding. The view may add on the space required to display
      * the scrollbars, depending on the style and visibility of the scrollbars.
      * So the values returned from {@link #getPaddingLeft}, {@link #getPaddingTop},
@@ -18090,6 +18554,10 @@
                 // parts from this transparent region.
                 applyDrawableToTransparentRegion(mBackground, region);
             }
+            final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
+            if (foreground != null) {
+                applyDrawableToTransparentRegion(mForegroundInfo.mDrawable, region);
+            }
         }
         return true;
     }
diff --git a/core/java/android/view/ViewAssistStructure.java b/core/java/android/view/ViewAssistStructure.java
index 5132bb9..c05ed6f 100644
--- a/core/java/android/view/ViewAssistStructure.java
+++ b/core/java/android/view/ViewAssistStructure.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import android.graphics.Rect;
+import android.os.Bundle;
 import android.text.TextPaint;
 
 /**
@@ -23,6 +25,37 @@
  * View.onProvideAssistStructure}.
  */
 public abstract class ViewAssistStructure {
+    public abstract void setId(int id, String packageName, String typeName, String entryName);
+
+    public abstract void setDimens(int left, int top, int scrollX, int scrollY, int width,
+            int height);
+
+    public abstract void setVisibility(int visibility);
+
+    public abstract void setEnabled(boolean state);
+
+    public abstract void setClickable(boolean state);
+
+    public abstract void setLongClickable(boolean state);
+
+    public abstract void setFocusable(boolean state);
+
+    public abstract void setFocused(boolean state);
+
+    public abstract void setAccessibilityFocused(boolean state);
+
+    public abstract void setCheckable(boolean state);
+
+    public abstract void setChecked(boolean state);
+
+    public abstract void setSelected(boolean state);
+
+    public abstract void setActivated(boolean state);
+
+    public abstract void setClassName(String className);
+
+    public abstract void setContentDescription(CharSequence contentDescription);
+
     public abstract void setText(CharSequence text);
     public abstract void setText(CharSequence text, int selectionStart, int selectionEnd);
     public abstract void setTextPaint(TextPaint paint);
@@ -32,4 +65,14 @@
     public abstract int getTextSelectionStart();
     public abstract int getTextSelectionEnd();
     public abstract CharSequence getHint();
+
+    public abstract Bundle editExtras();
+    public abstract void clearExtras();
+
+    public abstract void setChildCount(int num);
+    public abstract int getChildCount();
+    public abstract ViewAssistStructure newChild(int index);
+
+    /** @hide */
+    public abstract Rect getTempRect();
 }
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 50e64c6..a237afd 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1005,31 +1005,23 @@
             return fields;
         }
 
-        final ArrayList<Field> declaredFields = new ArrayList();
-        klass.getDeclaredFieldsUnchecked(false, declaredFields);
-
-        final ArrayList<Field> foundFields = new ArrayList<Field>();
-        final int count = declaredFields.size();
-        for (int i = 0; i < count; i++) {
-            final Field field = declaredFields.get(i);
-
-            // Ensure the field type can be resolved.
-            try {
-                field.getType();
-            } catch (NoClassDefFoundError e) {
-                continue;
+        try {
+            final Field[] declaredFields = klass.getDeclaredFieldsUnchecked(false);
+            final ArrayList<Field> foundFields = new ArrayList<Field>();
+            for (final Field field : declaredFields) {
+              // Fields which can't be resolved have a null type.
+              if (field.getType() != null && field.isAnnotationPresent(ExportedProperty.class)) {
+                  field.setAccessible(true);
+                  foundFields.add(field);
+                  sAnnotations.put(field, field.getAnnotation(ExportedProperty.class));
+              }
             }
-
-            if (field.isAnnotationPresent(ExportedProperty.class)) {
-                field.setAccessible(true);
-                foundFields.add(field);
-                sAnnotations.put(field, field.getAnnotation(ExportedProperty.class));
-            }
+            fields = foundFields.toArray(new Field[foundFields.size()]);
+            map.put(klass, fields);
+        } catch (NoClassDefFoundError e) {
+            throw new AssertionError(e);
         }
 
-        fields = foundFields.toArray(new Field[foundFields.size()]);
-        map.put(klass, fields);
-
         return fields;
     }
 
@@ -1651,4 +1643,4 @@
             }
         });
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 87f3e94..d0705bb 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2852,6 +2852,33 @@
         return false;
     }
 
+    /**
+     * Dispatch creation of {@link ViewAssistStructure} down the hierarchy.  This implementation
+     * adds in all child views of the view group, in addition to calling the default View
+     * implementation.
+     */
+    public void dispatchProvideAssistStructure(ViewAssistStructure structure) {
+        super.dispatchProvideAssistStructure(structure);
+        if (structure.getChildCount() == 0) {
+            final int childrenCount = getChildCount();
+            if (childrenCount > 0) {
+                structure.setChildCount(childrenCount);
+                final ArrayList<View> preorderedList = buildOrderedChildList();
+                final boolean customOrder = preorderedList == null
+                        && isChildrenDrawingOrderEnabled();
+                final View[] children = mChildren;
+                for (int i=0; i<childrenCount; i++) {
+                    final int childIndex = customOrder
+                            ? getChildDrawingOrder(childrenCount, i) : i;
+                    final View child = (preorderedList == null)
+                            ? children[childIndex] : preorderedList.get(childIndex);
+                    ViewAssistStructure cstructure = structure.newChild(i);
+                    child.dispatchProvideAssistStructure(cstructure);
+                }
+            }
+        }
+    }
+
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
@@ -5727,12 +5754,12 @@
             } else if (childDimension == LayoutParams.MATCH_PARENT) {
                 // Child wants to be our size... find out how big it should
                 // be
-                resultSize = 0;
+                resultSize = size;
                 resultMode = MeasureSpec.UNSPECIFIED;
             } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                 // Child wants to determine its own size.... find out how
                 // big it should be
-                resultSize = 0;
+                resultSize = size;
                 resultMode = MeasureSpec.UNSPECIFIED;
             }
             break;
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e790d4c..4158340 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -40,7 +40,6 @@
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.media.AudioManager;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
@@ -77,6 +76,7 @@
 
 import com.android.internal.R;
 import com.android.internal.os.SomeArgs;
+import com.android.internal.util.ScreenShapeHelper;
 import com.android.internal.view.BaseSurfaceHolder;
 import com.android.internal.view.RootViewSurfaceTaker;
 
@@ -118,10 +118,11 @@
      * at 60 Hz. This can be used to measure the potential framerate.
      */
     private static final String PROPERTY_PROFILE_RENDERING = "viewroot.profile_rendering";
-    private static final String PROPERTY_MEDIA_DISABLED = "config.disable_media";
 
-    // property used by emulator to determine display shape
+    // properties used by emulator to determine display shape
     public static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
+    public static final String PROPERTY_EMULATOR_WIN_OUTSET_BOTTOM_PX =
+            "ro.emu.win_outset_bottom_px";
 
     /**
      * Maximum time we allow the user to roll the trackball enough to generate
@@ -299,8 +300,6 @@
     private Choreographer.FrameCallback mRenderProfiler;
     private boolean mRenderProfilingEnabled;
 
-    private boolean mMediaDisabled;
-
     // Variables to track frames per second, enabled via DEBUG_FPS flag
     private long mFpsStartTime = -1;
     private long mFpsPrevTime = -1;
@@ -334,8 +333,6 @@
     /** Set to true once doDie() has been called. */
     private boolean mRemoved;
 
-    private boolean mIsEmulator;
-    private boolean mIsCircularEmulator;
     private final boolean mWindowIsRound;
 
     /**
@@ -392,8 +389,7 @@
         mChoreographer = Choreographer.getInstance();
         mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
         loadSystemProperties();
-        mWindowIsRound = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_windowIsRound);
+        mWindowIsRound = ScreenShapeHelper.getWindowIsRound(context.getResources());
     }
 
     public static void addFirstDrawHandler(Runnable callback) {
@@ -1248,9 +1244,8 @@
                 contentInsets = mPendingContentInsets;
                 stableInsets = mPendingStableInsets;
             }
-            final boolean isRound = (mIsEmulator && mIsCircularEmulator) || mWindowIsRound;
             mLastWindowInsets = new WindowInsets(contentInsets,
-                    null /* windowDecorInsets */, stableInsets, isRound);
+                    null /* windowDecorInsets */, stableInsets, mWindowIsRound);
         }
         return mLastWindowInsets;
     }
@@ -5362,10 +5357,6 @@
     public void playSoundEffect(int effectId) {
         checkThread();
 
-        if (mMediaDisabled) {
-            return;
-        }
-
         try {
             final AudioManager audioManager = getAudioManager();
 
@@ -5572,9 +5563,6 @@
                 mProfileRendering = SystemProperties.getBoolean(PROPERTY_PROFILE_RENDERING, false);
                 profileRendering(mAttachInfo.mHasWindowFocus);
 
-                // Media (used by sound effects)
-                mMediaDisabled = SystemProperties.getBoolean(PROPERTY_MEDIA_DISABLED, false);
-
                 // Hardware rendering
                 if (mAttachInfo.mHardwareRenderer != null) {
                     if (mAttachInfo.mHardwareRenderer.loadSystemProperties()) {
@@ -5590,11 +5578,6 @@
                         mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_WORLD, 200);
                     }
                 }
-
-                // detect emulator
-                mIsEmulator = Build.HARDWARE.contains("goldfish");
-                mIsCircularEmulator =
-                        SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false);
             }
         });
     }
@@ -6285,41 +6268,79 @@
 
 
             case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
-                if (mAccessibilityFocusedHost != null && mAccessibilityFocusedVirtualView != null) {
-                    // We care only for changes rooted in the focused host.
-                    final long eventSourceId = event.getSourceNodeId();
-                    final int hostViewId = AccessibilityNodeInfo.getAccessibilityViewId(
-                            eventSourceId);
-                    if (hostViewId != mAccessibilityFocusedHost.getAccessibilityViewId()) {
-                        break;
-                    }
-
-                    // We only care about changes that may change the virtual focused view bounds.
-                    final int changes = event.getContentChangeTypes();
-                    if ((changes & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) != 0
-                            || changes == AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED) {
-                        AccessibilityNodeProvider provider = mAccessibilityFocusedHost
-                                .getAccessibilityNodeProvider();
-                        if (provider != null) {
-                            final int virtualChildId = AccessibilityNodeInfo.getVirtualDescendantId(
-                                    mAccessibilityFocusedVirtualView.getSourceNodeId());
-                            if (virtualChildId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
-                                mAccessibilityFocusedVirtualView = provider
-                                        .createAccessibilityNodeInfo(
-                                                AccessibilityNodeProvider.HOST_VIEW_ID);
-                            } else {
-                                mAccessibilityFocusedVirtualView = provider
-                                        .createAccessibilityNodeInfo(virtualChildId);
-                            }
-                        }
-                    }
-                }
+                handleWindowContentChangedEvent(event);
             } break;
         }
         mAccessibilityManager.sendAccessibilityEvent(event);
         return true;
     }
 
+    /**
+     * Updates the focused virtual view, when necessary, in response to a
+     * content changed event.
+     * <p>
+     * This is necessary to get updated bounds after a position change.
+     *
+     * @param event an accessibility event of type
+     *              {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}
+     */
+    private void handleWindowContentChangedEvent(AccessibilityEvent event) {
+        // No virtual view focused, nothing to do here.
+        if (mAccessibilityFocusedHost == null || mAccessibilityFocusedVirtualView == null) {
+            return;
+        }
+
+        // If we have a node but no provider, abort.
+        final AccessibilityNodeProvider provider =
+                mAccessibilityFocusedHost.getAccessibilityNodeProvider();
+        if (provider == null) {
+            // TODO: Should we clear the focused virtual view?
+            return;
+        }
+
+        // We only care about change types that may affect the bounds of the
+        // focused virtual view.
+        final int changes = event.getContentChangeTypes();
+        if ((changes & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) == 0
+                && changes != AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED) {
+            return;
+        }
+
+        final long eventSourceNodeId = event.getSourceNodeId();
+        final int changedViewId = AccessibilityNodeInfo.getAccessibilityViewId(eventSourceNodeId);
+
+        // Search up the tree for subtree containment.
+        boolean hostInSubtree = false;
+        View root = mAccessibilityFocusedHost;
+        while (root != null && !hostInSubtree) {
+            if (changedViewId == root.getAccessibilityViewId()) {
+                hostInSubtree = true;
+            } else {
+                final ViewParent parent = root.getParent();
+                if (parent instanceof View) {
+                    root = (View) parent;
+                } else {
+                    root = null;
+                }
+            }
+        }
+
+        // We care only about changes in subtrees containing the host view.
+        if (!hostInSubtree) {
+            return;
+        }
+
+        final long focusedSourceNodeId = mAccessibilityFocusedVirtualView.getSourceNodeId();
+        int focusedChildId = AccessibilityNodeInfo.getVirtualDescendantId(focusedSourceNodeId);
+        if (focusedChildId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+            // TODO: Should we clear the focused virtual view?
+            focusedChildId = AccessibilityNodeProvider.HOST_VIEW_ID;
+        }
+
+        // Refresh the node for the focused virtual view.
+        mAccessibilityFocusedVirtualView = provider.createAccessibilityNodeInfo(focusedChildId);
+    }
+
     @Override
     public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) {
         postSendWindowContentChangedCallback(source, changeType);
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 9a92932..36f047e 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1856,14 +1856,14 @@
     public abstract int getStatusBarColor();
 
     /**
-     * Sets the color of the status bar to {@param color}.
+     * Sets the color of the status bar to {@code color}.
      *
      * For this to take effect,
      * the window must be drawing the system bar backgrounds with
      * {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} and
      * {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_STATUS} must not be set.
      *
-     * If {@param color} is not opaque, consider setting
+     * If {@code color} is not opaque, consider setting
      * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
      * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.
      * <p>
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 6096d7d..ec527d5 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -29,6 +29,8 @@
 import android.util.Pools.SynchronizedPool;
 import android.view.View;
 
+import com.android.internal.R;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -1560,7 +1562,7 @@
     }
 
     /**
-     * Sets whether this node is visible to the user.
+     * Gets whether this node is visible to the user.
      *
      * @return Whether the node is visible to the user.
      */
@@ -3402,6 +3404,15 @@
                 new AccessibilityAction(
                         AccessibilityNodeInfo.ACTION_SET_TEXT, null);
 
+        /**
+         * Action that requests the node make its bounding rectangle visible
+         * on the screen, scrolling if necessary just enough.
+         *
+         * @see View#requestRectangleOnScreen(Rect)
+         */
+        public static final AccessibilityAction ACTION_SHOW_ON_SCREEN =
+                new AccessibilityAction(R.id.accessibility_action_show_on_screen, null);
+
         private static final ArraySet<AccessibilityAction> sStandardActions = new ArraySet<AccessibilityAction>();
         static {
             sStandardActions.add(ACTION_FOCUS);
@@ -3426,6 +3437,7 @@
             sStandardActions.add(ACTION_COLLAPSE);
             sStandardActions.add(ACTION_DISMISS);
             sStandardActions.add(ACTION_SET_TEXT);
+            sStandardActions.add(ACTION_SHOW_ON_SCREEN);
         }
 
         private final int mActionId;
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 4737e9b..0b18bb8 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -284,13 +284,19 @@
      * currently set for that origin. The host application should invoke the
      * specified callback with the desired permission state. See
      * {@link GeolocationPermissions} for details.
+     *
+     * If this method isn't overridden, the callback is invoked with permission
+     * denied state.
+     *
      * @param origin The origin of the web content attempting to use the
      *               Geolocation API.
      * @param callback The callback to use to set the permission state for the
      *                 origin.
      */
     public void onGeolocationPermissionsShowPrompt(String origin,
-            GeolocationPermissions.Callback callback) {}
+            GeolocationPermissions.Callback callback) {
+        callback.invoke(origin, false, false);
+    }
 
     /**
      * Notify the host application that a request for Geolocation permissions,
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 53c7e04..8a2b3fa 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -200,8 +200,6 @@
     public static final int ERROR_FILE_NOT_FOUND = -14;
     /** Too many requests during this load */
     public static final int ERROR_TOO_MANY_REQUESTS = -15;
-    /** Request blocked by the browser */
-    public static final int ERROR_BLOCKED = -16;
 
     /**
      * Report an error to the host application. These errors are unrecoverable
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 1716dbd..45eee34 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -40,8 +40,10 @@
  * @attr ref android.R.styleable#AnalogClock_dial
  * @attr ref android.R.styleable#AnalogClock_hand_hour
  * @attr ref android.R.styleable#AnalogClock_hand_minute
+ * @deprecated This widget is no longer supported.
  */
 @RemoteView
+@Deprecated
 public class AnalogClock extends View {
     private Time mCalendar;
 
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index 5c05b5a..6feb94b 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -16,6 +16,7 @@
 */
 package android.widget;
 
+import android.os.UserHandle;
 import com.android.internal.R;
 
 import android.app.AlertDialog;
@@ -243,7 +244,8 @@
                 @Override
                 public void onClick(DialogInterface dialog, int which) {
                     PackageManager pm = getContext().getPackageManager();
-                    pm.revokePermission(mPackageName, mPerm.name);
+                    pm.revokePermission(mPackageName, mPerm.name,
+                            new UserHandle(mContext.getUserId()));
                     PermissionItemView.this.setVisibility(View.GONE);
                 }
             };
@@ -298,7 +300,7 @@
             }
             extractPerms(info, permSet, installedPkgInfo);
         }
-        // Get permissions related to  shared user if any
+        // Get permissions related to shared user if any
         if (info.sharedUserId != null) {
             int sharedUid;
             try {
@@ -358,7 +360,7 @@
             String permName = strList[i];
             // If we are only looking at an existing app, then we only
             // care about permissions that have actually been granted to it.
-            if (installedPkgInfo != null && info == installedPkgInfo) {
+            if (installedPkgInfo != null && info != installedPkgInfo) {
                 if ((flagsList[i]&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0) {
                     continue;
                 }
diff --git a/core/java/android/widget/CalendarView.java b/core/java/android/widget/CalendarView.java
index 5bc16cb..2aaa356 100644
--- a/core/java/android/widget/CalendarView.java
+++ b/core/java/android/widget/CalendarView.java
@@ -18,6 +18,7 @@
 
 import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
+import android.annotation.StyleRes;
 import android.annotation.Widget;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -31,6 +32,7 @@
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
+import java.util.Date;
 import java.util.Locale;
 import java.util.TimeZone;
 
@@ -118,7 +120,9 @@
      * @param count The shown week count.
      *
      * @attr ref android.R.styleable#CalendarView_shownWeekCount
+     * @deprecated No longer used by Material-style CalendarView.
      */
+    @Deprecated
     public void setShownWeekCount(int count) {
         mDelegate.setShownWeekCount(count);
     }
@@ -129,7 +133,9 @@
      * @return The shown week count.
      *
      * @attr ref android.R.styleable#CalendarView_shownWeekCount
+     * @deprecated No longer used by Material-style CalendarView.
      */
+    @Deprecated
     public int getShownWeekCount() {
         return mDelegate.getShownWeekCount();
     }
@@ -140,7 +146,9 @@
      * @param color The week background color.
      *
      * @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor
+     * @deprecated No longer used by Material-style CalendarView.
      */
+    @Deprecated
     public void setSelectedWeekBackgroundColor(@ColorInt int color) {
         mDelegate.setSelectedWeekBackgroundColor(color);
     }
@@ -151,8 +159,10 @@
      * @return The week background color.
      *
      * @attr ref android.R.styleable#CalendarView_selectedWeekBackgroundColor
+     * @deprecated No longer used by Material-style CalendarView.
      */
     @ColorInt
+    @Deprecated
     public int getSelectedWeekBackgroundColor() {
         return mDelegate.getSelectedWeekBackgroundColor();
     }
@@ -163,7 +173,9 @@
      * @param color The focused month date color.
      *
      * @attr ref android.R.styleable#CalendarView_focusedMonthDateColor
+     * @deprecated No longer used by Material-style CalendarView.
      */
+    @Deprecated
     public void setFocusedMonthDateColor(@ColorInt int color) {
         mDelegate.setFocusedMonthDateColor(color);
     }
@@ -174,8 +186,10 @@
      * @return The focused month date color.
      *
      * @attr ref android.R.styleable#CalendarView_focusedMonthDateColor
+     * @deprecated No longer used by Material-style CalendarView.
      */
     @ColorInt
+    @Deprecated
     public int getFocusedMonthDateColor() {
         return mDelegate.getFocusedMonthDateColor();
     }
@@ -186,7 +200,9 @@
      * @param color A not focused month date color.
      *
      * @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor
+     * @deprecated No longer used by Material-style CalendarView.
      */
+    @Deprecated
     public void setUnfocusedMonthDateColor(@ColorInt int color) {
         mDelegate.setUnfocusedMonthDateColor(color);
     }
@@ -197,8 +213,10 @@
      * @return A not focused month date color.
      *
      * @attr ref android.R.styleable#CalendarView_unfocusedMonthDateColor
+     * @deprecated No longer used by Material-style CalendarView.
      */
     @ColorInt
+    @Deprecated
     public int getUnfocusedMonthDateColor() {
         return mDelegate.getUnfocusedMonthDateColor();
     }
@@ -209,7 +227,9 @@
      * @param color The week number color.
      *
      * @attr ref android.R.styleable#CalendarView_weekNumberColor
+     * @deprecated No longer used by Material-style CalendarView.
      */
+    @Deprecated
     public void setWeekNumberColor(@ColorInt int color) {
         mDelegate.setWeekNumberColor(color);
     }
@@ -220,8 +240,10 @@
      * @return The week number color.
      *
      * @attr ref android.R.styleable#CalendarView_weekNumberColor
+     * @deprecated No longer used by Material-style CalendarView.
      */
     @ColorInt
+    @Deprecated
     public int getWeekNumberColor() {
         return mDelegate.getWeekNumberColor();
     }
@@ -232,7 +254,9 @@
      * @param color The week separator color.
      *
      * @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor
+     * @deprecated No longer used by Material-style CalendarView.
      */
+    @Deprecated
     public void setWeekSeparatorLineColor(@ColorInt int color) {
         mDelegate.setWeekSeparatorLineColor(color);
     }
@@ -243,8 +267,10 @@
      * @return The week separator color.
      *
      * @attr ref android.R.styleable#CalendarView_weekSeparatorLineColor
+     * @deprecated No longer used by Material-style CalendarView.
      */
     @ColorInt
+    @Deprecated
     public int getWeekSeparatorLineColor() {
         return mDelegate.getWeekSeparatorLineColor();
     }
@@ -256,7 +282,9 @@
      * @param resourceId The vertical bar drawable resource id.
      *
      * @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar
+     * @deprecated No longer used by Material-style CalendarView.
      */
+    @Deprecated
     public void setSelectedDateVerticalBar(@DrawableRes int resourceId) {
         mDelegate.setSelectedDateVerticalBar(resourceId);
     }
@@ -268,7 +296,9 @@
      * @param drawable The vertical bar drawable.
      *
      * @attr ref android.R.styleable#CalendarView_selectedDateVerticalBar
+     * @deprecated No longer used by Material-style CalendarView.
      */
+    @Deprecated
     public void setSelectedDateVerticalBar(Drawable drawable) {
         mDelegate.setSelectedDateVerticalBar(drawable);
     }
@@ -278,7 +308,9 @@
      * the end of the selected date.
      *
      * @return The vertical bar drawable.
+     * @deprecated No longer used by Material-style CalendarView.
      */
+    @Deprecated
     public Drawable getSelectedDateVerticalBar() {
         return mDelegate.getSelectedDateVerticalBar();
     }
@@ -519,29 +551,36 @@
         void setShownWeekCount(int count);
         int getShownWeekCount();
 
-        void setSelectedWeekBackgroundColor(int color);
+        void setSelectedWeekBackgroundColor(@ColorInt int color);
+        @ColorInt
         int getSelectedWeekBackgroundColor();
 
-        void setFocusedMonthDateColor(int color);
+        void setFocusedMonthDateColor(@ColorInt int color);
+        @ColorInt
         int getFocusedMonthDateColor();
 
-        void setUnfocusedMonthDateColor(int color);
+        void setUnfocusedMonthDateColor(@ColorInt int color);
+        @ColorInt
         int getUnfocusedMonthDateColor();
 
-        void setWeekNumberColor(int color);
+        void setWeekNumberColor(@ColorInt int color);
+        @ColorInt
         int getWeekNumberColor();
 
-        void setWeekSeparatorLineColor(int color);
+        void setWeekSeparatorLineColor(@ColorInt int color);
+        @ColorInt
         int getWeekSeparatorLineColor();
 
-        void setSelectedDateVerticalBar(int resourceId);
+        void setSelectedDateVerticalBar(@DrawableRes int resourceId);
         void setSelectedDateVerticalBar(Drawable drawable);
         Drawable getSelectedDateVerticalBar();
 
-        void setWeekDayTextAppearance(int resourceId);
+        void setWeekDayTextAppearance(@StyleRes int resourceId);
+        @StyleRes
         int getWeekDayTextAppearance();
 
-        void setDateTextAppearance(int resourceId);
+        void setDateTextAppearance(@StyleRes int resourceId);
+        @StyleRes
         int getDateTextAppearance();
 
         void setMinDate(long minDate);
@@ -569,18 +608,12 @@
      * An abstract class which can be used as a start for CalendarView implementations
      */
     abstract static class AbstractCalendarViewDelegate implements CalendarViewDelegate {
-        /** String for parsing dates. */
-        private static final String DATE_FORMAT = "MM/dd/yyyy";
-
         /** The default minimal date. */
         protected static final String DEFAULT_MIN_DATE = "01/01/1900";
 
         /** The default maximal date. */
         protected static final String DEFAULT_MAX_DATE = "01/01/2100";
 
-        /** Date format for parsing dates. */
-        protected static final DateFormat DATE_FORMATTER = new SimpleDateFormat(DATE_FORMAT);
-
         protected CalendarView mDelegator;
         protected Context mContext;
         protected Locale mCurrentLocale;
@@ -600,21 +633,131 @@
             mCurrentLocale = locale;
         }
 
-        /**
-         * Parses the given <code>date</code> and in case of success sets
-         * the result to the <code>outDate</code>.
-         *
-         * @return True if the date was parsed.
-         */
-        protected boolean parseDate(String date, Calendar outDate) {
-            try {
-                outDate.setTime(DATE_FORMATTER.parse(date));
-                return true;
-            } catch (ParseException e) {
-                Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
-                return false;
-            }
+        @Override
+        public void setShownWeekCount(int count) {
+            // Deprecated.
+        }
+
+        @Override
+        public int getShownWeekCount() {
+            // Deprecated.
+            return 0;
+        }
+
+        @Override
+        public void setSelectedWeekBackgroundColor(@ColorInt int color) {
+            // Deprecated.
+        }
+
+        @ColorInt
+        @Override
+        public int getSelectedWeekBackgroundColor() {
+            return 0;
+        }
+
+        @Override
+        public void setFocusedMonthDateColor(@ColorInt int color) {
+            // Deprecated.
+        }
+
+        @ColorInt
+        @Override
+        public int getFocusedMonthDateColor() {
+            return 0;
+        }
+
+        @Override
+        public void setUnfocusedMonthDateColor(@ColorInt int color) {
+            // Deprecated.
+        }
+
+        @ColorInt
+        @Override
+        public int getUnfocusedMonthDateColor() {
+            return 0;
+        }
+
+        @Override
+        public void setWeekNumberColor(@ColorInt int color) {
+            // Deprecated.
+        }
+
+        @ColorInt
+        @Override
+        public int getWeekNumberColor() {
+            // Deprecated.
+            return 0;
+        }
+
+        @Override
+        public void setWeekSeparatorLineColor(@ColorInt int color) {
+            // Deprecated.
+        }
+
+        @ColorInt
+        @Override
+        public int getWeekSeparatorLineColor() {
+            // Deprecated.
+            return 0;
+        }
+
+        @Override
+        public void setSelectedDateVerticalBar(@DrawableRes int resId) {
+            // Deprecated.
+        }
+
+        @Override
+        public void setSelectedDateVerticalBar(Drawable drawable) {
+            // Deprecated.
+        }
+
+        @Override
+        public Drawable getSelectedDateVerticalBar() {
+            // Deprecated.
+            return null;
+        }
+
+        @Override
+        public void setShowWeekNumber(boolean showWeekNumber) {
+            // Deprecated.
+        }
+
+        @Override
+        public boolean getShowWeekNumber() {
+            // Deprecated.
+            return false;
+        }
+
+        @Override
+        public void onConfigurationChanged(Configuration newConfig) {
+            // Nothing to do here, configuration changes are already propagated
+            // by ViewGroup.
         }
     }
 
+    /** String for parsing dates. */
+    private static final String DATE_FORMAT = "MM/dd/yyyy";
+
+    /** Date format for parsing dates. */
+    private static final DateFormat DATE_FORMATTER = new SimpleDateFormat(DATE_FORMAT);
+
+    /**
+     * Utility method for the date format used by CalendarView's min/max date.
+     *
+     * @hide Use only as directed. For internal use only.
+     */
+    public static boolean parseDate(String date, Calendar outDate) {
+        if (date == null || date.isEmpty()) {
+            return false;
+        }
+
+        try {
+            final Date parsedDate = DATE_FORMATTER.parse(date);
+            outDate.setTime(parsedDate);
+            return true;
+        } catch (ParseException e) {
+            Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
+            return false;
+        }
+    }
 }
diff --git a/core/java/android/widget/CalendarViewLegacyDelegate.java b/core/java/android/widget/CalendarViewLegacyDelegate.java
index 2ab3548..6ab3828 100644
--- a/core/java/android/widget/CalendarViewLegacyDelegate.java
+++ b/core/java/android/widget/CalendarViewLegacyDelegate.java
@@ -27,7 +27,6 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
@@ -267,12 +266,12 @@
         mFirstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
                 LocaleData.get(Locale.getDefault()).firstDayOfWeek);
         final String minDate = a.getString(R.styleable.CalendarView_minDate);
-        if (TextUtils.isEmpty(minDate) || !parseDate(minDate, mMinDate)) {
-            parseDate(DEFAULT_MIN_DATE, mMinDate);
+        if (!CalendarView.parseDate(minDate, mMinDate)) {
+            CalendarView.parseDate(DEFAULT_MIN_DATE, mMinDate);
         }
         final String maxDate = a.getString(R.styleable.CalendarView_maxDate);
-        if (TextUtils.isEmpty(maxDate) || !parseDate(maxDate, mMaxDate)) {
-            parseDate(DEFAULT_MAX_DATE, mMaxDate);
+        if (!CalendarView.parseDate(maxDate, mMaxDate)) {
+            CalendarView.parseDate(DEFAULT_MAX_DATE, mMaxDate);
         }
         if (mMaxDate.before(mMinDate)) {
             throw new IllegalArgumentException("Max date cannot be before min date.");
diff --git a/core/java/android/widget/CalendarViewMaterialDelegate.java b/core/java/android/widget/CalendarViewMaterialDelegate.java
index b0f3740..7bce756 100644
--- a/core/java/android/widget/CalendarViewMaterialDelegate.java
+++ b/core/java/android/widget/CalendarViewMaterialDelegate.java
@@ -16,20 +16,11 @@
 
 package android.widget;
 
-import com.android.internal.R;
-
+import android.annotation.StyleRes;
 import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
-import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.util.MathUtils;
 
 import java.util.Calendar;
-import java.util.Locale;
-
-import libcore.icu.LocaleData;
 
 class CalendarViewMaterialDelegate extends CalendarView.AbstractCalendarViewDelegate {
     private final DayPickerView mDayPickerView;
@@ -40,142 +31,32 @@
             int defStyleAttr, int defStyleRes) {
         super(delegator, context);
 
-        final TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.CalendarView, defStyleAttr, defStyleRes);
-        final int firstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
-                LocaleData.get(Locale.getDefault()).firstDayOfWeek);
-
-        final long minDate = parseDateToMillis(a.getString(
-                R.styleable.CalendarView_minDate), DEFAULT_MIN_DATE);
-        final long maxDate = parseDateToMillis(a.getString(
-                R.styleable.CalendarView_maxDate), DEFAULT_MAX_DATE);
-        if (maxDate < minDate) {
-            throw new IllegalArgumentException("max date cannot be before min date");
-        }
-
-        final long setDate = MathUtils.constrain(System.currentTimeMillis(), minDate, maxDate);
-        final int dateTextAppearanceResId = a.getResourceId(
-                R.styleable.CalendarView_dateTextAppearance,
-                R.style.TextAppearance_DeviceDefault_Small);
-
-        a.recycle();
-
-        mDayPickerView = new DayPickerView(context);
-        mDayPickerView.setFirstDayOfWeek(firstDayOfWeek);
-        mDayPickerView.setCalendarTextAppearance(dateTextAppearanceResId);
-        mDayPickerView.setMinDate(minDate);
-        mDayPickerView.setMaxDate(maxDate);
-        mDayPickerView.setDate(setDate, false, true);
+        mDayPickerView = new DayPickerView(context, attrs, defStyleAttr, defStyleRes);
         mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener);
 
         delegator.addView(mDayPickerView);
     }
 
-    private long parseDateToMillis(String dateStr, String defaultDateStr) {
-        final Calendar tempCalendar = Calendar.getInstance();
-        if (TextUtils.isEmpty(dateStr) || !parseDate(dateStr, tempCalendar)) {
-            parseDate(defaultDateStr, tempCalendar);
-        }
-        return tempCalendar.getTimeInMillis();
-    }
-
     @Override
-    public void setShownWeekCount(int count) {
-        // Deprecated.
+    public void setWeekDayTextAppearance(@StyleRes int resId) {
+        mDayPickerView.setDayOfWeekTextAppearance(resId);
     }
 
-    @Override
-    public int getShownWeekCount() {
-        // Deprecated.
-        return 0;
-    }
-
-    @Override
-    public void setSelectedWeekBackgroundColor(int color) {
-        // TODO: Should use a ColorStateList. Deprecate?
-    }
-
-    @Override
-    public int getSelectedWeekBackgroundColor() {
-        return 0;
-    }
-
-    @Override
-    public void setFocusedMonthDateColor(int color) {
-        // TODO: Should use a ColorStateList. Deprecate?
-    }
-
-    @Override
-    public int getFocusedMonthDateColor() {
-        return 0;
-    }
-
-    @Override
-    public void setUnfocusedMonthDateColor(int color) {
-        // TODO: Should use a ColorStateList. Deprecate?
-    }
-
-    @Override
-    public int getUnfocusedMonthDateColor() {
-        return 0;
-    }
-
-    @Override
-    public void setWeekDayTextAppearance(int resourceId) {
-
-    }
-
+    @StyleRes
     @Override
     public int getWeekDayTextAppearance() {
-        return 0;
+        return mDayPickerView.getDayOfWeekTextAppearance();
     }
 
     @Override
-    public void setDateTextAppearance(int resourceId) {
-
+    public void setDateTextAppearance(@StyleRes int resId) {
+        mDayPickerView.setDayTextAppearance(resId);
     }
 
+    @StyleRes
     @Override
     public int getDateTextAppearance() {
-        return 0;
-    }
-
-    @Override
-    public void setWeekNumberColor(int color) {
-        // Deprecated.
-    }
-
-    @Override
-    public int getWeekNumberColor() {
-        // Deprecated.
-        return 0;
-    }
-
-    @Override
-    public void setWeekSeparatorLineColor(int color) {
-        // Deprecated.
-    }
-
-    @Override
-    public int getWeekSeparatorLineColor() {
-        // Deprecated.
-        return 0;
-    }
-
-    @Override
-    public void setSelectedDateVerticalBar(int resourceId) {
-        // Deprecated.
-    }
-
-    @Override
-    public void setSelectedDateVerticalBar(Drawable drawable) {
-        // Deprecated.
-    }
-
-    @Override
-    public Drawable getSelectedDateVerticalBar() {
-        // Deprecated.
-        return null;
+        return mDayPickerView.getDayTextAppearance();
     }
 
     @Override
@@ -199,17 +80,6 @@
     }
 
     @Override
-    public void setShowWeekNumber(boolean showWeekNumber) {
-        // Deprecated.
-    }
-
-    @Override
-    public boolean getShowWeekNumber() {
-        // Deprecated.
-        return false;
-    }
-
-    @Override
     public void setFirstDayOfWeek(int firstDayOfWeek) {
         mDayPickerView.setFirstDayOfWeek(firstDayOfWeek);
     }
@@ -221,12 +91,12 @@
 
     @Override
     public void setDate(long date) {
-        mDayPickerView.setDate(date, true, false);
+        mDayPickerView.setDate(date, true);
     }
 
     @Override
     public void setDate(long date, boolean animate, boolean center) {
-        mDayPickerView.setDate(date, animate, center);
+        mDayPickerView.setDate(date, animate);
     }
 
     @Override
@@ -239,12 +109,6 @@
         mOnDateChangeListener = listener;
     }
 
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        // Nothing to do here, configuration changes are already propagated
-        // by ViewGroup.
-    }
-
     private final DayPickerView.OnDaySelectedListener mOnDaySelectedListener =
             new DayPickerView.OnDaySelectedListener() {
         @Override
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index 45998f7..5f5943f 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -98,7 +98,7 @@
     private final DatePickerDelegate mDelegate;
 
     /**
-     * The callback used to indicate the user changes\d the date.
+     * The callback used to indicate the user changed the date.
      */
     public interface OnDateChangedListener {
 
@@ -348,9 +348,13 @@
     }
 
     /**
-     * Gets whether the {@link CalendarView} is shown.
+     * Returns whether the {@link CalendarView} is shown.
+     * <p>
+     * <strong>Note:</strong> This method returns {@code false} when the
+     * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
+     * to {@code calendar}.
      *
-     * @return True if the calendar view is shown.
+     * @return {@code true} if the calendar view is shown
      * @see #getCalendarView()
      */
     public boolean getCalendarViewShown() {
@@ -358,13 +362,13 @@
     }
 
     /**
-     * Gets the {@link CalendarView}.
+     * Returns the {@link CalendarView} used by this picker.
      * <p>
-     * This method returns {@code null} when the
+     * <strong>Note:</strong> This method returns {@code null} when the
      * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
      * to {@code calendar}.
      *
-     * @return The calendar view.
+     * @return the calendar view
      * @see #getCalendarViewShown()
      */
     public CalendarView getCalendarView() {
@@ -374,20 +378,25 @@
     /**
      * Sets whether the {@link CalendarView} is shown.
      * <p>
-     * Calling this method has no effect when the
+     * <strong>Note:</strong> Calling this method has no effect when the
      * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
      * to {@code calendar}.
      *
-     * @param shown True if the calendar view is to be shown.
+     * @param shown {@code true} to show the calendar view, {@code false} to
+     *              hide it
      */
     public void setCalendarViewShown(boolean shown) {
         mDelegate.setCalendarViewShown(shown);
     }
 
     /**
-     * Gets whether the spinners are shown.
+     * Returns whether the spinners are shown.
+     * <p>
+     * <strong>Note:</strong> his method returns {@code false} when the
+     * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
+     * to {@code calendar}.
      *
-     * @return True if the spinners are shown.
+     * @return {@code true} if the spinners are shown
      */
     public boolean getSpinnersShown() {
         return mDelegate.getSpinnersShown();
@@ -395,8 +404,13 @@
 
     /**
      * Sets whether the spinners are shown.
+     * <p>
+     * Calling this method has no effect when the
+     * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
+     * to {@code calendar}.
      *
-     * @param shown True if the spinners are to be shown.
+     * @param shown {@code true} to show the spinners, {@code false} to hide
+     *              them
      */
     public void setSpinnersShown(boolean shown) {
         mDelegate.setSpinnersShown(shown);
@@ -489,15 +503,14 @@
             mDelegator = delegator;
             mContext = context;
 
-            // initialization based on locale
             setCurrentLocale(Locale.getDefault());
         }
 
         protected void setCurrentLocale(Locale locale) {
-            if (locale.equals(mCurrentLocale)) {
-                return;
+            if (!locale.equals(mCurrentLocale)) {
+                mCurrentLocale = locale;
+                onLocaleChanged(locale);
             }
-            mCurrentLocale = locale;
         }
 
         @Override
@@ -510,6 +523,10 @@
                 mValidationCallback.onValidationChanged(valid);
             }
         }
+
+        protected void onLocaleChanged(Locale locale) {
+            // Stub.
+        }
     }
 
     /**
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 0e3ec7f..a157087 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -16,6 +16,7 @@
 
 package android.widget;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
@@ -26,90 +27,83 @@
 import android.text.format.DateFormat;
 import android.text.format.DateUtils;
 import android.util.AttributeSet;
+import android.util.StateSet;
 import android.view.HapticFeedbackConstants;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
+import android.widget.DayPickerView.OnDaySelectedListener;
+import android.widget.YearPickerView.OnYearSelectedListener;
 
 import com.android.internal.R;
-import com.android.internal.widget.AccessibleDateAnimator;
 
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
-import java.util.HashSet;
 import java.util.Locale;
 
 /**
  * A delegate for picking up a date (day / month / year).
  */
-class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate implements
-        View.OnClickListener, DatePickerController {
+class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
     private static final int USE_LOCALE = 0;
 
     private static final int UNINITIALIZED = -1;
-    private static final int MONTH_AND_DAY_VIEW = 0;
-    private static final int YEAR_VIEW = 1;
+    private static final int VIEW_MONTH_DAY = 0;
+    private static final int VIEW_YEAR = 1;
 
     private static final int DEFAULT_START_YEAR = 1900;
     private static final int DEFAULT_END_YEAR = 2100;
 
     private static final int ANIMATION_DURATION = 300;
 
-    private static final int MONTH_INDEX = 0;
-    private static final int DAY_INDEX = 1;
-    private static final int YEAR_INDEX = 2;
+    private static final int[] ATTRS_TEXT_COLOR = new int[] {
+            com.android.internal.R.attr.textColor};
+    private static final int[] ATTRS_DISABLED_ALPHA = new int[] {
+            com.android.internal.R.attr.disabledAlpha};
 
-    private SimpleDateFormat mYearFormat = new SimpleDateFormat("y", Locale.getDefault());
-    private SimpleDateFormat mDayFormat = new SimpleDateFormat("d", Locale.getDefault());
+    private SimpleDateFormat mYearFormat;
+    private SimpleDateFormat mMonthDayFormat;
 
-    private TextView mDayOfWeekView;
+    // Top-level container.
+    private ViewGroup mContainer;
 
-    /** Layout that contains the current month, day, and year. */
-    private LinearLayout mMonthDayYearLayout;
+    // Header views.
+    private TextView mHeaderYear;
+    private TextView mHeaderMonthDay;
 
-    /** Clickable layout that contains the current day and year. */
-    private LinearLayout mMonthAndDayLayout;
-
-    private TextView mHeaderMonthTextView;
-    private TextView mHeaderDayOfMonthTextView;
-    private TextView mHeaderYearTextView;
+    // Picker views.
+    private ViewAnimator mAnimator;
     private DayPickerView mDayPickerView;
     private YearPickerView mYearPickerView;
 
-    private boolean mIsEnabled = true;
-
     // Accessibility strings.
-    private String mDayPickerDescription;
     private String mSelectDay;
-    private String mYearPickerDescription;
     private String mSelectYear;
 
-    private AccessibleDateAnimator mAnimator;
-
     private DatePicker.OnDateChangedListener mDateChangedListener;
 
     private int mCurrentView = UNINITIALIZED;
 
-    private Calendar mCurrentDate;
-    private Calendar mTempDate;
-    private Calendar mMinDate;
-    private Calendar mMaxDate;
+    private final Calendar mCurrentDate;
+    private final Calendar mTempDate;
+    private final Calendar mMinDate;
+    private final Calendar mMaxDate;
 
     private int mFirstDayOfWeek = USE_LOCALE;
 
-    private HashSet<OnDateChangedListener> mListeners = new HashSet<OnDateChangedListener>();
-
     public DatePickerCalendarDelegate(DatePicker delegator, Context context, AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
         super(delegator, context);
 
-        final Locale locale = Locale.getDefault();
-        mMinDate = getCalendarForLocale(mMinDate, locale);
-        mMaxDate = getCalendarForLocale(mMaxDate, locale);
-        mTempDate = getCalendarForLocale(mMaxDate, locale);
-        mCurrentDate = getCalendarForLocale(mCurrentDate, locale);
+        final Locale locale = mCurrentLocale;
+        mCurrentDate = Calendar.getInstance(locale);
+        mTempDate = Calendar.getInstance(locale);
+        mMinDate = Calendar.getInstance(locale);
+        mMaxDate = Calendar.getInstance(locale);
 
         mMinDate.set(DEFAULT_START_YEAR, Calendar.JANUARY, 1);
         mMaxDate.set(DEFAULT_END_YEAR, Calendar.DECEMBER, 31);
@@ -120,242 +114,246 @@
         final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
         final int layoutResourceId = a.getResourceId(
-                R.styleable.DatePicker_internalLayout, R.layout.date_picker_holo);
-        final View mainView = inflater.inflate(layoutResourceId, null);
-        mDelegator.addView(mainView);
+                R.styleable.DatePicker_internalLayout, R.layout.date_picker_material);
 
-        mDayOfWeekView = (TextView) mainView.findViewById(R.id.date_picker_header);
+        // Set up and attach container.
+        mContainer = (ViewGroup) inflater.inflate(layoutResourceId, mDelegator);
 
-        // Layout that contains the current date and day name header.
-        final LinearLayout dateLayout = (LinearLayout) mainView.findViewById(
-                R.id.day_picker_selector_layout);
-        mMonthDayYearLayout = (LinearLayout) mainView.findViewById(
-                R.id.date_picker_month_day_year_layout);
-        mMonthAndDayLayout = (LinearLayout) mainView.findViewById(
-                R.id.date_picker_month_and_day_layout);
-        mMonthAndDayLayout.setOnClickListener(this);
-        mHeaderMonthTextView = (TextView) mainView.findViewById(R.id.date_picker_month);
-        mHeaderDayOfMonthTextView = (TextView) mainView.findViewById(R.id.date_picker_day);
-        mHeaderYearTextView = (TextView) mainView.findViewById(R.id.date_picker_year);
-        mHeaderYearTextView.setOnClickListener(this);
+        // Set up header views.
+        final ViewGroup header = (ViewGroup) mContainer.findViewById(R.id.date_picker_header);
+        mHeaderYear = (TextView) header.findViewById(R.id.date_picker_header_year);
+        mHeaderYear.setOnClickListener(mOnHeaderClickListener);
+        mHeaderMonthDay = (TextView) header.findViewById(R.id.date_picker_header_date);
+        mHeaderMonthDay.setOnClickListener(mOnHeaderClickListener);
 
-        // Obtain default highlight color from the theme.
-        final int defaultHighlightColor = mHeaderYearTextView.getHighlightColor();
+        // For the sake of backwards compatibility, attempt to extract the text
+        // color from the header month text appearance. If it's set, we'll let
+        // that override the "real" header text color.
+        ColorStateList headerTextColor = null;
 
-        // Use Theme attributes if possible
-        final int dayOfWeekTextAppearanceResId = a.getResourceId(
-                R.styleable.DatePicker_dayOfWeekTextAppearance, 0);
-        if (dayOfWeekTextAppearanceResId != 0) {
-            mDayOfWeekView.setTextAppearance(context, dayOfWeekTextAppearanceResId);
-        }
-
-        mDayOfWeekView.setBackground(a.getDrawable(R.styleable.DatePicker_dayOfWeekBackground));
-
-        dateLayout.setBackground(a.getDrawable(R.styleable.DatePicker_headerBackground));
-
-        final int monthTextAppearanceResId = a.getResourceId(
+        @SuppressWarnings("deprecation")
+        final int monthHeaderTextAppearance = a.getResourceId(
                 R.styleable.DatePicker_headerMonthTextAppearance, 0);
-        if (monthTextAppearanceResId != 0) {
-            mHeaderMonthTextView.setTextAppearance(context, monthTextAppearanceResId);
+        if (monthHeaderTextAppearance != 0) {
+            final TypedArray textAppearance = mContext.obtainStyledAttributes(null,
+                    ATTRS_TEXT_COLOR, 0, monthHeaderTextAppearance);
+            final ColorStateList legacyHeaderTextColor = textAppearance.getColorStateList(0);
+            headerTextColor = applyLegacyColorFixes(legacyHeaderTextColor);
+            textAppearance.recycle();
         }
 
-        final int dayOfMonthTextAppearanceResId = a.getResourceId(
-                R.styleable.DatePicker_headerDayOfMonthTextAppearance, 0);
-        if (dayOfMonthTextAppearanceResId != 0) {
-            mHeaderDayOfMonthTextView.setTextAppearance(context, dayOfMonthTextAppearanceResId);
+        if (headerTextColor == null) {
+            headerTextColor = a.getColorStateList(R.styleable.DatePicker_headerTextColor);
         }
 
-        final int headerYearTextAppearanceResId = a.getResourceId(
-                R.styleable.DatePicker_headerYearTextAppearance, 0);
-        if (headerYearTextAppearanceResId != 0) {
-            mHeaderYearTextView.setTextAppearance(context, headerYearTextAppearanceResId);
+        if (headerTextColor != null) {
+            mHeaderYear.setTextColor(headerTextColor);
+            mHeaderMonthDay.setTextColor(headerTextColor);
         }
 
-        mDayPickerView = new DayPickerView(mContext);
+        // Set up header background, if available.
+        if (a.hasValueOrEmpty(R.styleable.DatePicker_headerBackground)) {
+            header.setBackground(a.getDrawable(R.styleable.DatePicker_headerBackground));
+        }
+
+        a.recycle();
+
+        // Set up picker container.
+        mAnimator = (ViewAnimator) mContainer.findViewById(R.id.animator);
+
+        // Set up day picker view.
+        mDayPickerView = (DayPickerView) mAnimator.findViewById(R.id.date_picker_day_picker);
         mDayPickerView.setFirstDayOfWeek(mFirstDayOfWeek);
         mDayPickerView.setMinDate(mMinDate.getTimeInMillis());
         mDayPickerView.setMaxDate(mMaxDate.getTimeInMillis());
         mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
         mDayPickerView.setOnDaySelectedListener(mOnDaySelectedListener);
 
-        mYearPickerView = new YearPickerView(mContext);
-        mYearPickerView.init(this);
+        // Set up year picker view.
+        mYearPickerView = (YearPickerView) mAnimator.findViewById(R.id.date_picker_year_picker);
         mYearPickerView.setRange(mMinDate, mMaxDate);
+        mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
+        mYearPickerView.setOnYearSelectedListener(mOnYearSelectedListener);
 
-        final ColorStateList yearBackgroundColor = a.getColorStateList(
-                R.styleable.DatePicker_yearListSelectorColor);
-        mYearPickerView.setYearBackgroundColor(yearBackgroundColor);
-
-        final int yearTextAppearanceResId = a.getResourceId(
-                R.styleable.DatePicker_yearListItemTextAppearance, 0);
-        if (yearTextAppearanceResId != 0) {
-            mYearPickerView.setYearTextAppearance(yearTextAppearanceResId);
-        }
-
-        final ColorStateList calendarTextColor = a.getColorStateList(
-                R.styleable.DatePicker_calendarTextColor);
-        mDayPickerView.setCalendarTextColor(calendarTextColor);
-
-        final ColorStateList calendarDayBackgroundColor = a.getColorStateList(
-                R.styleable.DatePicker_calendarDayBackgroundColor);
-        mDayPickerView.setCalendarDayBackgroundColor(calendarDayBackgroundColor);
-
-        mDayPickerDescription = res.getString(R.string.day_picker_description);
+        // Set up content descriptions.
         mSelectDay = res.getString(R.string.select_day);
-        mYearPickerDescription = res.getString(R.string.year_picker_description);
         mSelectYear = res.getString(R.string.select_year);
 
-        mAnimator = (AccessibleDateAnimator) mainView.findViewById(R.id.animator);
-        mAnimator.addView(mDayPickerView);
-        mAnimator.addView(mYearPickerView);
-        mAnimator.setDateMillis(mCurrentDate.getTimeInMillis());
+        // Initialize for current locale. This also initializes the date, so no
+        // need to call onDateChanged.
+        onLocaleChanged(mCurrentLocale);
 
-        final Animation animation = new AlphaAnimation(0.0f, 1.0f);
-        animation.setDuration(ANIMATION_DURATION);
-        mAnimator.setInAnimation(animation);
-
-        final Animation animation2 = new AlphaAnimation(1.0f, 0.0f);
-        animation2.setDuration(ANIMATION_DURATION);
-        mAnimator.setOutAnimation(animation2);
-
-        updateDisplay(false);
-        setCurrentView(MONTH_AND_DAY_VIEW);
+        setCurrentView(VIEW_MONTH_DAY);
     }
 
     /**
-     * Gets a calendar for locale bootstrapped with the value of a given calendar.
+     * The legacy text color might have been poorly defined. Ensures that it
+     * has an appropriate activated state, using the selected state if one
+     * exists or modifying the default text color otherwise.
      *
-     * @param oldCalendar The old calendar.
-     * @param locale The locale.
+     * @param color a legacy text color, or {@code null}
+     * @return a color state list with an appropriate activated state, or
+     *         {@code null} if a valid activated state could not be generated
      */
-    private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
-        if (oldCalendar == null) {
-            return Calendar.getInstance(locale);
-        } else {
-            final long currentTimeMillis = oldCalendar.getTimeInMillis();
-            Calendar newCalendar = Calendar.getInstance(locale);
-            newCalendar.setTimeInMillis(currentTimeMillis);
-            return newCalendar;
+    @Nullable
+    private ColorStateList applyLegacyColorFixes(@Nullable ColorStateList color) {
+        if (color == null || color.hasState(R.attr.state_activated)) {
+            return color;
         }
+
+        final int activatedColor;
+        final int defaultColor;
+        if (color.hasState(R.attr.state_selected)) {
+            activatedColor = color.getColorForState(StateSet.get(
+                    StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_SELECTED), 0);
+            defaultColor = color.getColorForState(StateSet.get(
+                    StateSet.VIEW_STATE_ENABLED), 0);
+        } else {
+            activatedColor = color.getDefaultColor();
+
+            // Generate a non-activated color using the disabled alpha.
+            final TypedArray ta = mContext.obtainStyledAttributes(ATTRS_DISABLED_ALPHA);
+            final float disabledAlpha = ta.getFloat(0, 0.30f);
+            defaultColor = multiplyAlphaComponent(activatedColor, disabledAlpha);
+        }
+
+        if (activatedColor == 0 || defaultColor == 0) {
+            // We somehow failed to obtain the colors.
+            return null;
+        }
+
+        final int[][] stateSet = new int[][] {{ R.attr.state_activated }, {}};
+        final int[] colors = new int[] { activatedColor, defaultColor };
+        return new ColorStateList(stateSet, colors);
+    }
+
+    private int multiplyAlphaComponent(int color, float alphaMod) {
+        final int srcRgb = color & 0xFFFFFF;
+        final int srcAlpha = (color >> 24) & 0xFF;
+        final int dstAlpha = (int) (srcAlpha * alphaMod + 0.5f);
+        return srcRgb | (dstAlpha << 24);
     }
 
     /**
-     * Compute the array representing the order of Month / Day / Year views in their layout.
-     * Will be used for I18N purpose as the order of them depends on the Locale.
+     * Listener called when the user selects a day in the day picker view.
      */
-    private int[] getMonthDayYearIndexes(String pattern) {
-        int[] result = new int[3];
+    private final OnDaySelectedListener mOnDaySelectedListener = new OnDaySelectedListener() {
+        @Override
+        public void onDaySelected(DayPickerView view, Calendar day) {
+            mCurrentDate.setTimeInMillis(day.getTimeInMillis());
+            onDateChanged(true, true);
+        }
+    };
 
-        final String filteredPattern = pattern.replaceAll("'.*?'", "");
-
-        final int dayIndex = filteredPattern.indexOf('d');
-        final int monthMIndex = filteredPattern.indexOf("M");
-        final int monthIndex = (monthMIndex != -1) ? monthMIndex : filteredPattern.indexOf("L");
-        final int yearIndex = filteredPattern.indexOf("y");
-
-        if (yearIndex < monthIndex) {
-            result[YEAR_INDEX] = 0;
-
-            if (monthIndex < dayIndex) {
-                result[MONTH_INDEX] = 1;
-                result[DAY_INDEX] = 2;
-            } else {
-                result[MONTH_INDEX] = 2;
-                result[DAY_INDEX] = 1;
+    /**
+     * Listener called when the user selects a year in the year picker view.
+     */
+    private final OnYearSelectedListener mOnYearSelectedListener = new OnYearSelectedListener() {
+        @Override
+        public void onYearChanged(YearPickerView view, int year) {
+            // If the newly selected month / year does not contain the
+            // currently selected day number, change the selected day number
+            // to the last day of the selected month or year.
+            // e.g. Switching from Mar to Apr when Mar 31 is selected -> Apr 30
+            // e.g. Switching from 2012 to 2013 when Feb 29, 2012 is selected -> Feb 28, 2013
+            final int day = mCurrentDate.get(Calendar.DAY_OF_MONTH);
+            final int month = mCurrentDate.get(Calendar.MONTH);
+            final int daysInMonth = getDaysInMonth(month, year);
+            if (day > daysInMonth) {
+                mCurrentDate.set(Calendar.DAY_OF_MONTH, daysInMonth);
             }
-        } else {
-            result[YEAR_INDEX] = 2;
 
-            if (monthIndex < dayIndex) {
-                result[MONTH_INDEX] = 0;
-                result[DAY_INDEX] = 1;
-            } else {
-                result[MONTH_INDEX] = 1;
-                result[DAY_INDEX] = 0;
+            mCurrentDate.set(Calendar.YEAR, year);
+            onDateChanged(true, true);
+
+            // Automatically switch to day picker.
+            setCurrentView(VIEW_MONTH_DAY);
+        }
+    };
+
+    /**
+     * Listener called when the user clicks on a header item.
+     */
+    private final OnClickListener mOnHeaderClickListener = new OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            tryVibrate();
+
+            switch (v.getId()) {
+                case R.id.date_picker_header_year:
+                    setCurrentView(VIEW_YEAR);
+                    break;
+                case R.id.date_picker_header_date:
+                    setCurrentView(VIEW_MONTH_DAY);
+                    break;
             }
         }
-        return result;
+    };
+
+    @Override
+    protected void onLocaleChanged(Locale locale) {
+        final TextView headerYear = mHeaderYear;
+        if (headerYear == null) {
+            // Abort, we haven't initialized yet. This method will get called
+            // again later after everything has been set up.
+            return;
+        }
+
+        // Update the date formatter.
+        final String datePattern = DateFormat.getBestDateTimePattern(locale, "EMMMd");
+        mMonthDayFormat = new SimpleDateFormat(datePattern, locale);
+        mYearFormat = new SimpleDateFormat("y", locale);
+
+        // Update the header text.
+        onCurrentDateChanged(false);
     }
 
-    private void updateDisplay(boolean announce) {
-        if (mDayOfWeekView != null) {
-            mDayOfWeekView.setText(mCurrentDate.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG,
-                    Locale.getDefault()));
+    private void onCurrentDateChanged(boolean announce) {
+        if (mHeaderYear == null) {
+            // Abort, we haven't initialized yet. This method will get called
+            // again later after everything has been set up.
+            return;
         }
 
-        // Compute indices of Month, Day and Year views
-        final String bestDateTimePattern =
-                DateFormat.getBestDateTimePattern(mCurrentLocale, "yMMMd");
-        final int[] viewIndices = getMonthDayYearIndexes(bestDateTimePattern);
+        final String year = mYearFormat.format(mCurrentDate.getTime());
+        mHeaderYear.setText(year);
 
-        // Position the Year and MonthAndDay views within the header.
-        mMonthDayYearLayout.removeAllViews();
-        if (viewIndices[YEAR_INDEX] == 0) {
-            mMonthDayYearLayout.addView(mHeaderYearTextView);
-            mMonthDayYearLayout.addView(mMonthAndDayLayout);
-        } else {
-            mMonthDayYearLayout.addView(mMonthAndDayLayout);
-            mMonthDayYearLayout.addView(mHeaderYearTextView);
-        }
+        final String monthDay = mMonthDayFormat.format(mCurrentDate.getTime());
+        mHeaderMonthDay.setText(monthDay);
 
-        // Position Day and Month views within the MonthAndDay view.
-        mMonthAndDayLayout.removeAllViews();
-        if (viewIndices[MONTH_INDEX] > viewIndices[DAY_INDEX]) {
-            mMonthAndDayLayout.addView(mHeaderDayOfMonthTextView);
-            mMonthAndDayLayout.addView(mHeaderMonthTextView);
-        } else {
-            mMonthAndDayLayout.addView(mHeaderMonthTextView);
-            mMonthAndDayLayout.addView(mHeaderDayOfMonthTextView);
-        }
-
-        mHeaderMonthTextView.setText(mCurrentDate.getDisplayName(Calendar.MONTH, Calendar.SHORT,
-                Locale.getDefault()).toUpperCase(Locale.getDefault()));
-        mHeaderDayOfMonthTextView.setText(mDayFormat.format(mCurrentDate.getTime()));
-        mHeaderYearTextView.setText(mYearFormat.format(mCurrentDate.getTime()));
-
-        // Accessibility.
-        long millis = mCurrentDate.getTimeInMillis();
-        mAnimator.setDateMillis(millis);
-        int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR;
-        String monthAndDayText = DateUtils.formatDateTime(mContext, millis, flags);
-        mMonthAndDayLayout.setContentDescription(monthAndDayText);
-
+        // TODO: This should use live regions.
         if (announce) {
-            flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
-            String fullDateText = DateUtils.formatDateTime(mContext, millis, flags);
+            final long millis = mCurrentDate.getTimeInMillis();
+            final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
+            final String fullDateText = DateUtils.formatDateTime(mContext, millis, flags);
             mAnimator.announceForAccessibility(fullDateText);
         }
     }
 
     private void setCurrentView(final int viewIndex) {
-        long millis = mCurrentDate.getTimeInMillis();
-
         switch (viewIndex) {
-            case MONTH_AND_DAY_VIEW:
-                mDayPickerView.setDate(getSelectedDay().getTimeInMillis());
+            case VIEW_MONTH_DAY:
+                mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
+
                 if (mCurrentView != viewIndex) {
-                    mMonthAndDayLayout.setSelected(true);
-                    mHeaderYearTextView.setSelected(false);
-                    mAnimator.setDisplayedChild(MONTH_AND_DAY_VIEW);
+                    mHeaderMonthDay.setActivated(true);
+                    mHeaderYear.setActivated(false);
+                    mAnimator.setDisplayedChild(VIEW_MONTH_DAY);
                     mCurrentView = viewIndex;
                 }
 
-                final int flags = DateUtils.FORMAT_SHOW_DATE;
-                final String dayString = DateUtils.formatDateTime(mContext, millis, flags);
-                mAnimator.setContentDescription(mDayPickerDescription + ": " + dayString);
                 mAnimator.announceForAccessibility(mSelectDay);
                 break;
-            case YEAR_VIEW:
-                mYearPickerView.onDateChanged();
+            case VIEW_YEAR:
+                mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
+
                 if (mCurrentView != viewIndex) {
-                    mMonthAndDayLayout.setSelected(false);
-                    mHeaderYearTextView.setSelected(true);
-                    mAnimator.setDisplayedChild(YEAR_VIEW);
+                    mHeaderMonthDay.setActivated(false);
+                    mHeaderYear.setActivated(true);
+                    mAnimator.setDisplayedChild(VIEW_YEAR);
                     mCurrentView = viewIndex;
                 }
 
-                final CharSequence yearString = mYearFormat.format(millis);
-                mAnimator.setContentDescription(mYearPickerDescription + ": " + yearString);
                 mAnimator.announceForAccessibility(mSelectYear);
                 break;
         }
@@ -383,20 +381,18 @@
     }
 
     private void onDateChanged(boolean fromUser, boolean callbackToClient) {
+        final int year = mCurrentDate.get(Calendar.YEAR);
+
         if (callbackToClient && mDateChangedListener != null) {
-            final int year = mCurrentDate.get(Calendar.YEAR);
             final int monthOfYear = mCurrentDate.get(Calendar.MONTH);
             final int dayOfMonth = mCurrentDate.get(Calendar.DAY_OF_MONTH);
             mDateChangedListener.onDateChanged(mDelegator, year, monthOfYear, dayOfMonth);
         }
 
-        for (OnDateChangedListener listener : mListeners) {
-            listener.onDateChanged();
-        }
+        mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
+        mYearPickerView.setYear(year);
 
-        mDayPickerView.setDate(getSelectedDay().getTimeInMillis());
-
-        updateDisplay(fromUser);
+        onCurrentDateChanged(fromUser);
 
         if (fromUser) {
             tryVibrate();
@@ -477,15 +473,12 @@
 
     @Override
     public void setEnabled(boolean enabled) {
-        mMonthAndDayLayout.setEnabled(enabled);
-        mHeaderYearTextView.setEnabled(enabled);
-        mAnimator.setEnabled(enabled);
-        mIsEnabled = enabled;
+        mContainer.setEnabled(false);
     }
 
     @Override
     public boolean isEnabled() {
-        return mIsEnabled;
+        return mContainer.isEnabled();
     }
 
     @Override
@@ -516,8 +509,7 @@
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
-        mYearFormat = new SimpleDateFormat("y", newConfig.locale);
-        mDayFormat = new SimpleDateFormat("d", newConfig.locale);
+        setCurrentLocale(newConfig.locale);
     }
 
     @Override
@@ -529,9 +521,9 @@
         int listPosition = -1;
         int listPositionOffset = -1;
 
-        if (mCurrentView == MONTH_AND_DAY_VIEW) {
+        if (mCurrentView == VIEW_MONTH_DAY) {
             listPosition = mDayPickerView.getMostVisiblePosition();
-        } else if (mCurrentView == YEAR_VIEW) {
+        } else if (mCurrentView == VIEW_YEAR) {
             listPosition = mYearPickerView.getFirstVisiblePosition();
             listPositionOffset = mYearPickerView.getFirstPositionOffset();
         }
@@ -544,20 +536,22 @@
     public void onRestoreInstanceState(Parcelable state) {
         SavedState ss = (SavedState) state;
 
+        // TODO: Move instance state into DayPickerView, YearPickerView.
         mCurrentDate.set(ss.getSelectedYear(), ss.getSelectedMonth(), ss.getSelectedDay());
         mCurrentView = ss.getCurrentView();
         mMinDate.setTimeInMillis(ss.getMinDate());
         mMaxDate.setTimeInMillis(ss.getMaxDate());
 
-        updateDisplay(false);
+        onCurrentDateChanged(false);
         setCurrentView(mCurrentView);
 
         final int listPosition = ss.getListPosition();
         if (listPosition != -1) {
-            if (mCurrentView == MONTH_AND_DAY_VIEW) {
-                mDayPickerView.postSetSelection(listPosition);
-            } else if (mCurrentView == YEAR_VIEW) {
-                mYearPickerView.postSetSelectionFromTop(listPosition, ss.getListPositionOffset());
+            if (mCurrentView == VIEW_MONTH_DAY) {
+                mDayPickerView.setCurrentItem(listPosition);
+            } else if (mCurrentView == VIEW_YEAR) {
+                final int listPositionOffset = ss.getListPositionOffset();
+                mYearPickerView.setSelectionFromTop(listPosition, listPositionOffset);
             }
         }
     }
@@ -577,28 +571,6 @@
         return DatePicker.class.getName();
     }
 
-    @Override
-    public void onYearSelected(int year) {
-        adjustDayInMonthIfNeeded(mCurrentDate.get(Calendar.MONTH), year);
-        mCurrentDate.set(Calendar.YEAR, year);
-        onDateChanged(true, true);
-
-        // Auto-advance to month and day view.
-        setCurrentView(MONTH_AND_DAY_VIEW);
-    }
-
-    // If the newly selected month / year does not contain the currently selected day number,
-    // change the selected day number to the last day of the selected month or year.
-    //      e.g. Switching from Mar to Apr when Mar 31 is selected -> Apr 30
-    //      e.g. Switching from 2012 to 2013 when Feb 29, 2012 is selected -> Feb 28, 2013
-    private void adjustDayInMonthIfNeeded(int month, int year) {
-        int day = mCurrentDate.get(Calendar.DAY_OF_MONTH);
-        int daysInMonth = getDaysInMonth(month, year);
-        if (day > daysInMonth) {
-            mCurrentDate.set(Calendar.DAY_OF_MONTH, daysInMonth);
-        }
-    }
-
     public static int getDaysInMonth(int month, int year) {
         switch (month) {
             case Calendar.JANUARY:
@@ -621,43 +593,10 @@
         }
     }
 
-    @Override
-    public void registerOnDateChangedListener(OnDateChangedListener listener) {
-        mListeners.add(listener);
-    }
-
-    @Override
-    public Calendar getSelectedDay() {
-        return mCurrentDate;
-    }
-
-    @Override
-    public void tryVibrate() {
+    private void tryVibrate() {
         mDelegator.performHapticFeedback(HapticFeedbackConstants.CALENDAR_DATE);
     }
 
-    @Override
-    public void onClick(View v) {
-        tryVibrate();
-        if (v.getId() == R.id.date_picker_year) {
-            setCurrentView(YEAR_VIEW);
-        } else if (v.getId() == R.id.date_picker_month_and_day_layout) {
-            setCurrentView(MONTH_AND_DAY_VIEW);
-        }
-    }
-
-    /**
-     * Listener called when the user selects a day in the day picker view.
-     */
-    private final DayPickerView.OnDaySelectedListener
-            mOnDaySelectedListener = new DayPickerView.OnDaySelectedListener() {
-        @Override
-        public void onDaySelected(DayPickerView view, Calendar day) {
-            mCurrentDate.setTimeInMillis(day.getTimeInMillis());
-            onDateChanged(true, true);
-        }
-    };
-
     /**
      * Class for managing state storing/restoring.
      */
diff --git a/core/java/android/widget/DayPickerAdapter.java b/core/java/android/widget/DayPickerAdapter.java
new file mode 100644
index 0000000..9a4b6f5
--- /dev/null
+++ b/core/java/android/widget/DayPickerAdapter.java
@@ -0,0 +1,330 @@
+/*
+ * 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 android.widget;
+
+import com.android.internal.widget.PagerAdapter;
+
+import android.annotation.IdRes;
+import android.annotation.LayoutRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.SimpleMonthView.OnDayClickListener;
+
+import java.util.Calendar;
+
+/**
+ * An adapter for a list of {@link android.widget.SimpleMonthView} items.
+ */
+class DayPickerAdapter extends PagerAdapter {
+    private static final int MONTHS_IN_YEAR = 12;
+
+    private final Calendar mMinDate = Calendar.getInstance();
+    private final Calendar mMaxDate = Calendar.getInstance();
+
+    private final SparseArray<ViewHolder> mItems = new SparseArray<>();
+
+    private final LayoutInflater mInflater;
+    private final int mLayoutResId;
+    private final int mCalendarViewId;
+
+    private Calendar mSelectedDay = null;
+
+    private int mMonthTextAppearance;
+    private int mDayOfWeekTextAppearance;
+    private int mDayTextAppearance;
+
+    private ColorStateList mCalendarTextColor;
+    private ColorStateList mDaySelectorColor;
+    private ColorStateList mDayHighlightColor;
+
+    private OnDaySelectedListener mOnDaySelectedListener;
+
+    private int mCount;
+    private int mFirstDayOfWeek;
+
+    public DayPickerAdapter(@NonNull Context context, @LayoutRes int layoutResId,
+            @IdRes int calendarViewId) {
+        mInflater = LayoutInflater.from(context);
+        mLayoutResId = layoutResId;
+        mCalendarViewId = calendarViewId;
+
+        final TypedArray ta = context.obtainStyledAttributes(new int[] {
+                com.android.internal.R.attr.colorControlHighlight});
+        mDayHighlightColor = ta.getColorStateList(0);
+        ta.recycle();
+    }
+
+    public void setRange(@NonNull Calendar min, @NonNull Calendar max) {
+        mMinDate.setTimeInMillis(min.getTimeInMillis());
+        mMaxDate.setTimeInMillis(max.getTimeInMillis());
+
+        final int diffYear = mMaxDate.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR);
+        final int diffMonth = mMaxDate.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH);
+        mCount = diffMonth + MONTHS_IN_YEAR * diffYear + 1;
+
+        // Positions are now invalid, clear everything and start over.
+        notifyDataSetChanged();
+    }
+
+    /**
+     * Sets the first day of the week.
+     *
+     * @param weekStart which day the week should start on, valid values are
+     *                  {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY}
+     */
+    public void setFirstDayOfWeek(int weekStart) {
+        mFirstDayOfWeek = weekStart;
+
+        // Update displayed views.
+        final int count = mItems.size();
+        for (int i = 0; i < count; i++) {
+            final SimpleMonthView monthView = mItems.valueAt(i).calendar;
+            monthView.setFirstDayOfWeek(weekStart);
+        }
+    }
+
+    public int getFirstDayOfWeek() {
+        return mFirstDayOfWeek;
+    }
+
+    /**
+     * Sets the selected day.
+     *
+     * @param day the selected day
+     */
+    public void setSelectedDay(@Nullable Calendar day) {
+        final int oldPosition = getPositionForDay(mSelectedDay);
+        final int newPosition = getPositionForDay(day);
+
+        // Clear the old position if necessary.
+        if (oldPosition != newPosition && oldPosition >= 0) {
+            final ViewHolder oldMonthView = mItems.get(oldPosition, null);
+            if (oldMonthView != null) {
+                oldMonthView.calendar.setSelectedDay(-1);
+            }
+        }
+
+        // Set the new position.
+        if (newPosition >= 0) {
+            final ViewHolder newMonthView = mItems.get(newPosition, null);
+            if (newMonthView != null) {
+                final int dayOfMonth = day.get(Calendar.DAY_OF_MONTH);
+                newMonthView.calendar.setSelectedDay(dayOfMonth);
+            }
+        }
+
+        mSelectedDay = day;
+    }
+
+    /**
+     * Sets the listener to call when the user selects a day.
+     *
+     * @param listener The listener to call.
+     */
+    public void setOnDaySelectedListener(OnDaySelectedListener listener) {
+        mOnDaySelectedListener = listener;
+    }
+
+    void setCalendarTextColor(ColorStateList calendarTextColor) {
+        mCalendarTextColor = calendarTextColor;
+    }
+
+    void setDaySelectorColor(ColorStateList selectorColor) {
+        mDaySelectorColor = selectorColor;
+    }
+
+    void setMonthTextAppearance(int resId) {
+        mMonthTextAppearance = resId;
+    }
+
+    void setDayOfWeekTextAppearance(int resId) {
+        mDayOfWeekTextAppearance = resId;
+    }
+
+    int getDayOfWeekTextAppearance() {
+        return mDayOfWeekTextAppearance;
+    }
+
+    void setDayTextAppearance(int resId) {
+        mDayTextAppearance = resId;
+    }
+
+    int getDayTextAppearance() {
+        return mDayTextAppearance;
+    }
+
+    @Override
+    public int getCount() {
+        return mCount;
+    }
+
+    @Override
+    public boolean isViewFromObject(View view, Object object) {
+        final ViewHolder holder = (ViewHolder) object;
+        return view == holder.container;
+    }
+
+    private int getMonthForPosition(int position) {
+        return position % MONTHS_IN_YEAR + mMinDate.get(Calendar.MONTH);
+    }
+
+    private int getYearForPosition(int position) {
+        return position / MONTHS_IN_YEAR + mMinDate.get(Calendar.YEAR);
+    }
+
+    private int getPositionForDay(@Nullable Calendar day) {
+        if (day == null) {
+            return -1;
+        }
+
+        final int yearOffset = (day.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR));
+        final int monthOffset = (day.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH));
+        return yearOffset * MONTHS_IN_YEAR + monthOffset;
+    }
+
+    @Override
+    public Object instantiateItem(ViewGroup container, int position) {
+        final View itemView = mInflater.inflate(mLayoutResId, container, false);
+
+        final SimpleMonthView v = (SimpleMonthView) itemView.findViewById(mCalendarViewId);
+        v.setOnDayClickListener(mOnDayClickListener);
+        v.setMonthTextAppearance(mMonthTextAppearance);
+        v.setDayOfWeekTextAppearance(mDayOfWeekTextAppearance);
+        v.setDayTextAppearance(mDayTextAppearance);
+
+        if (mDaySelectorColor != null) {
+            v.setDaySelectorColor(mDaySelectorColor);
+        }
+
+        if (mDayHighlightColor != null) {
+            v.setDayHighlightColor(mDayHighlightColor);
+        }
+
+        if (mCalendarTextColor != null) {
+            v.setMonthTextColor(mCalendarTextColor);
+            v.setDayOfWeekTextColor(mCalendarTextColor);
+            v.setDayTextColor(mCalendarTextColor);
+        }
+
+        final int month = getMonthForPosition(position);
+        final int year = getYearForPosition(position);
+
+        final int selectedDay;
+        if (mSelectedDay != null && mSelectedDay.get(Calendar.MONTH) == month) {
+            selectedDay = mSelectedDay.get(Calendar.DAY_OF_MONTH);
+        } else {
+            selectedDay = -1;
+        }
+
+        final int enabledDayRangeStart;
+        if (mMinDate.get(Calendar.MONTH) == month && mMinDate.get(Calendar.YEAR) == year) {
+            enabledDayRangeStart = mMinDate.get(Calendar.DAY_OF_MONTH);
+        } else {
+            enabledDayRangeStart = 1;
+        }
+
+        final int enabledDayRangeEnd;
+        if (mMaxDate.get(Calendar.MONTH) == month && mMaxDate.get(Calendar.YEAR) == year) {
+            enabledDayRangeEnd = mMaxDate.get(Calendar.DAY_OF_MONTH);
+        } else {
+            enabledDayRangeEnd = 31;
+        }
+
+        v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek,
+                enabledDayRangeStart, enabledDayRangeEnd);
+        v.setPrevEnabled(position > 0);
+        v.setNextEnabled(position < mCount - 1);
+
+        final ViewHolder holder = new ViewHolder(position, itemView, v);
+        mItems.put(position, holder);
+
+        container.addView(itemView);
+
+        return holder;
+    }
+
+    @Override
+    public void destroyItem(ViewGroup container, int position, Object object) {
+        final ViewHolder holder = (ViewHolder) object;
+        container.removeView(holder.container);
+
+        mItems.remove(position);
+    }
+
+    @Override
+    public int getItemPosition(Object object) {
+        final ViewHolder holder = (ViewHolder) object;
+        return holder.position;
+    }
+
+    @Override
+    public CharSequence getPageTitle(int position) {
+        final SimpleMonthView v = mItems.get(position).calendar;
+        if (v != null) {
+            return v.getTitle();
+        }
+        return null;
+    }
+
+    private boolean isCalendarInRange(Calendar value) {
+        return value.compareTo(mMinDate) >= 0 && value.compareTo(mMaxDate) <= 0;
+    }
+
+    private final OnDayClickListener mOnDayClickListener = new OnDayClickListener() {
+        @Override
+        public void onDayClick(SimpleMonthView view, Calendar day) {
+            if (day != null && isCalendarInRange(day)) {
+                setSelectedDay(day);
+
+                if (mOnDaySelectedListener != null) {
+                    mOnDaySelectedListener.onDaySelected(DayPickerAdapter.this, day);
+                }
+            }
+        }
+
+        @Override
+        public void onNavigationClick(SimpleMonthView view, int direction, boolean animate) {
+            if (mOnDaySelectedListener != null) {
+                mOnDaySelectedListener.onNavigationClick(DayPickerAdapter.this, direction, animate);
+            }
+        }
+    };
+
+    private static class ViewHolder {
+        public final int position;
+        public final View container;
+        public final SimpleMonthView calendar;
+
+        public ViewHolder(int position, View container, SimpleMonthView calendar) {
+            this.position = position;
+            this.container = container;
+            this.calendar = calendar;
+        }
+    }
+
+    public interface OnDaySelectedListener {
+        public void onDaySelected(DayPickerAdapter view, Calendar day);
+        public void onNavigationClick(DayPickerAdapter view, int direction, boolean animate);
+    }
+}
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index 65af45d..ec2528f 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -16,87 +16,188 @@
 
 package android.widget;
 
+import com.android.internal.widget.ViewPager;
+import com.android.internal.R;
+
 import android.content.Context;
 import android.content.res.ColorStateList;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.util.Log;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
 import android.util.MathUtils;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
 
-import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Locale;
 
+import libcore.icu.LocaleData;
+
 /**
  * This displays a list of months in a calendar format with selectable days.
  */
-class DayPickerView extends ListView implements AbsListView.OnScrollListener {
-    private static final String TAG = "DayPickerView";
+class DayPickerView extends ViewPager {
+    private static final int DEFAULT_START_YEAR = 1900;
+    private static final int DEFAULT_END_YEAR = 2100;
 
-    // How long the GoTo fling animation should last
-    private static final int GOTO_SCROLL_DURATION = 250;
+    private final Calendar mSelectedDay = Calendar.getInstance();
+    private final Calendar mMinDate = Calendar.getInstance();
+    private final Calendar mMaxDate = Calendar.getInstance();
 
-    // How long to wait after receiving an onScrollStateChanged notification before acting on it
-    private static final int SCROLL_CHANGE_DELAY = 40;
+    private final DayPickerAdapter mAdapter;
 
-    // so that the top line will be under the separator
-    private static final int LIST_TOP_OFFSET = -1;
-
-    private final SimpleMonthAdapter mAdapter = new SimpleMonthAdapter(getContext());
-
-    private final ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable(this);
-
-    private SimpleDateFormat mYearFormat = new SimpleDateFormat("yyyy", Locale.getDefault());
-
-    // highlighted time
-    private Calendar mSelectedDay = Calendar.getInstance();
-    private Calendar mTempDay = Calendar.getInstance();
-    private Calendar mMinDate = Calendar.getInstance();
-    private Calendar mMaxDate = Calendar.getInstance();
-
+    /** Temporary calendar used for date calculations. */
     private Calendar mTempCalendar;
 
     private OnDaySelectedListener mOnDaySelectedListener;
 
-    // which month should be displayed/highlighted [0-11]
-    private int mCurrentMonthDisplayed;
-    // used for tracking what state listview is in
-    private int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE;
-    // used for tracking what state listview is in
-    private int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE;
-
-    private boolean mPerformingScroll;
-
     public DayPickerView(Context context) {
-        super(context);
+        this(context, null);
+    }
+
+    public DayPickerView(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.calendarViewStyle);
+    }
+
+    public DayPickerView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public DayPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        final TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.CalendarView, defStyleAttr, defStyleRes);
+
+        final int firstDayOfWeek = a.getInt(R.styleable.CalendarView_firstDayOfWeek,
+                LocaleData.get(Locale.getDefault()).firstDayOfWeek);
+
+        final String minDate = a.getString(R.styleable.CalendarView_minDate);
+        final String maxDate = a.getString(R.styleable.CalendarView_maxDate);
+
+        final int monthTextAppearanceResId = a.getResourceId(
+                R.styleable.CalendarView_monthTextAppearance,
+                R.style.TextAppearance_Material_Widget_Calendar_Month);
+        final int dayOfWeekTextAppearanceResId = a.getResourceId(
+                R.styleable.CalendarView_weekDayTextAppearance,
+                R.style.TextAppearance_Material_Widget_Calendar_DayOfWeek);
+        final int dayTextAppearanceResId = a.getResourceId(
+                R.styleable.CalendarView_dateTextAppearance,
+                R.style.TextAppearance_Material_Widget_Calendar_Day);
+
+        final ColorStateList daySelectorColor = a.getColorStateList(
+                R.styleable.CalendarView_daySelectorColor);
+
+        a.recycle();
+
+        // Set up adapter.
+        mAdapter = new DayPickerAdapter(context,
+                R.layout.date_picker_month_item_material, R.id.month_view);
+        mAdapter.setMonthTextAppearance(monthTextAppearanceResId);
+        mAdapter.setDayOfWeekTextAppearance(dayOfWeekTextAppearanceResId);
+        mAdapter.setDayTextAppearance(dayTextAppearanceResId);
+        mAdapter.setDaySelectorColor(daySelectorColor);
 
         setAdapter(mAdapter);
-        setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
-        setDrawSelectorOnTop(false);
-        setUpListView();
 
-        goTo(mSelectedDay.getTimeInMillis(), false, false, true);
+        // Set up min and max dates.
+        final Calendar tempDate = Calendar.getInstance();
+        if (!CalendarView.parseDate(minDate, tempDate)) {
+            tempDate.set(DEFAULT_START_YEAR, Calendar.JANUARY, 1);
+        }
+        final long minDateMillis = tempDate.getTimeInMillis();
 
-        mAdapter.setOnDaySelectedListener(mProxyOnDaySelectedListener);
+        if (!CalendarView.parseDate(maxDate, tempDate)) {
+            tempDate.set(DEFAULT_END_YEAR, Calendar.DECEMBER, 31);
+        }
+        final long maxDateMillis = tempDate.getTimeInMillis();
+
+        if (maxDateMillis < minDateMillis) {
+            throw new IllegalArgumentException("maxDate must be >= minDate");
+        }
+
+        final long setDateMillis = MathUtils.constrain(
+                System.currentTimeMillis(), minDateMillis, maxDateMillis);
+
+        setFirstDayOfWeek(firstDayOfWeek);
+        setMinDate(minDateMillis);
+        setMaxDate(maxDateMillis);
+        setDate(setDateMillis, false);
+
+        // Proxy selection callbacks to our own listener.
+        mAdapter.setOnDaySelectedListener(new DayPickerAdapter.OnDaySelectedListener() {
+            @Override
+            public void onDaySelected(DayPickerAdapter adapter, Calendar day) {
+                if (mOnDaySelectedListener != null) {
+                    mOnDaySelectedListener.onDaySelected(DayPickerView.this, day);
+                }
+            }
+
+            @Override
+            public void onNavigationClick(DayPickerAdapter view, int direction, boolean animate) {
+                // ViewPager clamps input values, so we don't need to worry
+                // about passing invalid indices.
+                final int nextItem = getCurrentItem() + direction;
+                setCurrentItem(nextItem, animate);
+            }
+        });
+    }
+
+    public void setDayOfWeekTextAppearance(int resId) {
+        mAdapter.setDayOfWeekTextAppearance(resId);
+    }
+
+    public int getDayOfWeekTextAppearance() {
+        return mAdapter.getDayOfWeekTextAppearance();
+    }
+
+    public void setDayTextAppearance(int resId) {
+        mAdapter.setDayTextAppearance(resId);
+    }
+
+    public int getDayTextAppearance() {
+        return mAdapter.getDayTextAppearance();
     }
 
     /**
      * Sets the currently selected date to the specified timestamp. Jumps
      * immediately to the new date. To animate to the new date, use
-     * {@link #setDate(long, boolean, boolean)}.
+     * {@link #setDate(long, boolean)}.
      *
-     * @param timeInMillis
+     * @param timeInMillis the target day in milliseconds
      */
     public void setDate(long timeInMillis) {
-        setDate(timeInMillis, false, true);
+        setDate(timeInMillis, false);
     }
 
-    public void setDate(long timeInMillis, boolean animate, boolean forceScroll) {
-        goTo(timeInMillis, animate, true, forceScroll);
+    /**
+     * Sets the currently selected date to the specified timestamp. Jumps
+     * immediately to the new date, optionally animating the transition.
+     *
+     * @param timeInMillis the target day in milliseconds
+     * @param animate whether to smooth scroll to the new position
+     */
+    public void setDate(long timeInMillis, boolean animate) {
+        setDate(timeInMillis, animate, true);
+    }
+
+    /**
+     * Moves to the month containing the specified day, optionally setting the
+     * day as selected.
+     *
+     * @param timeInMillis the target day in milliseconds
+     * @param animate whether to smooth scroll to the new position
+     * @param setSelected whether to set the specified day as selected
+     */
+    private void setDate(long timeInMillis, boolean animate, boolean setSelected) {
+        if (setSelected) {
+            mSelectedDay.setTimeInMillis(timeInMillis);
+        }
+
+        final int position = getPositionFromDay(timeInMillis);
+        if (position != getCurrentItem()) {
+            setCurrentItem(position, animate);
+        }
+
+        mTempCalendar.setTimeInMillis(timeInMillis);
+        mAdapter.setSelectedDay(mTempCalendar);
     }
 
     public long getDate() {
@@ -137,7 +238,7 @@
 
         // Changing the min/max date changes the selection position since we
         // don't really have stable IDs. Jumps immediately to the new position.
-        goTo(mSelectedDay.getTimeInMillis(), false, false, true);
+        setDate(mSelectedDay.getTimeInMillis(), false, false);
     }
 
     /**
@@ -149,30 +250,9 @@
         mOnDaySelectedListener = listener;
     }
 
-    /*
-     * Sets all the required fields for the list view. Override this method to
-     * set a different list view behavior.
-     */
-    private void setUpListView() {
-        // Transparent background on scroll
-        setCacheColorHint(0);
-        // No dividers
-        setDivider(null);
-        // Items are clickable
-        setItemsCanFocus(true);
-        // The thumb gets in the way, so disable it
-        setFastScrollEnabled(false);
-        setVerticalScrollBarEnabled(false);
-        setOnScrollListener(this);
-        setFadingEdgeLength(0);
-        // Make the scrolling behavior nicer
-        setFriction(ViewConfiguration.getScrollFriction());
-    }
-
     private int getDiffMonths(Calendar start, Calendar end) {
         final int diffYears = end.get(Calendar.YEAR) - start.get(Calendar.YEAR);
-        final int diffMonths = end.get(Calendar.MONTH) - start.get(Calendar.MONTH) + 12 * diffYears;
-        return diffMonths;
+        return end.get(Calendar.MONTH) - start.get(Calendar.MONTH) + 12 * diffYears;
     }
 
     private int getPositionFromDay(long timeInMillis) {
@@ -190,366 +270,13 @@
     }
 
     /**
-     * This moves to the specified time in the view. If the time is not already
-     * in range it will move the list so that the first of the month containing
-     * the time is at the top of the view. If the new time is already in view
-     * the list will not be scrolled unless forceScroll is true. This time may
-     * optionally be highlighted as selected as well.
-     *
-     * @param day The day to move to
-     * @param animate Whether to scroll to the given time or just redraw at the
-     *            new location
-     * @param setSelected Whether to set the given time as selected
-     * @param forceScroll Whether to recenter even if the time is already
-     *            visible
-     * @return Whether or not the view animated to the new location
-     */
-    private boolean goTo(long day, boolean animate, boolean setSelected, boolean forceScroll) {
-
-        // Set the selected day
-        if (setSelected) {
-            mSelectedDay.setTimeInMillis(day);
-        }
-
-        mTempDay.setTimeInMillis(day);
-        final int position = getPositionFromDay(day);
-
-        View child;
-        int i = 0;
-        int top = 0;
-        // Find a child that's completely in the view
-        do {
-            child = getChildAt(i++);
-            if (child == null) {
-                break;
-            }
-            top = child.getTop();
-        } while (top < 0);
-
-        // Compute the first and last position visible
-        int selectedPosition;
-        if (child != null) {
-            selectedPosition = getPositionForView(child);
-        } else {
-            selectedPosition = 0;
-        }
-
-        if (setSelected) {
-            mAdapter.setSelectedDay(mSelectedDay);
-        }
-
-        // Check if the selected day is now outside of our visible range
-        // and if so scroll to the month that contains it
-        if (position != selectedPosition || forceScroll) {
-            setMonthDisplayed(mTempDay);
-            mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING;
-            if (animate) {
-                smoothScrollToPositionFromTop(
-                        position, LIST_TOP_OFFSET, GOTO_SCROLL_DURATION);
-                return true;
-            } else {
-                postSetSelection(position);
-            }
-        } else if (setSelected) {
-            setMonthDisplayed(mSelectedDay);
-        }
-        return false;
-    }
-
-    public void postSetSelection(final int position) {
-        clearFocus();
-        post(new Runnable() {
-
-            @Override
-            public void run() {
-                setSelection(position);
-            }
-        });
-        onScrollStateChanged(this, OnScrollListener.SCROLL_STATE_IDLE);
-    }
-
-    /**
-     * Updates the title and selected month if the view has moved to a new
-     * month.
-     */
-    @Override
-    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
-                         int totalItemCount) {
-        SimpleMonthView child = (SimpleMonthView) view.getChildAt(0);
-        if (child == null) {
-            return;
-        }
-
-        mPreviousScrollState = mCurrentScrollState;
-    }
-
-    /**
-     * Sets the month displayed at the top of this view based on time. Override
-     * to add custom events when the title is changed.
-     */
-    protected void setMonthDisplayed(Calendar date) {
-        if (mCurrentMonthDisplayed != date.get(Calendar.MONTH)) {
-            mCurrentMonthDisplayed = date.get(Calendar.MONTH);
-            invalidateViews();
-        }
-    }
-
-    @Override
-    public void onScrollStateChanged(AbsListView view, int scrollState) {
-        // use a post to prevent re-entering onScrollStateChanged before it
-        // exits
-        mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
-    }
-
-    void setCalendarTextColor(ColorStateList colors) {
-        mAdapter.setCalendarTextColor(colors);
-    }
-
-    void setCalendarDayBackgroundColor(ColorStateList dayBackgroundColor) {
-        mAdapter.setCalendarDayBackgroundColor(dayBackgroundColor);
-    }
-
-    void setCalendarTextAppearance(int resId) {
-        mAdapter.setCalendarTextAppearance(resId);
-    }
-
-    protected class ScrollStateRunnable implements Runnable {
-        private int mNewState;
-        private View mParent;
-
-        ScrollStateRunnable(View view) {
-            mParent = view;
-        }
-
-        /**
-         * Sets up the runnable with a short delay in case the scroll state
-         * immediately changes again.
-         *
-         * @param view The list view that changed state
-         * @param scrollState The new state it changed to
-         */
-        public void doScrollStateChange(AbsListView view, int scrollState) {
-            mParent.removeCallbacks(this);
-            mNewState = scrollState;
-            mParent.postDelayed(this, SCROLL_CHANGE_DELAY);
-        }
-
-        @Override
-        public void run() {
-            mCurrentScrollState = mNewState;
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG,
-                        "new scroll state: " + mNewState + " old state: " + mPreviousScrollState);
-            }
-            // Fix the position after a scroll or a fling ends
-            if (mNewState == OnScrollListener.SCROLL_STATE_IDLE
-                    && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE
-                    && mPreviousScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
-                mPreviousScrollState = mNewState;
-                int i = 0;
-                View child = getChildAt(i);
-                while (child != null && child.getBottom() <= 0) {
-                    child = getChildAt(++i);
-                }
-                if (child == null) {
-                    // The view is no longer visible, just return
-                    return;
-                }
-                int firstPosition = getFirstVisiblePosition();
-                int lastPosition = getLastVisiblePosition();
-                boolean scroll = firstPosition != 0 && lastPosition != getCount() - 1;
-                final int top = child.getTop();
-                final int bottom = child.getBottom();
-                final int midpoint = getHeight() / 2;
-                if (scroll && top < LIST_TOP_OFFSET) {
-                    if (bottom > midpoint) {
-                        smoothScrollBy(top, GOTO_SCROLL_DURATION);
-                    } else {
-                        smoothScrollBy(bottom, GOTO_SCROLL_DURATION);
-                    }
-                }
-            } else {
-                mPreviousScrollState = mNewState;
-            }
-        }
-    }
-
-    /**
      * Gets the position of the view that is most prominently displayed within the list view.
      */
     public int getMostVisiblePosition() {
-        final int firstPosition = getFirstVisiblePosition();
-        final int height = getHeight();
-
-        int maxDisplayedHeight = 0;
-        int mostVisibleIndex = 0;
-        int i=0;
-        int bottom = 0;
-        while (bottom < height) {
-            View child = getChildAt(i);
-            if (child == null) {
-                break;
-            }
-            bottom = child.getBottom();
-            int displayedHeight = Math.min(bottom, height) - Math.max(0, child.getTop());
-            if (displayedHeight > maxDisplayedHeight) {
-                mostVisibleIndex = i;
-                maxDisplayedHeight = displayedHeight;
-            }
-            i++;
-        }
-        return firstPosition + mostVisibleIndex;
-    }
-
-    /**
-     * Attempts to return the date that has accessibility focus.
-     *
-     * @return The date that has accessibility focus, or {@code null} if no date
-     *         has focus.
-     */
-    private Calendar findAccessibilityFocus() {
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            if (child instanceof SimpleMonthView) {
-                final Calendar focus = ((SimpleMonthView) child).getAccessibilityFocus();
-                if (focus != null) {
-                    return focus;
-                }
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Attempts to restore accessibility focus to a given date. No-op if
-     * {@code day} is {@code null}.
-     *
-     * @param day The date that should receive accessibility focus
-     * @return {@code true} if focus was restored
-     */
-    private boolean restoreAccessibilityFocus(Calendar day) {
-        if (day == null) {
-            return false;
-        }
-
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            if (child instanceof SimpleMonthView) {
-                if (((SimpleMonthView) child).restoreAccessibilityFocus(day)) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    @Override
-    protected void layoutChildren() {
-        final Calendar focusedDay = findAccessibilityFocus();
-        super.layoutChildren();
-        if (mPerformingScroll) {
-            mPerformingScroll = false;
-        } else {
-            restoreAccessibilityFocus(focusedDay);
-        }
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        mYearFormat = new SimpleDateFormat("yyyy", Locale.getDefault());
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        event.setItemCount(-1);
-    }
-
-    private String getMonthAndYearString(Calendar day) {
-        final StringBuilder sbuf = new StringBuilder();
-        sbuf.append(day.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault()));
-        sbuf.append(" ");
-        sbuf.append(mYearFormat.format(day.getTime()));
-        return sbuf.toString();
-    }
-
-    /**
-     * Necessary for accessibility, to ensure we support "scrolling" forward and backward
-     * in the month list.
-     *
-     * @hide
-     */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
-        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
-    }
-
-    /**
-     * When scroll forward/backward events are received, announce the newly scrolled-to month.
-     *
-     * @hide
-     */
-    @Override
-    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
-        if (action != AccessibilityNodeInfo.ACTION_SCROLL_FORWARD &&
-                action != AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
-            return super.performAccessibilityActionInternal(action, arguments);
-        }
-
-        // Figure out what month is showing.
-        final int firstVisiblePosition = getFirstVisiblePosition();
-        final int month = firstVisiblePosition % 12;
-        final int year = firstVisiblePosition / 12 + mMinDate.get(Calendar.YEAR);
-        final Calendar day = Calendar.getInstance();
-        day.set(year, month, 1);
-
-        // Scroll either forward or backward one month.
-        if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) {
-            day.add(Calendar.MONTH, 1);
-            if (day.get(Calendar.MONTH) == 12) {
-                day.set(Calendar.MONTH, 0);
-                day.add(Calendar.YEAR, 1);
-            }
-        } else if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
-            View firstVisibleView = getChildAt(0);
-            // If the view is fully visible, jump one month back. Otherwise, we'll just jump
-            // to the first day of first visible month.
-            if (firstVisibleView != null && firstVisibleView.getTop() >= -1) {
-                // There's an off-by-one somewhere, so the top of the first visible item will
-                // actually be -1 when it's at the exact top.
-                day.add(Calendar.MONTH, -1);
-                if (day.get(Calendar.MONTH) == -1) {
-                    day.set(Calendar.MONTH, 11);
-                    day.add(Calendar.YEAR, -1);
-                }
-            }
-        }
-
-        // Go to that month.
-        announceForAccessibility(getMonthAndYearString(day));
-        goTo(day.getTimeInMillis(), true, false, true);
-        mPerformingScroll = true;
-        return true;
+        return getCurrentItem();
     }
 
     public interface OnDaySelectedListener {
         public void onDaySelected(DayPickerView view, Calendar day);
     }
-
-    private final SimpleMonthAdapter.OnDaySelectedListener
-            mProxyOnDaySelectedListener = new SimpleMonthAdapter.OnDaySelectedListener() {
-        @Override
-        public void onDaySelected(SimpleMonthAdapter adapter, Calendar day) {
-            if (mOnDaySelectedListener != null) {
-                mOnDaySelectedListener.onDaySelected(DayPickerView.this, day);
-            }
-        }
-    };
 }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 3fca463..32b99a8 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -16,18 +16,6 @@
 
 package android.widget;
 
-import android.content.UndoManager;
-import android.content.UndoOperation;
-import android.content.UndoOwner;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.InputFilter;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.GrowingArrayUtils;
-import com.android.internal.view.menu.MenuBuilder;
-import com.android.internal.widget.EditableInputConnection;
-
 import android.R;
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
@@ -35,6 +23,9 @@
 import android.content.ClipData.Item;
 import android.content.Context;
 import android.content.Intent;
+import android.content.UndoManager;
+import android.content.UndoOperation;
+import android.content.UndoOwner;
 import android.content.pm.PackageManager;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
@@ -46,13 +37,17 @@
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.inputmethodservice.ExtractEditText;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.ParcelableParcel;
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.text.DynamicLayout;
 import android.text.Editable;
+import android.text.InputFilter;
 import android.text.InputType;
 import android.text.Layout;
 import android.text.ParcelableSpan;
@@ -105,6 +100,11 @@
 import android.widget.TextView.Drawables;
 import android.widget.TextView.OnEditorActionListener;
 
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.GrowingArrayUtils;
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.widget.EditableInputConnection;
+
 import java.text.BreakIterator;
 import java.util.Arrays;
 import java.util.Comparator;
@@ -2939,7 +2939,27 @@
      * The default callback provides a subset of Select All, Cut, Copy and Paste actions, depending
      * on which of these this TextView supports.
      */
-    private class SelectionActionModeCallback implements ActionMode.Callback {
+    private class SelectionActionModeCallback extends ActionMode.Callback2 {
+        private final Path mSelectionPath = new Path();
+        private final RectF mSelectionBounds = new RectF();
+
+        private int mSelectionHandleHeight;
+        private int mInsertionHandleHeight;
+
+        public SelectionActionModeCallback() {
+            SelectionModifierCursorController selectionController = getSelectionController();
+            if (selectionController.mStartHandle == null) {
+                selectionController.initDrawables();
+                selectionController.initHandles();
+            }
+            mSelectionHandleHeight = Math.max(
+                    mSelectHandleLeft.getMinimumHeight(), mSelectHandleRight.getMinimumHeight());
+            InsertionPointCursorController insertionController = getInsertionController();
+            if (insertionController != null) {
+                insertionController.getHandle();
+                mInsertionHandleHeight = mSelectHandleCenter.getMinimumHeight();
+            }
+        }
 
         @Override
         public boolean onCreateActionMode(ActionMode mode, Menu menu) {
@@ -2956,13 +2976,6 @@
             mode.setSubtitle(null);
             mode.setTitleOptionalHint(true);
 
-            menu.add(0, TextView.ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
-                    setIcon(styledAttributes.getResourceId(
-                            R.styleable.SelectionModeDrawables_actionModeSelectAllDrawable, 0)).
-                    setAlphabeticShortcut('a').
-                    setShowAsAction(
-                            MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
-
             if (mTextView.canCut()) {
                 menu.add(0, TextView.ID_CUT, 0, com.android.internal.R.string.cut).
                     setIcon(styledAttributes.getResourceId(
@@ -2990,6 +3003,13 @@
                                 MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
             }
 
+            menu.add(0, TextView.ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
+                    setIcon(styledAttributes.getResourceId(
+                            R.styleable.SelectionModeDrawables_actionModeSelectAllDrawable, 0)).
+                    setAlphabeticShortcut('a').
+                    setShowAsAction(
+                            MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
+
             if (mTextView.isSuggestionsEnabled() && isCursorInsideSuggestionSpan()) {
                 menu.add(0, TextView.ID_REPLACE, 0, com.android.internal.R.string.replace).
                         setShowAsAction(
@@ -3057,6 +3077,40 @@
 
             mSelectionActionMode = null;
         }
+
+        @Override
+        public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
+            if (!view.equals(mTextView) || mTextView.getLayout() == null) {
+                super.onGetContentRect(mode, view, outRect);
+                return;
+            }
+            if (mTextView.getSelectionStart() != mTextView.getSelectionEnd()) {
+                // We have a selection.
+                mSelectionPath.reset();
+                mTextView.getLayout().getSelectionPath(
+                        mTextView.getSelectionStart(), mTextView.getSelectionEnd(), mSelectionPath);
+                mSelectionPath.computeBounds(mSelectionBounds, true);
+                mSelectionBounds.bottom += mSelectionHandleHeight;
+            } else {
+                // We have a single cursor.
+                int line = mTextView.getLayout().getLineForOffset(mTextView.getSelectionStart());
+                float primaryHorizontal =
+                        mTextView.getLayout().getPrimaryHorizontal(mTextView.getSelectionStart());
+                mSelectionBounds.set(
+                        primaryHorizontal,
+                        mTextView.getLayout().getLineTop(line),
+                        primaryHorizontal + 1,
+                        mTextView.getLayout().getLineTop(line + 1) + mInsertionHandleHeight);
+            }
+            // Take TextView's padding and scroll into account.
+            int textHorizontalOffset = mTextView.viewportToContentHorizontalOffset();
+            int textVerticalOffset = mTextView.viewportToContentVerticalOffset();
+            outRect.set(
+                    (int) Math.floor(mSelectionBounds.left + textHorizontalOffset),
+                    (int) Math.floor(mSelectionBounds.top + textVerticalOffset),
+                    (int) Math.ceil(mSelectionBounds.right + textHorizontalOffset),
+                    (int) Math.ceil(mSelectionBounds.bottom + textVerticalOffset));
+        }
     }
 
     private void onReplace() {
@@ -3066,97 +3120,6 @@
         showSuggestions();
     }
 
-    private class ActionPopupWindow extends PinnedPopupWindow implements OnClickListener {
-        private static final int POPUP_TEXT_LAYOUT =
-                com.android.internal.R.layout.text_edit_action_popup_text;
-        private TextView mPasteTextView;
-        private TextView mReplaceTextView;
-
-        @Override
-        protected void createPopupWindow() {
-            mPopupWindow = new PopupWindow(mTextView.getContext(), null,
-                    com.android.internal.R.attr.textSelectHandleWindowStyle);
-            mPopupWindow.setClippingEnabled(true);
-        }
-
-        @Override
-        protected void initContentView() {
-            LinearLayout linearLayout = new LinearLayout(mTextView.getContext());
-            linearLayout.setOrientation(LinearLayout.HORIZONTAL);
-            mContentView = linearLayout;
-            mContentView.setBackgroundResource(
-                    com.android.internal.R.drawable.text_edit_paste_window);
-
-            LayoutInflater inflater = (LayoutInflater) mTextView.getContext().
-                    getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
-            LayoutParams wrapContent = new LayoutParams(
-                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
-            mPasteTextView = (TextView) inflater.inflate(POPUP_TEXT_LAYOUT, null);
-            mPasteTextView.setLayoutParams(wrapContent);
-            mContentView.addView(mPasteTextView);
-            mPasteTextView.setText(com.android.internal.R.string.paste);
-            mPasteTextView.setOnClickListener(this);
-
-            mReplaceTextView = (TextView) inflater.inflate(POPUP_TEXT_LAYOUT, null);
-            mReplaceTextView.setLayoutParams(wrapContent);
-            mContentView.addView(mReplaceTextView);
-            mReplaceTextView.setText(com.android.internal.R.string.replace);
-            mReplaceTextView.setOnClickListener(this);
-        }
-
-        @Override
-        public void show() {
-            boolean canPaste = mTextView.canPaste();
-            boolean canSuggest = mTextView.isSuggestionsEnabled() && isCursorInsideSuggestionSpan();
-            mPasteTextView.setVisibility(canPaste ? View.VISIBLE : View.GONE);
-            mReplaceTextView.setVisibility(canSuggest ? View.VISIBLE : View.GONE);
-
-            if (!canPaste && !canSuggest) return;
-
-            super.show();
-        }
-
-        @Override
-        public void onClick(View view) {
-            if (view == mPasteTextView && mTextView.canPaste()) {
-                mTextView.onTextContextMenuItem(TextView.ID_PASTE);
-                hide();
-            } else if (view == mReplaceTextView) {
-                onReplace();
-            }
-        }
-
-        @Override
-        protected int getTextOffset() {
-            return (mTextView.getSelectionStart() + mTextView.getSelectionEnd()) / 2;
-        }
-
-        @Override
-        protected int getVerticalLocalPosition(int line) {
-            return mTextView.getLayout().getLineTop(line) - mContentView.getMeasuredHeight();
-        }
-
-        @Override
-        protected int clipVertically(int positionY) {
-            if (positionY < 0) {
-                final int offset = getTextOffset();
-                final Layout layout = mTextView.getLayout();
-                final int line = layout.getLineForOffset(offset);
-                positionY += layout.getLineBottom(line) - layout.getLineTop(line);
-                positionY += mContentView.getMeasuredHeight();
-
-                // Assumes insertion and selection handles share the same height
-                final Drawable handle = mTextView.getContext().getDrawable(
-                        mTextView.mTextSelectHandleRes);
-                positionY += handle.getIntrinsicHeight();
-            }
-
-            return positionY;
-        }
-    }
-
     /**
      * A listener to call {@link InputMethodManager#updateCursorAnchorInfo(View, CursorAnchorInfo)}
      * while the input method is requesting the cursor/anchor position. Does nothing as long as
@@ -3348,7 +3311,7 @@
         // Minimum touch target size for handles
         private int mMinSize;
         // Indicates the line of text that the handle is on.
-        protected int mLine = -1;
+        protected int mPrevLine = -1;
 
         public HandleView(Drawable drawableLtr, Drawable drawableRtl) {
             super(mTextView.getContext());
@@ -3499,7 +3462,7 @@
                     addPositionToTouchUpFilter(offset);
                 }
                 final int line = layout.getLineForOffset(offset);
-                mLine = line;
+                mPrevLine = line;
 
                 mPositionX = (int) (layout.getPrimaryHorizontal(offset) - 0.5f - mHotspotX -
                         getHorizontalOffset() + getCursorOffset());
@@ -3839,19 +3802,22 @@
         public void updatePosition(float x, float y) {
             final int trueOffset = mTextView.getOffsetForPosition(x, y);
             final int currLine = mTextView.getLineAtCoordinate(y);
-            int offset = trueOffset;
-            boolean positionCursor = false;
 
+            // Don't select white space on different lines.
+            if (isWhitespaceLine(mPrevLine, currLine, trueOffset)) return;
+
+            boolean positionCursor = false;
+            int offset = trueOffset;
             int end = getWordEnd(offset, true);
             int start = getWordStart(offset);
 
             if (offset < mPrevOffset) {
                 // User is increasing the selection.
-                if (!mInWord || currLine < mLine) {
+                if (!mInWord || currLine < mPrevLine) {
                     // We're not in a word, or we're on a different line so we'll expand by
                     // word. First ensure the user has at least entered the next word.
                     int offsetToWord = Math.min((end - start) / 2, 2);
-                    if (offset <= end - offsetToWord || currLine < mLine) {
+                    if (offset <= end - offsetToWord || currLine < mPrevLine) {
                         offset = start;
                     } else {
                         offset = mPrevOffset;
@@ -3863,7 +3829,7 @@
                 positionCursor = true;
             } else if (offset - mTouchWordOffset > mPrevOffset) {
                 // User is shrinking the selection.
-                if (currLine > mLine) {
+                if (currLine > mPrevLine) {
                     // We're on a different line, so we'll snap to word boundaries.
                     offset = end;
                 }
@@ -3878,7 +3844,7 @@
                 final int selectionEnd = mTextView.getSelectionEnd();
                 if (offset >= selectionEnd) {
                     // We can't cross the handles so let's just constrain the Y value.
-                    int alteredOffset = mTextView.getOffsetAtCoordinate(mLine, x);
+                    int alteredOffset = mTextView.getOffsetAtCoordinate(mPrevLine, x);
                     if (alteredOffset >= selectionEnd) {
                         // Can't pass the other drag handle.
                         offset = Math.max(0, selectionEnd - 1);
@@ -3939,6 +3905,10 @@
         public void updatePosition(float x, float y) {
             final int trueOffset = mTextView.getOffsetForPosition(x, y);
             final int currLine = mTextView.getLineAtCoordinate(y);
+
+            // Don't select white space on different lines.
+            if (isWhitespaceLine(mPrevLine, currLine, trueOffset)) return;
+
             int offset = trueOffset;
             boolean positionCursor = false;
 
@@ -3947,11 +3917,11 @@
 
             if (offset > mPrevOffset) {
                 // User is increasing the selection.
-                if (!mInWord || currLine > mLine) {
+                if (!mInWord || currLine > mPrevLine) {
                     // We're not in a word, or we're on a different line so we'll expand by
                     // word. First ensure the user has at least entered the next word.
                     int midPoint = Math.min((end - start) / 2, 2);
-                    if (offset >= start + midPoint || currLine > mLine) {
+                    if (offset >= start + midPoint || currLine > mPrevLine) {
                         offset = end;
                     } else {
                         offset = mPrevOffset;
@@ -3963,7 +3933,7 @@
                 positionCursor = true;
             } else if (offset + mTouchWordOffset < mPrevOffset) {
                 // User is shrinking the selection.
-                if (currLine > mLine) {
+                if (currLine > mPrevLine) {
                     // We're on a different line, so we'll snap to word boundaries.
                     offset = getWordStart(offset);
                 }
@@ -3977,7 +3947,7 @@
                 final int selectionStart = mTextView.getSelectionStart();
                 if (offset <= selectionStart) {
                     // We can't cross the handles so let's just constrain the Y value.
-                    int alteredOffset = mTextView.getOffsetAtCoordinate(mLine, x);
+                    int alteredOffset = mTextView.getOffsetAtCoordinate(mPrevLine, x);
                     int length = mTextView.getText().length();
                     if (alteredOffset <= selectionStart) {
                         // Can't pass the other drag handle.
@@ -4002,6 +3972,36 @@
     }
 
     /**
+     * Checks whether selection is happening on a different line than previous and
+     * if that line only contains whitespace up to the touch location.
+     *
+     * @param prevLine The previous line the selection was on.
+     * @param currLine The current line being selected.
+     * @param offset The offset in the text where the touch occurred.
+     * @return Whether or not it was just a white space line being selected.
+     */
+    private boolean isWhitespaceLine(int prevLine, int currLine, int offset) {
+        if (prevLine == currLine) {
+            // Same line; don't care.
+            return false;
+        }
+        CharSequence text = mTextView.getText();
+        if (offset == text.length()) {
+            // No character at the last position.
+            return false;
+        }
+        int lineEndOffset = mTextView.getLayout().getLineEnd(currLine);
+        for (int cp, i = offset; i < lineEndOffset; i += Character.charCount(cp)) {
+            cp = Character.codePointAt(text, i);
+            if (!Character.isSpaceChar(cp) && !Character.isWhitespace(cp)) {
+                // There are non white space chars on the line.
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
      * A CursorController instance can be used to control a cursor in the text.
      */
     private interface CursorController extends ViewTreeObserver.OnTouchModeChangeListener {
@@ -4065,7 +4065,6 @@
     }
 
     class SelectionModifierCursorController implements CursorController {
-        private static final int DELAY_BEFORE_REPLACE_ACTION = 200; // milliseconds
         // The cursor controller handles, lazily created when shown.
         private SelectionStartHandleView mStartHandle;
         private SelectionEndHandleView mEndHandle;
@@ -4081,6 +4080,8 @@
         private int mStartOffset = -1;
         // Indicates whether the user is selecting text and using the drag accelerator.
         private boolean mDragAcceleratorActive;
+        // Indicates the line of text the drag accelerator is on.
+        private int mPrevLine = -1;
 
         SelectionModifierCursorController() {
             resetTouchOffsets();
@@ -4173,6 +4174,8 @@
                         }
                     }
 
+                    // New selection, reset line.
+                    mPrevLine = mTextView.getLineAtCoordinate(y);
                     mDownPositionX = x;
                     mDownPositionY = y;
                     mGestureStayedInTapRegion = true;
@@ -4229,6 +4232,13 @@
                             if (my > fingerOffset) my -= fingerOffset;
                             offset = mTextView.getOffsetForPosition(mx, my);
 
+                            int currLine = mTextView.getLineAtCoordinate(my);
+
+                            // Don't select white space on different lines.
+                            if (isWhitespaceLine(mPrevLine, currLine, offset)) return;
+
+                            mPrevLine = currLine;
+
                             // Perform the check for closeness at edge of view, if we're very close
                             // don't adjust the offset to be in front of the finger - otherwise the
                             // user can't select words at the edge.
@@ -4616,8 +4626,8 @@
 
                 // Otherwise the user inserted the composition.
                 String newText = TextUtils.substring(source, start, end);
-                EditOperation edit = new EditOperation(mEditor, false, "", dstart, newText);
-                recordEdit(edit);
+                EditOperation edit = new EditOperation(mEditor, "", dstart, newText);
+                recordEdit(edit, false /* forceMerge */);
                 return true;
             }
 
@@ -4636,11 +4646,15 @@
             // Build a new operation with all the information from this edit.
             String newText = TextUtils.substring(source, start, end);
             String oldText = TextUtils.substring(dest, dstart, dend);
-            EditOperation edit = new EditOperation(mEditor, forceMerge, oldText, dstart, newText);
-            recordEdit(edit);
+            EditOperation edit = new EditOperation(mEditor, oldText, dstart, newText);
+            recordEdit(edit, forceMerge);
         }
 
-        private void recordEdit(EditOperation edit) {
+        /**
+         * Fetches the last undo operation and checks to see if a new edit should be merged into it.
+         * If forceMerge is true then the new edit is always merged.
+         */
+        private void recordEdit(EditOperation edit, boolean forceMerge) {
             // Fetch the last edit operation and attempt to merge in the new edit.
             final UndoManager um = mEditor.mUndoManager;
             um.beginUpdate("Edit text");
@@ -4650,6 +4664,11 @@
                 // Add this as the first edit.
                 if (DEBUG_UNDO) Log.d(TAG, "filter: adding first op " + edit);
                 um.addOperation(edit, UndoManager.MERGE_MODE_NONE);
+            } else if (forceMerge) {
+                // Forced merges take priority because they could be the result of a non-user-edit
+                // change and this case should not create a new undo operation.
+                if (DEBUG_UNDO) Log.d(TAG, "filter: force merge " + edit);
+                lastEdit.forceMergeWith(edit);
             } else if (!mIsUserEdit) {
                 // An application directly modified the Editable outside of a text edit. Treat this
                 // as a new change and don't attempt to merge.
@@ -4725,7 +4744,6 @@
         private static final int TYPE_REPLACE = 2;
 
         private int mType;
-        private boolean mForceMerge;
         private String mOldText;
         private int mOldTextStart;
         private String mNewText;
@@ -4736,13 +4754,10 @@
 
         /**
          * Constructs an edit operation from a text input operation on editor that replaces the
-         * oldText starting at dstart with newText. If forceMerge is true then always forcibly
-         * merge this operation with any previous one.
+         * oldText starting at dstart with newText.
          */
-        public EditOperation(Editor editor, boolean forceMerge, String oldText, int dstart,
-                String newText) {
+        public EditOperation(Editor editor, String oldText, int dstart, String newText) {
             super(editor.mUndoOwner);
-            mForceMerge = forceMerge;
             mOldText = oldText;
             mNewText = newText;
 
@@ -4769,7 +4784,6 @@
         public EditOperation(Parcel src, ClassLoader loader) {
             super(src, loader);
             mType = src.readInt();
-            mForceMerge = src.readInt() != 0;
             mOldText = src.readString();
             mOldTextStart = src.readInt();
             mNewText = src.readString();
@@ -4781,7 +4795,6 @@
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(mType);
-            dest.writeInt(mForceMerge ? 1 : 0);
             dest.writeString(mOldText);
             dest.writeInt(mOldTextStart);
             dest.writeString(mNewText);
@@ -4833,10 +4846,6 @@
                 Log.d(TAG, "mergeWith old " + this);
                 Log.d(TAG, "mergeWith new " + edit);
             }
-            if (edit.mForceMerge) {
-                forceMergeWith(edit);
-                return true;
-            }
             switch (mType) {
                 case TYPE_INSERT:
                     return mergeInsertWith(edit);
@@ -4894,7 +4903,7 @@
          * Forcibly creates a single merged edit operation by simulating the entire text
          * contents being replaced.
          */
-        private void forceMergeWith(EditOperation edit) {
+        public void forceMergeWith(EditOperation edit) {
             if (DEBUG_UNDO) Log.d(TAG, "forceMerge");
             Editor editor = getOwnerData();
 
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index f6d198b..0602944 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -53,8 +53,6 @@
  * only if {@link #setMeasureAllChildren(boolean) setConsiderGoneChildrenWhenMeasuring()}
  * is set to true.
  *
- * @attr ref android.R.styleable#FrameLayout_foreground
- * @attr ref android.R.styleable#FrameLayout_foregroundGravity
  * @attr ref android.R.styleable#FrameLayout_measureAllChildren
  */
 @RemoteView
@@ -64,13 +62,6 @@
     @ViewDebug.ExportedProperty(category = "measurement")
     boolean mMeasureAllChildren = false;
 
-    @ViewDebug.ExportedProperty(category = "drawing")
-    private Drawable mForeground;
-    private ColorStateList mForegroundTintList = null;
-    private PorterDuff.Mode mForegroundTintMode = null;
-    private boolean mHasForegroundTint = false;
-    private boolean mHasForegroundTintMode = false;
-
     @ViewDebug.ExportedProperty(category = "padding")
     private int mForegroundPaddingLeft = 0;
 
@@ -85,15 +76,6 @@
 
     private final Rect mSelfBounds = new Rect();
     private final Rect mOverlayBounds = new Rect();
-
-    @ViewDebug.ExportedProperty(category = "drawing")
-    private int mForegroundGravity = Gravity.FILL;
-
-    /** {@hide} */
-    @ViewDebug.ExportedProperty(category = "drawing")
-    protected boolean mForegroundInPadding = true;
-
-    boolean mForegroundBoundsChanged = false;
     
     private final ArrayList<View> mMatchParentChildren = new ArrayList<View>(1);
     
@@ -115,48 +97,12 @@
 
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, com.android.internal.R.styleable.FrameLayout, defStyleAttr, defStyleRes);
-
-        mForegroundGravity = a.getInt(
-                com.android.internal.R.styleable.FrameLayout_foregroundGravity, mForegroundGravity);
-
-        final Drawable d = a.getDrawable(com.android.internal.R.styleable.FrameLayout_foreground);
-        if (d != null) {
-            setForeground(d);
-        }
         
         if (a.getBoolean(com.android.internal.R.styleable.FrameLayout_measureAllChildren, false)) {
             setMeasureAllChildren(true);
         }
 
-        if (a.hasValue(R.styleable.FrameLayout_foregroundTintMode)) {
-            mForegroundTintMode = Drawable.parseTintMode(a.getInt(
-                    R.styleable.FrameLayout_foregroundTintMode, -1), mForegroundTintMode);
-            mHasForegroundTintMode = true;
-        }
-
-        if (a.hasValue(R.styleable.FrameLayout_foregroundTint)) {
-            mForegroundTintList = a.getColorStateList(R.styleable.FrameLayout_foregroundTint);
-            mHasForegroundTint = true;
-        }
-
-        mForegroundInPadding = a.getBoolean(R.styleable.FrameLayout_foregroundInsidePadding, true);
-
         a.recycle();
-
-        applyForegroundTint();
-    }
-
-    /**
-     * Describes how the foreground is positioned.
-     *
-     * @return foreground gravity.
-     *
-     * @see #setForegroundGravity(int)
-     *
-     * @attr ref android.R.styleable#FrameLayout_foregroundGravity
-     */
-    public int getForegroundGravity() {
-        return mForegroundGravity;
     }
 
     /**
@@ -166,25 +112,18 @@
      *
      * @see #getForegroundGravity()
      *
-     * @attr ref android.R.styleable#FrameLayout_foregroundGravity
+     * @attr ref android.R.styleable#View_foregroundGravity
      */
     @android.view.RemotableViewMethod
     public void setForegroundGravity(int foregroundGravity) {
-        if (mForegroundGravity != foregroundGravity) {
-            if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
-                foregroundGravity |= Gravity.START;
-            }
+        if (getForegroundGravity() != foregroundGravity) {
+            super.setForegroundGravity(foregroundGravity);
 
-            if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
-                foregroundGravity |= Gravity.TOP;
-            }
-
-            mForegroundGravity = foregroundGravity;
-
-
-            if (mForegroundGravity == Gravity.FILL && mForeground != null) {
+            // calling get* again here because the set above may apply default constraints
+            final Drawable foreground = getForeground();
+            if (getForegroundGravity() == Gravity.FILL && foreground != null) {
                 Rect padding = new Rect();
-                if (mForeground.getPadding(padding)) {
+                if (foreground.getPadding(padding)) {
                     mForegroundPaddingLeft = padding.left;
                     mForegroundPaddingTop = padding.top;
                     mForegroundPaddingRight = padding.right;
@@ -201,53 +140,6 @@
         }
     }
 
-    @Override
-    protected void onVisibilityChanged(@NonNull View changedView, @Visibility int visibility) {
-        super.onVisibilityChanged(changedView, visibility);
-
-        final Drawable dr = mForeground;
-        if (dr != null) {
-            final boolean visible = visibility == VISIBLE && getVisibility() == VISIBLE;
-            if (visible != dr.isVisible()) {
-                dr.setVisible(visible, false);
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return super.verifyDrawable(who) || (who == mForeground);
-    }
-
-    @Override
-    public void jumpDrawablesToCurrentState() {
-        super.jumpDrawablesToCurrentState();
-        if (mForeground != null) mForeground.jumpToCurrentState();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        if (mForeground != null && mForeground.isStateful()) {
-            mForeground.setState(getDrawableState());
-        }
-    }
-
-    @Override
-    public void drawableHotspotChanged(float x, float y) {
-        super.drawableHotspotChanged(x, y);
-
-        if (mForeground != null) {
-            mForeground.setHotspot(x, y);
-        }
-    }
-
     /**
      * Returns a set of layout parameters with a width of
      * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT},
@@ -258,161 +150,23 @@
         return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
     }
 
-    /**
-     * Supply a Drawable that is to be rendered on top of all of the child
-     * views in the frame layout.  Any padding in the Drawable will be taken
-     * into account by ensuring that the children are inset to be placed
-     * inside of the padding area.
-     * 
-     * @param d The Drawable to be drawn on top of the children.
-     * 
-     * @attr ref android.R.styleable#FrameLayout_foreground
-     */
-    public void setForeground(Drawable d) {
-        if (mForeground != d) {
-            if (mForeground != null) {
-                mForeground.setCallback(null);
-                unscheduleDrawable(mForeground);
-            }
-
-            mForeground = d;
-            mForegroundPaddingLeft = 0;
-            mForegroundPaddingTop = 0;
-            mForegroundPaddingRight = 0;
-            mForegroundPaddingBottom = 0;
-
-            if (d != null) {
-                setWillNotDraw(false);
-                d.setCallback(this);
-                d.setLayoutDirection(getLayoutDirection());
-                if (d.isStateful()) {
-                    d.setState(getDrawableState());
-                }
-                applyForegroundTint();
-                if (mForegroundGravity == Gravity.FILL) {
-                    Rect padding = new Rect();
-                    if (d.getPadding(padding)) {
-                        mForegroundPaddingLeft = padding.left;
-                        mForegroundPaddingTop = padding.top;
-                        mForegroundPaddingRight = padding.right;
-                        mForegroundPaddingBottom = padding.bottom;
-                    }
-                }
-            }  else {
-                setWillNotDraw(true);
-            }
-            requestLayout();
-            invalidate();
-        }
-    }
-
-    /**
-     * Returns the drawable used as the foreground of this FrameLayout. The
-     * foreground drawable, if non-null, is always drawn on top of the children.
-     *
-     * @return A Drawable or null if no foreground was set.
-     */
-    public Drawable getForeground() {
-        return mForeground;
-    }
-
-    /**
-     * Applies a tint to the foreground drawable. Does not modify the current
-     * tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
-     * <p>
-     * Subsequent calls to {@link #setForeground(Drawable)} will automatically
-     * mutate the drawable and apply the specified tint and tint mode using
-     * {@link Drawable#setTintList(ColorStateList)}.
-     *
-     * @param tint the tint to apply, may be {@code null} to clear tint
-     *
-     * @attr ref android.R.styleable#FrameLayout_foregroundTint
-     * @see #getForegroundTintList()
-     * @see Drawable#setTintList(ColorStateList)
-     */
-    public void setForegroundTintList(@Nullable ColorStateList tint) {
-        mForegroundTintList = tint;
-        mHasForegroundTint = true;
-
-        applyForegroundTint();
-    }
-
-    /**
-     * @return the tint applied to the foreground drawable
-     * @attr ref android.R.styleable#FrameLayout_foregroundTint
-     * @see #setForegroundTintList(ColorStateList)
-     */
-    @Nullable
-    public ColorStateList getForegroundTintList() {
-        return mForegroundTintList;
-    }
-
-    /**
-     * Specifies the blending mode used to apply the tint specified by
-     * {@link #setForegroundTintList(ColorStateList)}} to the foreground drawable.
-     * The default mode is {@link PorterDuff.Mode#SRC_IN}.
-     *
-     * @param tintMode the blending mode used to apply the tint, may be
-     *                 {@code null} to clear tint
-     * @attr ref android.R.styleable#FrameLayout_foregroundTintMode
-     * @see #getForegroundTintMode()
-     * @see Drawable#setTintMode(PorterDuff.Mode)
-     */
-    public void setForegroundTintMode(@Nullable PorterDuff.Mode tintMode) {
-        mForegroundTintMode = tintMode;
-        mHasForegroundTintMode = true;
-
-        applyForegroundTint();
-    }
-
-    /**
-     * @return the blending mode used to apply the tint to the foreground
-     *         drawable
-     * @attr ref android.R.styleable#FrameLayout_foregroundTintMode
-     * @see #setForegroundTintMode(PorterDuff.Mode)
-     */
-    @Nullable
-    public PorterDuff.Mode getForegroundTintMode() {
-        return mForegroundTintMode;
-    }
-
-    private void applyForegroundTint() {
-        if (mForeground != null && (mHasForegroundTint || mHasForegroundTintMode)) {
-            mForeground = mForeground.mutate();
-
-            if (mHasForegroundTint) {
-                mForeground.setTintList(mForegroundTintList);
-            }
-
-            if (mHasForegroundTintMode) {
-                mForeground.setTintMode(mForegroundTintMode);
-            }
-
-            // The drawable (or one of its children) may not have been
-            // stateful before applying the tint, so let's try again.
-            if (mForeground.isStateful()) {
-                mForeground.setState(getDrawableState());
-            }
-        }
-    }
-
     int getPaddingLeftWithForeground() {
-        return mForegroundInPadding ? Math.max(mPaddingLeft, mForegroundPaddingLeft) :
+        return isForegroundInsidePadding() ? Math.max(mPaddingLeft, mForegroundPaddingLeft) :
             mPaddingLeft + mForegroundPaddingLeft;
     }
 
     int getPaddingRightWithForeground() {
-        return mForegroundInPadding ? Math.max(mPaddingRight, mForegroundPaddingRight) :
+        return isForegroundInsidePadding() ? Math.max(mPaddingRight, mForegroundPaddingRight) :
             mPaddingRight + mForegroundPaddingRight;
     }
 
     private int getPaddingTopWithForeground() {
-        return mForegroundInPadding ? Math.max(mPaddingTop, mForegroundPaddingTop) :
+        return isForegroundInsidePadding() ? Math.max(mPaddingTop, mForegroundPaddingTop) :
             mPaddingTop + mForegroundPaddingTop;
     }
 
     private int getPaddingBottomWithForeground() {
-        return mForegroundInPadding ? Math.max(mPaddingBottom, mForegroundPaddingBottom) :
+        return isForegroundInsidePadding() ? Math.max(mPaddingBottom, mForegroundPaddingBottom) :
             mPaddingBottom + mForegroundPaddingBottom;
     }
 
@@ -527,8 +281,6 @@
         final int parentTop = getPaddingTopWithForeground();
         final int parentBottom = bottom - top - getPaddingBottomWithForeground();
 
-        mForegroundBoundsChanged = true;
-        
         for (int i = 0; i < count; i++) {
             final View child = getChildAt(i);
             if (child.getVisibility() != GONE) {
@@ -585,62 +337,6 @@
     }
 
     /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-        mForegroundBoundsChanged = true;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void draw(Canvas canvas) {
-        super.draw(canvas);
-
-        if (mForeground != null) {
-            final Drawable foreground = mForeground;
-
-            if (mForegroundBoundsChanged) {
-                mForegroundBoundsChanged = false;
-                final Rect selfBounds = mSelfBounds;
-                final Rect overlayBounds = mOverlayBounds;
-
-                final int w = mRight-mLeft;
-                final int h = mBottom-mTop;
-
-                if (mForegroundInPadding) {
-                    selfBounds.set(0, 0, w, h);
-                } else {
-                    selfBounds.set(mPaddingLeft, mPaddingTop, w - mPaddingRight, h - mPaddingBottom);
-                }
-
-                final int layoutDirection = getLayoutDirection();
-                Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),
-                        foreground.getIntrinsicHeight(), selfBounds, overlayBounds,
-                        layoutDirection);
-                foreground.setBounds(overlayBounds);
-            }
-            
-            foreground.draw(canvas);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean gatherTransparentRegion(Region region) {
-        boolean opaque = super.gatherTransparentRegion(region);
-        if (region != null && mForeground != null) {
-            applyDrawableToTransparentRegion(mForeground, region);
-        }
-        return opaque;
-    }
-
-    /**
      * Sets whether to consider all children, or just those in
      * the VISIBLE or INVISIBLE state, when measuring. Defaults to false.
      *
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index af5a8bf..b187c1c 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -1209,13 +1209,13 @@
         switch (keyCode) {
             
         case KeyEvent.KEYCODE_DPAD_LEFT:
-            if (movePrevious()) {
+            if (moveDirection(-1)) {
                 playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
                 return true;
             }
             break;
         case KeyEvent.KEYCODE_DPAD_RIGHT:
-            if (moveNext()) {
+            if (moveDirection(1)) {
                 playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
                 return true;
             }
@@ -1255,18 +1255,12 @@
         return super.onKeyUp(keyCode, event);
     }
     
-    boolean movePrevious() {
-        if (mItemCount > 0 && mSelectedPosition > 0) {
-            scrollToChild(mSelectedPosition - mFirstPosition - 1);
-            return true;
-        } else {
-            return false;
-        }
-    }
+    boolean moveDirection(int direction) {
+        direction = isLayoutRtl() ? -direction : direction;
+        int targetPosition = mSelectedPosition + direction;
 
-    boolean moveNext() {
-        if (mItemCount > 0 && mSelectedPosition < mItemCount - 1) {
-            scrollToChild(mSelectedPosition - mFirstPosition + 1);
+        if (mItemCount > 0 && targetPosition >= 0 && targetPosition < mItemCount) {
+            scrollToChild(targetPosition - mFirstPosition);
             return true;
         } else {
             return false;
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index d85bbb9..310412f9 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -567,13 +567,11 @@
     public void show() {
         int height = buildDropDown();
 
-        int widthSpec = 0;
-        int heightSpec = 0;
-
         boolean noInputMethod = isInputMethodNotNeeded();
         mPopup.setAllowScrollingAnchorParent(!noInputMethod);
 
         if (mPopup.isShowing()) {
+            final int widthSpec;
             if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
                 // The call to PopupWindow's update method below can accept -1 for any
                 // value you do not want to update.
@@ -584,19 +582,19 @@
                 widthSpec = mDropDownWidth;
             }
 
+            final int heightSpec;
             if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
                 // The call to PopupWindow's update method below can accept -1 for any
                 // value you do not want to update.
                 heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.MATCH_PARENT;
                 if (noInputMethod) {
-                    mPopup.setWindowLayoutMode(
-                            mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
-                                    ViewGroup.LayoutParams.MATCH_PARENT : 0, 0);
+                    mPopup.setWidth(mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
+                            ViewGroup.LayoutParams.MATCH_PARENT : 0);
+                    mPopup.setHeight(0);
                 } else {
-                    mPopup.setWindowLayoutMode(
-                            mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
-                                    ViewGroup.LayoutParams.MATCH_PARENT : 0,
-                            ViewGroup.LayoutParams.MATCH_PARENT);
+                    mPopup.setWidth(mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
+                                    ViewGroup.LayoutParams.MATCH_PARENT : 0);
+                    mPopup.setHeight(ViewGroup.LayoutParams.MATCH_PARENT);
                 }
             } else if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
                 heightSpec = height;
@@ -604,32 +602,37 @@
                 heightSpec = mDropDownHeight;
             }
 
+            mPopup.setWidth(widthSpec);
+            mPopup.setHeight(heightSpec);
             mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
 
             mPopup.update(getAnchorView(), mDropDownHorizontalOffset,
-                    mDropDownVerticalOffset, widthSpec, heightSpec);
+                            mDropDownVerticalOffset, -1, -1);
         } else {
+            final int widthSpec;
             if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
                 widthSpec = ViewGroup.LayoutParams.MATCH_PARENT;
             } else {
                 if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
-                    mPopup.setWidth(getAnchorView().getWidth());
+                    widthSpec = getAnchorView().getWidth();
                 } else {
-                    mPopup.setWidth(mDropDownWidth);
+                    widthSpec = mDropDownWidth;
                 }
             }
 
+            final int heightSpec;
             if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
                 heightSpec = ViewGroup.LayoutParams.MATCH_PARENT;
             } else {
                 if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
-                    mPopup.setHeight(height);
+                    heightSpec = height;
                 } else {
-                    mPopup.setHeight(mDropDownHeight);
+                    heightSpec = mDropDownHeight;
                 }
             }
 
-            mPopup.setWindowLayoutMode(widthSpec, heightSpec);
+            mPopup.setWidth(widthSpec);
+            mPopup.setHeight(heightSpec);
             mPopup.setClipToScreenEnabled(true);
             
             // use outside touchable to dismiss drop down when touching outside of it, so
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index f676254..8792323 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -46,6 +46,7 @@
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.view.ViewTreeObserver.OnScrollChangedListener;
 import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
 
 import java.lang.ref.WeakReference;
 
@@ -126,10 +127,10 @@
     private OnTouchListener mTouchInterceptor;
 
     private int mWidthMode;
-    private int mWidth;
+    private int mWidth = LayoutParams.WRAP_CONTENT;
     private int mLastWidth;
     private int mHeightMode;
-    private int mHeight;
+    private int mHeight = LayoutParams.WRAP_CONTENT;
     private int mLastHeight;
 
     private int mPopupWidth;
@@ -907,17 +908,19 @@
      * {@link ViewGroup.LayoutParams#WRAP_CONTENT},
      * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute
      * height.
+     *
+     * @deprecated Use {@link #setWidth(int)} and {@link #setHeight(int)}.
      */
+    @Deprecated
     public void setWindowLayoutMode(int widthSpec, int heightSpec) {
         mWidthMode = widthSpec;
         mHeightMode = heightSpec;
     }
 
     /**
-     * <p>Return this popup's height MeasureSpec</p>
+     * Returns the popup's height MeasureSpec.
      *
      * @return the height MeasureSpec of the popup
-     *
      * @see #setHeight(int)
      */
     public int getHeight() {
@@ -925,13 +928,12 @@
     }
 
     /**
-     * <p>Change the popup's height MeasureSpec</p>
-     *
-     * <p>If the popup is showing, calling this method will take effect only
-     * the next time the popup is shown.</p>
+     * Sets the popup's height MeasureSpec.
+     * <p>
+     * If the popup is showing, calling this method will take effect the next
+     * time the popup is shown.
      *
      * @param height the height MeasureSpec of the popup
-     *
      * @see #getHeight()
      * @see #isShowing()
      */
@@ -940,10 +942,9 @@
     }
 
     /**
-     * <p>Return this popup's width MeasureSpec</p>
+     * Returns the popup's width MeasureSpec.
      *
      * @return the width MeasureSpec of the popup
-     *
      * @see #setWidth(int)
      */
     public int getWidth() {
@@ -951,13 +952,12 @@
     }
 
     /**
-     * <p>Change the popup's width MeasureSpec</p>
-     *
-     * <p>If the popup is showing, calling this method will take effect only
-     * the next time the popup is shown.</p>
+     * Sets the popup's width MeasureSpec.
+     * <p>
+     * If the popup is showing, calling this method will take effect the next
+     * time the popup is shown.
      *
      * @param width the width MeasureSpec of the popup
-     *
      * @see #getWidth()
      * @see #isShowing()
      */
@@ -1658,10 +1658,17 @@
 
     /**
      * Updates the state of the popup window, if it is currently being displayed,
-     * from the currently set state.  This includes:
-     * {@link #setClippingEnabled(boolean)}, {@link #setFocusable(boolean)},
-     * {@link #setIgnoreCheekPress()}, {@link #setInputMethodMode(int)},
-     * {@link #setTouchable(boolean)}, and {@link #setAnimationStyle(int)}.
+     * from the currently set state.
+     * <p>
+     * This includes:
+     * <ul>
+     *     <li>{@link #setClippingEnabled(boolean)}</li>
+     *     <li>{@link #setFocusable(boolean)}</li>
+     *     <li>{@link #setIgnoreCheekPress()}</li>
+     *     <li>{@link #setInputMethodMode(int)}</li>
+     *     <li>{@link #setTouchable(boolean)}</li>
+     *     <li>{@link #setAnimationStyle(int)}</li>
+     * </ul>
      */
     public void update() {
         if (!isShowing() || mContentView == null) {
@@ -1692,12 +1699,13 @@
     }
 
     /**
-     * <p>Updates the dimension of the popup window. Calling this function
-     * also updates the window with the current popup state as described
-     * for {@link #update()}.</p>
+     * Updates the dimension of the popup window.
+     * <p>
+     * Calling this function also updates the window with the current popup
+     * state as described for {@link #update()}.
      *
-     * @param width the new width
-     * @param height the new height
+     * @param width the new width, must be >= 0 or -1 to ignore
+     * @param height the new height, must be >= 0 or -1 to ignore
      */
     public void update(int width, int height) {
         final WindowManager.LayoutParams p =
@@ -1706,40 +1714,43 @@
     }
 
     /**
-     * <p>Updates the position and the dimension of the popup window. Width and
-     * height can be set to -1 to update location only.  Calling this function
-     * also updates the window with the current popup state as
-     * described for {@link #update()}.</p>
+     * Updates the position and the dimension of the popup window.
+     * <p>
+     * Width and height can be set to -1 to update location only. Calling this
+     * function also updates the window with the current popup state as
+     * described for {@link #update()}.
      *
      * @param x the new x location
      * @param y the new y location
-     * @param width the new width, can be -1 to ignore
-     * @param height the new height, can be -1 to ignore
+     * @param width the new width, must be >= 0 or -1 to ignore
+     * @param height the new height, must be >= 0 or -1 to ignore
      */
     public void update(int x, int y, int width, int height) {
         update(x, y, width, height, false);
     }
 
     /**
-     * <p>Updates the position and the dimension of the popup window. Width and
-     * height can be set to -1 to update location only.  Calling this function
-     * also updates the window with the current popup state as
-     * described for {@link #update()}.</p>
+     * Updates the position and the dimension of the popup window.
+     * <p>
+     * Width and height can be set to -1 to update location only. Calling this
+     * function also updates the window with the current popup state as
+     * described for {@link #update()}.
      *
      * @param x the new x location
      * @param y the new y location
-     * @param width the new width, can be -1 to ignore
-     * @param height the new height, can be -1 to ignore
-     * @param force reposition the window even if the specified position
-     *              already seems to correspond to the LayoutParams
+     * @param width the new width, must be >= 0 or -1 to ignore
+     * @param height the new height, must be >= 0 or -1 to ignore
+     * @param force {@code true} to reposition the window even if the specified
+     *              position already seems to correspond to the LayoutParams,
+     *              {@code false} to only reposition if needed
      */
     public void update(int x, int y, int width, int height, boolean force) {
-        if (width != -1) {
+        if (width >= 0) {
             mLastWidth = width;
             setWidth(width);
         }
 
-        if (height != -1) {
+        if (height >= 0) {
             mLastHeight = height;
             setHeight(height);
         }
@@ -1794,32 +1805,34 @@
     }
 
     /**
-     * <p>Updates the position and the dimension of the popup window. Calling this
-     * function also updates the window with the current popup state as described
-     * for {@link #update()}.</p>
+     * Updates the position and the dimension of the popup window.
+     * <p>
+     * Calling this function also updates the window with the current popup
+     * state as described for {@link #update()}.
      *
      * @param anchor the popup's anchor view
-     * @param width the new width, can be -1 to ignore
-     * @param height the new height, can be -1 to ignore
+     * @param width the new width, must be >= 0 or -1 to ignore
+     * @param height the new height, must be >= 0 or -1 to ignore
      */
     public void update(View anchor, int width, int height) {
         update(anchor, false, 0, 0, true, width, height);
     }
 
     /**
-     * <p>Updates the position and the dimension of the popup window. Width and
-     * height can be set to -1 to update location only.  Calling this function
-     * also updates the window with the current popup state as
-     * described for {@link #update()}.</p>
-     *
-     * <p>If the view later scrolls to move <code>anchor</code> to a different
-     * location, the popup will be moved correspondingly.</p>
+     * Updates the position and the dimension of the popup window.
+     * <p>
+     * Width and height can be set to -1 to update location only. Calling this
+     * function also updates the window with the current popup state as
+     * described for {@link #update()}.
+     * <p>
+     * If the view later scrolls to move {@code anchor} to a different
+     * location, the popup will be moved correspondingly.
      *
      * @param anchor the popup's anchor view
      * @param xoff x offset from the view's left edge
      * @param yoff y offset from the view's bottom edge
-     * @param width the new width, can be -1 to ignore
-     * @param height the new height, can be -1 to ignore
+     * @param width the new width, must be >= 0 or -1 to ignore
+     * @param height the new height, must be >= 0 or -1 to ignore
      */
     public void update(View anchor, int xoff, int yoff, int width, int height) {
         update(anchor, true, xoff, yoff, true, width, height);
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 28b4db2..143dea4 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -122,8 +122,9 @@
     private final Paint mPaintCenter = new Paint();
 
     private final Paint[][] mPaintSelector = new Paint[2][3];
-    private final int[][] mColorSelector = new int[2][3];
-    private final IntHolder[][] mAlphaSelector = new IntHolder[2][3];
+
+    private final int mSelectorColor;
+    private final int mSelectorDotColor;
 
     private final Paint mPaintBackground = new Paint();
 
@@ -147,6 +148,8 @@
 
     private final RadialPickerTouchHelper mTouchHelper;
 
+    private final Path mSelectorPath = new Path();
+
     private boolean mIs24HourMode;
     private boolean mShowHours;
 
@@ -316,11 +319,6 @@
         for (int i = 0; i < mAlpha.length; i++) {
             mAlpha[i] = new IntHolder(ALPHA_OPAQUE);
         }
-        for (int i = 0; i < mAlphaSelector.length; i++) {
-            for (int j = 0; j < mAlphaSelector[i].length; j++) {
-                mAlphaSelector[i][j] = new IntHolder(ALPHA_OPAQUE);
-            }
-        }
 
         mTextColor[HOURS] = a.getColorStateList(R.styleable.TimePicker_numbersTextColor);
         mTextColor[HOURS_INNER] = a.getColorStateList(R.styleable.TimePicker_numbersInnerTextColor);
@@ -345,33 +343,28 @@
         final int[] activatedStateSet = StateSet.get(
                 StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED);
 
+        mSelectorColor = selectorActivatedColor;
+        mSelectorDotColor = mTextColor[HOURS].getColorForState(activatedStateSet, 0);
+
         mPaintSelector[HOURS][SELECTOR_CIRCLE] = new Paint();
         mPaintSelector[HOURS][SELECTOR_CIRCLE].setAntiAlias(true);
-        mColorSelector[HOURS][SELECTOR_CIRCLE] = selectorActivatedColor;
 
         mPaintSelector[HOURS][SELECTOR_DOT] = new Paint();
         mPaintSelector[HOURS][SELECTOR_DOT].setAntiAlias(true);
-        mColorSelector[HOURS][SELECTOR_DOT] =
-                mTextColor[HOURS].getColorForState(activatedStateSet, 0);
 
         mPaintSelector[HOURS][SELECTOR_LINE] = new Paint();
         mPaintSelector[HOURS][SELECTOR_LINE].setAntiAlias(true);
         mPaintSelector[HOURS][SELECTOR_LINE].setStrokeWidth(2);
-        mColorSelector[HOURS][SELECTOR_LINE] = selectorActivatedColor;
 
         mPaintSelector[MINUTES][SELECTOR_CIRCLE] = new Paint();
         mPaintSelector[MINUTES][SELECTOR_CIRCLE].setAntiAlias(true);
-        mColorSelector[MINUTES][SELECTOR_CIRCLE] = selectorActivatedColor;
 
         mPaintSelector[MINUTES][SELECTOR_DOT] = new Paint();
         mPaintSelector[MINUTES][SELECTOR_DOT].setAntiAlias(true);
-        mColorSelector[MINUTES][SELECTOR_DOT] =
-                mTextColor[MINUTES].getColorForState(activatedStateSet, 0);
 
         mPaintSelector[MINUTES][SELECTOR_LINE] = new Paint();
         mPaintSelector[MINUTES][SELECTOR_LINE].setAntiAlias(true);
         mPaintSelector[MINUTES][SELECTOR_LINE].setStrokeWidth(2);
-        mColorSelector[MINUTES][SELECTOR_LINE] = selectorActivatedColor;
 
         mPaintBackground.setColor(a.getColor(R.styleable.TimePicker_numbersBackgroundColor,
                 context.getColor(R.color.timepicker_default_numbers_background_color_material)));
@@ -600,8 +593,8 @@
         // Initialize the hours and minutes numbers.
         for (int i = 0; i < 12; i++) {
             mHours12Texts[i] = String.format("%d", HOURS_NUMBERS[i]);
-            mOuterHours24Texts[i] = String.format("%02d", HOURS_NUMBERS_24[i]);
-            mInnerHours24Texts[i] = String.format("%d", HOURS_NUMBERS[i]);
+            mInnerHours24Texts[i] = String.format("%02d", HOURS_NUMBERS_24[i]);
+            mOuterHours24Texts[i] = String.format("%d", HOURS_NUMBERS[i]);
             mMinutesTexts[i] = String.format("%02d", MINUTES_NUMBERS[i]);
         }
     }
@@ -612,22 +605,16 @@
             mInnerTextHours = mInnerHours24Texts;
         } else {
             mOuterTextHours = mHours12Texts;
-            mInnerTextHours = null;
+            mInnerTextHours = mHours12Texts;
         }
 
         mOuterTextMinutes = mMinutesTexts;
 
         final int hoursAlpha = mShowHours ? ALPHA_OPAQUE : ALPHA_TRANSPARENT;
         mAlpha[HOURS].setValue(hoursAlpha);
-        mAlphaSelector[HOURS][SELECTOR_CIRCLE].setValue(hoursAlpha);
-        mAlphaSelector[HOURS][SELECTOR_DOT].setValue(hoursAlpha);
-        mAlphaSelector[HOURS][SELECTOR_LINE].setValue(hoursAlpha);
 
         final int minutesAlpha = mShowHours ? ALPHA_TRANSPARENT : ALPHA_OPAQUE;
         mAlpha[MINUTES].setValue(minutesAlpha);
-        mAlphaSelector[MINUTES][SELECTOR_CIRCLE].setValue(minutesAlpha);
-        mAlphaSelector[MINUTES][SELECTOR_DOT].setValue(minutesAlpha);
-        mAlphaSelector[MINUTES][SELECTOR_LINE].setValue(minutesAlpha);
     }
 
     @Override
@@ -641,7 +628,7 @@
         mCircleRadius = Math.min(mXCenter, mYCenter);
 
         mMinHypotenuseForInnerNumber = mCircleRadius - mTextInset[HOURS_INNER] - mSelectorRadius;
-        mMaxHypotenuseForOuterNumber = mCircleRadius - mTextInset[HOURS] - mSelectorRadius;
+        mMaxHypotenuseForOuterNumber = mCircleRadius - mTextInset[HOURS] + mSelectorRadius;
         mHalfwayHypotenusePoint = mCircleRadius - (mTextInset[HOURS] + mTextInset[HOURS_INNER]) / 2;
 
         calculatePositionsHours();
@@ -675,7 +662,7 @@
                     mOuterTextHours, mOuterTextX[HOURS], mOuterTextY[HOURS], mPaint[HOURS],
                     hoursAlpha, !mIsOnInnerCircle, mSelectionDegrees[HOURS], false);
 
-            // Draw inner hours (12-23) for 24-hour time.
+            // Draw inner hours (13-00) for 24-hour time.
             if (mIs24HourMode && mInnerTextHours != null) {
                 drawTextElements(canvas, mTextSize[HOURS_INNER], mTypeface, mTextColor[HOURS_INNER],
                         mInnerTextHours, mInnerTextX, mInnerTextY, mPaint[HOURS], hoursAlpha,
@@ -714,69 +701,61 @@
         canvas.drawCircle(mXCenter, mYCenter, mCenterDotRadius, mPaintCenter);
     }
 
+    private int applyAlpha(int argb, int alpha) {
+        final int srcAlpha = (argb >> 24) & 0xFF;
+        final int dstAlpha = (int) (srcAlpha * (alpha / 255.0) + 0.5f);
+        return (0xFFFFFF & argb) | (dstAlpha << 24);
+    }
+
     private int getMultipliedAlpha(int argb, int alpha) {
         return (int) (Color.alpha(argb) * (alpha / 255.0) + 0.5);
     }
 
-    private final Path mSelectorPath = new Path();
-
     private void drawSelector(Canvas canvas, int index, Path selectorPath, float alphaMod) {
+        final int alpha = (int) (mAlpha[index % 2].getValue() * alphaMod + 0.5f);
+        final int color = applyAlpha(mSelectorColor, alpha);
+
         // Calculate the current radius at which to place the selection circle.
-        mLineLength[index] = mCircleRadius - mTextInset[index];
+        final int selRadius = mSelectorRadius;
+        final int selLength = mCircleRadius - mTextInset[index];
+        final double selAngleRad = Math.toRadians(mSelectionDegrees[index]);
+        final float selCenterX = mXCenter + selLength * (float) Math.sin(selAngleRad);
+        final float selCenterY = mYCenter - selLength * (float) Math.cos(selAngleRad);
 
-        final double selectionRadians = Math.toRadians(mSelectionDegrees[index]);
-
-        float pointX = mXCenter + (int) (mLineLength[index] * Math.sin(selectionRadians));
-        float pointY = mYCenter - (int) (mLineLength[index] * Math.cos(selectionRadians));
-
-        int color;
-        int alpha;
-        Paint paint;
-
-        // Draw the selection circle
-        color = mColorSelector[index % 2][SELECTOR_CIRCLE];
-        alpha = (int) (mAlphaSelector[index % 2][SELECTOR_CIRCLE].getValue() * alphaMod + 0.5f);
-        paint = mPaintSelector[index % 2][SELECTOR_CIRCLE];
+        // Draw the selection circle.
+        final Paint paint = mPaintSelector[index % 2][SELECTOR_CIRCLE];
         paint.setColor(color);
-        paint.setAlpha(getMultipliedAlpha(color, alpha));
-        canvas.drawCircle(pointX, pointY, mSelectorRadius, paint);
+        canvas.drawCircle(selCenterX, selCenterY, selRadius, paint);
 
         // If needed, set up the clip path for later.
         if (selectorPath != null) {
-            mSelectorPath.reset();
-            mSelectorPath.addCircle(pointX, pointY, mSelectorRadius, Path.Direction.CCW);
+            selectorPath.reset();
+            selectorPath.addCircle(selCenterX, selCenterY, selRadius, Path.Direction.CCW);
         }
 
-        // Draw the dot if needed.
+        // Draw the dot if we're between two items.
         final boolean shouldDrawDot = mSelectionDegrees[index] % 30 != 0;
         if (shouldDrawDot) {
-            // We're not on a direct tick
-            color = mColorSelector[index % 2][SELECTOR_DOT];
-            alpha = (int) (mAlphaSelector[index % 2][SELECTOR_DOT].getValue() * alphaMod + 0.5f);
-            paint = mPaintSelector[index % 2][SELECTOR_DOT];
-            paint.setColor(color);
-            paint.setAlpha(getMultipliedAlpha(color, alpha));
-            canvas.drawCircle(pointX, pointY, mSelectorDotRadius, paint);
+            final Paint dotPaint = mPaintSelector[index % 2][SELECTOR_DOT];
+            dotPaint.setColor(color);
+            canvas.drawCircle(selCenterX, selCenterY, mSelectorDotRadius, dotPaint);
         }
 
         // Shorten the line to only go from the edge of the center dot to the
         // edge of the selection circle.
-        final double sin = Math.sin(selectionRadians);
-        final double cos = Math.cos(selectionRadians);
-        final int lineLength = mLineLength[index] - mSelectorRadius;
+        final double sin = Math.sin(selAngleRad);
+        final double cos = Math.cos(selAngleRad);
+        final int lineLength = selLength - selRadius;
         final int centerX = mXCenter + (int) (mCenterDotRadius * sin);
         final int centerY = mYCenter - (int) (mCenterDotRadius * cos);
-        pointX = centerX + (int) (lineLength * sin);
-        pointY = centerY - (int) (lineLength * cos);
+        final float linePointX = centerX + (int) (lineLength * sin);
+        final float linePointY = centerY - (int) (lineLength * cos);
 
-        // Draw the line
-        color = mColorSelector[index % 2][SELECTOR_LINE];
-        alpha = (int) (mAlphaSelector[index % 2][SELECTOR_LINE].getValue() * alphaMod + 0.5f);
-        paint = mPaintSelector[index % 2][SELECTOR_LINE];
-        paint.setColor(color);
-        paint.setStrokeWidth(mSelectorStroke);
-        paint.setAlpha(getMultipliedAlpha(color, alpha));
-        canvas.drawLine(mXCenter, mYCenter, pointX, pointY, paint);
+        // Draw the line.
+        final Paint linePaint = mPaintSelector[index % 2][SELECTOR_LINE];
+        linePaint.setColor(color);
+        linePaint.setStrokeWidth(mSelectorStroke);
+        canvas.drawLine(mXCenter, mYCenter, linePointX, linePointY, linePaint);
     }
 
     private void calculatePositionsHours() {
@@ -890,21 +869,8 @@
         if (mHoursToMinutesAnims.size() == 0) {
             mHoursToMinutesAnims.add(getFadeOutAnimator(mAlpha[HOURS],
                     ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-            mHoursToMinutesAnims.add(getFadeOutAnimator(mAlphaSelector[HOURS][SELECTOR_CIRCLE],
-                    ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-            mHoursToMinutesAnims.add(getFadeOutAnimator(mAlphaSelector[HOURS][SELECTOR_DOT],
-                    ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-            mHoursToMinutesAnims.add(getFadeOutAnimator(mAlphaSelector[HOURS][SELECTOR_LINE],
-                    ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-
             mHoursToMinutesAnims.add(getFadeInAnimator(mAlpha[MINUTES],
                     ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
-            mHoursToMinutesAnims.add(getFadeInAnimator(mAlphaSelector[MINUTES][SELECTOR_CIRCLE],
-                    ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
-            mHoursToMinutesAnims.add(getFadeInAnimator(mAlphaSelector[MINUTES][SELECTOR_DOT],
-                    ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
-            mHoursToMinutesAnims.add(getFadeInAnimator(mAlphaSelector[MINUTES][SELECTOR_LINE],
-                    ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
         }
 
         if (mTransition != null && mTransition.isRunning()) {
@@ -919,21 +885,8 @@
         if (mMinuteToHoursAnims.size() == 0) {
             mMinuteToHoursAnims.add(getFadeOutAnimator(mAlpha[MINUTES],
                     ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-            mMinuteToHoursAnims.add(getFadeOutAnimator(mAlphaSelector[MINUTES][SELECTOR_CIRCLE],
-                    ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-            mMinuteToHoursAnims.add(getFadeOutAnimator(mAlphaSelector[MINUTES][SELECTOR_DOT],
-                    ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-            mMinuteToHoursAnims.add(getFadeOutAnimator(mAlphaSelector[MINUTES][SELECTOR_LINE],
-                    ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-
             mMinuteToHoursAnims.add(getFadeInAnimator(mAlpha[HOURS],
                     ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
-            mMinuteToHoursAnims.add(getFadeInAnimator(mAlphaSelector[HOURS][SELECTOR_CIRCLE],
-                    ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
-            mMinuteToHoursAnims.add(getFadeInAnimator(mAlphaSelector[HOURS][SELECTOR_DOT],
-                    ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
-            mMinuteToHoursAnims.add(getFadeInAnimator(mAlphaSelector[HOURS][SELECTOR_LINE],
-                    ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
         }
 
         if (mTransition != null && mTransition.isRunning()) {
@@ -1144,30 +1097,31 @@
 
         private void adjustPicker(int step) {
             final int stepSize;
-            final int initialValue;
+            final int initialStep;
             final int maxValue;
             final int minValue;
             if (mShowHours) {
-                stepSize = DEGREES_FOR_ONE_HOUR;
-                initialValue = getCurrentHour() % 12;
+                stepSize = 1;
 
+                final int currentHour24 = getCurrentHour();
                 if (mIs24HourMode) {
-                    maxValue = 23;
+                    initialStep = currentHour24;
                     minValue = 0;
+                    maxValue = 23;
                 } else {
-                    maxValue = 12;
+                    initialStep = hour24To12(currentHour24);
                     minValue = 1;
+                    maxValue = 12;
                 }
             } else {
-                stepSize = DEGREES_FOR_ONE_MINUTE;
-                initialValue = getCurrentMinute();
-
-                maxValue = 55;
+                stepSize = 5;
+                initialStep = getCurrentMinute() / stepSize;
                 minValue = 0;
+                maxValue = 55;
             }
 
-            final int steppedValue = snapOnly30s(initialValue * stepSize, step) / stepSize;
-            final int clampedValue = MathUtils.constrain(steppedValue, minValue, maxValue);
+            final int nextValue = (initialStep + step) * stepSize;
+            final int clampedValue = MathUtils.constrain(nextValue, minValue, maxValue);
             if (mShowHours) {
                 setCurrentHour(clampedValue);
             } else {
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 349f3f0..a50941b 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -815,12 +815,12 @@
         mContext = context;
         mIntent = intent;
 
-        mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
-
-        mLayoutInflater = LayoutInflater.from(context);
         if (mIntent == null) {
             throw new IllegalArgumentException("Non-null Intent must be specified.");
         }
+
+        mAppWidgetId = intent.getIntExtra(RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID, -1);
+        mLayoutInflater = LayoutInflater.from(context);
         mRequestedViews = new RemoteViewsFrameLayoutRefSet();
 
         // Strip the previously injected app widget id from service intent
diff --git a/core/java/android/widget/SimpleMonthAdapter.java b/core/java/android/widget/SimpleMonthAdapter.java
deleted file mode 100644
index c807d56..0000000
--- a/core/java/android/widget/SimpleMonthAdapter.java
+++ /dev/null
@@ -1,220 +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 android.widget;
-
-import com.android.internal.R;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.SimpleMonthView.OnDayClickListener;
-
-import java.util.Calendar;
-
-/**
- * An adapter for a list of {@link android.widget.SimpleMonthView} items.
- */
-class SimpleMonthAdapter extends BaseAdapter {
-    private final Calendar mMinDate = Calendar.getInstance();
-    private final Calendar mMaxDate = Calendar.getInstance();
-
-    private final Context mContext;
-
-    private Calendar mSelectedDay = Calendar.getInstance();
-    private ColorStateList mCalendarTextColors = ColorStateList.valueOf(Color.BLACK);
-    private ColorStateList mCalendarDayBackgroundColor = ColorStateList.valueOf(Color.MAGENTA);
-    private OnDaySelectedListener mOnDaySelectedListener;
-
-    private int mFirstDayOfWeek;
-
-    public SimpleMonthAdapter(Context context) {
-        mContext = context;
-    }
-
-    public void setRange(Calendar min, Calendar max) {
-        mMinDate.setTimeInMillis(min.getTimeInMillis());
-        mMaxDate.setTimeInMillis(max.getTimeInMillis());
-
-        notifyDataSetInvalidated();
-    }
-
-    public void setFirstDayOfWeek(int firstDayOfWeek) {
-        mFirstDayOfWeek = firstDayOfWeek;
-
-        notifyDataSetInvalidated();
-    }
-
-    public int getFirstDayOfWeek() {
-        return mFirstDayOfWeek;
-    }
-
-    /**
-     * Updates the selected day and related parameters.
-     *
-     * @param day The day to highlight
-     */
-    public void setSelectedDay(Calendar day) {
-        mSelectedDay = day;
-
-        notifyDataSetChanged();
-    }
-
-    /**
-     * Sets the listener to call when the user selects a day.
-     *
-     * @param listener The listener to call.
-     */
-    public void setOnDaySelectedListener(OnDaySelectedListener listener) {
-        mOnDaySelectedListener = listener;
-    }
-
-    void setCalendarTextColor(ColorStateList colors) {
-        mCalendarTextColors = colors;
-    }
-
-    void setCalendarDayBackgroundColor(ColorStateList dayBackgroundColor) {
-        mCalendarDayBackgroundColor = dayBackgroundColor;
-    }
-
-    /**
-     * Sets the text color, size, style, hint color, and highlight color from
-     * the specified TextAppearance resource. This is mostly copied from
-     * {@link TextView#setTextAppearance(Context, int)}.
-     */
-    void setCalendarTextAppearance(int resId) {
-        final TypedArray a = mContext.obtainStyledAttributes(resId, R.styleable.TextAppearance);
-
-        final ColorStateList textColor = a.getColorStateList(R.styleable.TextAppearance_textColor);
-        if (textColor != null) {
-            mCalendarTextColors = textColor;
-        }
-
-        // TODO: Support font size, etc.
-
-        a.recycle();
-    }
-
-    @Override
-    public int getCount() {
-        final int diffYear = mMaxDate.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR);
-        final int diffMonth = mMaxDate.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH);
-        return diffMonth + 12 * diffYear + 1;
-    }
-
-    @Override
-    public Object getItem(int position) {
-        return null;
-    }
-
-    @Override
-    public long getItemId(int position) {
-        return position;
-    }
-
-    @Override
-    public boolean hasStableIds() {
-        return true;
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-        final SimpleMonthView v;
-        if (convertView != null) {
-            v = (SimpleMonthView) convertView;
-        } else {
-            v = new SimpleMonthView(mContext);
-
-            // Set up the new view
-            final AbsListView.LayoutParams params = new AbsListView.LayoutParams(
-                    AbsListView.LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.MATCH_PARENT);
-            v.setLayoutParams(params);
-            v.setClickable(true);
-            v.setOnDayClickListener(mOnDayClickListener);
-
-            v.setMonthTextColor(mCalendarTextColors);
-            v.setDayOfWeekTextColor(mCalendarTextColors);
-            v.setDayTextColor(mCalendarTextColors);
-
-            v.setDayBackgroundColor(mCalendarDayBackgroundColor);
-        }
-
-        final int minMonth = mMinDate.get(Calendar.MONTH);
-        final int minYear = mMinDate.get(Calendar.YEAR);
-        final int currentMonth = position + minMonth;
-        final int month = currentMonth % 12;
-        final int year = currentMonth / 12 + minYear;
-        final int selectedDay;
-        if (isSelectedDayInMonth(year, month)) {
-            selectedDay = mSelectedDay.get(Calendar.DAY_OF_MONTH);
-        } else {
-            selectedDay = -1;
-        }
-
-        // Invokes requestLayout() to ensure that the recycled view is set with the appropriate
-        // height/number of weeks before being displayed.
-        v.reuse();
-
-        final int enabledDayRangeStart;
-        if (minMonth == month && minYear == year) {
-            enabledDayRangeStart = mMinDate.get(Calendar.DAY_OF_MONTH);
-        } else {
-            enabledDayRangeStart = 1;
-        }
-
-        final int enabledDayRangeEnd;
-        if (mMaxDate.get(Calendar.MONTH) == month && mMaxDate.get(Calendar.YEAR) == year) {
-            enabledDayRangeEnd = mMaxDate.get(Calendar.DAY_OF_MONTH);
-        } else {
-            enabledDayRangeEnd = 31;
-        }
-
-        v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek,
-                enabledDayRangeStart, enabledDayRangeEnd);
-        v.invalidate();
-
-        return v;
-    }
-
-    private boolean isSelectedDayInMonth(int year, int month) {
-        return mSelectedDay.get(Calendar.YEAR) == year && mSelectedDay.get(Calendar.MONTH) == month;
-    }
-
-    private boolean isCalendarInRange(Calendar value) {
-        return value.compareTo(mMinDate) >= 0 && value.compareTo(mMaxDate) <= 0;
-    }
-
-    private final OnDayClickListener mOnDayClickListener = new OnDayClickListener() {
-        @Override
-        public void onDayClick(SimpleMonthView view, Calendar day) {
-            if (day != null && isCalendarInRange(day)) {
-                setSelectedDay(day);
-
-                if (mOnDaySelectedListener != null) {
-                    mOnDaySelectedListener.onDaySelected(SimpleMonthAdapter.this, day);
-                }
-            }
-        }
-    };
-
-    public interface OnDaySelectedListener {
-        public void onDaySelected(SimpleMonthAdapter view, Calendar day);
-    }
-}
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 58ad515..d9f1f0e 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -18,19 +18,18 @@
 
 import android.content.Context;
 import android.content.res.ColorStateList;
-import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Paint.Align;
 import android.graphics.Paint.Style;
 import android.graphics.Rect;
 import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.text.TextPaint;
 import android.text.format.DateFormat;
-import android.text.format.DateUtils;
-import android.text.format.Time;
 import android.util.AttributeSet;
 import android.util.IntArray;
 import android.util.StateSet;
@@ -38,13 +37,13 @@
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
 import com.android.internal.R;
 import com.android.internal.widget.ExploreByTouchHelper;
 
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
-import java.util.Formatter;
 import java.util.Locale;
 
 /**
@@ -52,93 +51,105 @@
  * within the specified month.
  */
 class SimpleMonthView extends View {
-    private static final int MIN_ROW_HEIGHT = 10;
+    private static final int DAYS_IN_WEEK = 7;
+    private static final int MAX_WEEKS_IN_MONTH = 6;
 
     private static final int DEFAULT_SELECTED_DAY = -1;
     private static final int DEFAULT_WEEK_START = Calendar.SUNDAY;
-    private static final int DEFAULT_NUM_DAYS = 7;
-    private static final int DEFAULT_NUM_ROWS = 6;
-    private static final int MAX_NUM_ROWS = 6;
 
-    private final Formatter mFormatter;
-    private final StringBuilder mStringBuilder;
+    private static final String DEFAULT_TITLE_FORMAT = "MMMMy";
+    private static final String DAY_OF_WEEK_FORMAT = "EEEEE";
 
-    private final int mMonthTextSize;
-    private final int mDayOfWeekTextSize;
-    private final int mDayTextSize;
+    /** Virtual view ID for previous button. */
+    private static final int ITEM_ID_PREV = 0x101;
 
-    /** Height of the header containing the month and day of week labels. */
-    private final int mMonthHeaderHeight;
+    /** Virtual view ID for next button. */
+    private static final int ITEM_ID_NEXT = 0x100;
 
     private final TextPaint mMonthPaint = new TextPaint();
     private final TextPaint mDayOfWeekPaint = new TextPaint();
     private final TextPaint mDayPaint = new TextPaint();
+    private final Paint mDaySelectorPaint = new Paint();
+    private final Paint mDayHighlightPaint = new Paint();
 
-    private final Paint mDayBackgroundPaint = new Paint();
+    private final Calendar mCalendar = Calendar.getInstance();
+    private final Calendar mDayOfWeekLabelCalendar = Calendar.getInstance();
 
-    /** Single-letter (when available) formatter for the day of week label. */
-    private SimpleDateFormat mDayFormatter = new SimpleDateFormat("EEEEE", Locale.getDefault());
+    private final MonthViewTouchHelper mTouchHelper;
 
-    // affects the padding on the sides of this view
-    private int mPadding = 0;
+    private final SimpleDateFormat mTitleFormatter;
+    private final SimpleDateFormat mDayOfWeekFormatter;
 
-    private String mDayOfWeekTypeface;
-    private String mMonthTypeface;
+    // Desired dimensions.
+    private final int mDesiredMonthHeight;
+    private final int mDesiredDayOfWeekHeight;
+    private final int mDesiredDayHeight;
+    private final int mDesiredCellWidth;
+    private final int mDesiredDaySelectorRadius;
+
+    // Next/previous drawables.
+    private final Drawable mPrevDrawable;
+    private final Drawable mNextDrawable;
+    private final Rect mPrevHitArea;
+    private final Rect mNextHitArea;
+    private final CharSequence mPrevContentDesc;
+    private final CharSequence mNextContentDesc;
+
+    private CharSequence mTitle;
 
     private int mMonth;
     private int mYear;
 
-    // Quick reference to the width of this view, matches parent
-    private int mWidth;
+    // Dimensions as laid out.
+    private int mMonthHeight;
+    private int mDayOfWeekHeight;
+    private int mDayHeight;
+    private int mCellWidth;
+    private int mDaySelectorRadius;
 
-    // The height this view should draw at in pixels, set by height param
-    private final int mRowHeight;
+    private int mPaddedWidth;
+    private int mPaddedHeight;
 
-    // If this view contains the today
-    private boolean mHasToday = false;
-
-    // Which day is selected [0-6] or -1 if no day is selected
+    /** The day of month for the selected day, or -1 if no day is selected. */
     private int mActivatedDay = -1;
 
-    // Which day is today [0-6] or -1 if no day is today
+    /**
+     * The day of month for today, or -1 if the today is not in the current
+     * month.
+     */
     private int mToday = DEFAULT_SELECTED_DAY;
 
-    // Which day of the week to start on [0-6]
+    /** The first day of the week (ex. Calendar.SUNDAY). */
     private int mWeekStart = DEFAULT_WEEK_START;
 
-    // How many days to display
-    private int mNumDays = DEFAULT_NUM_DAYS;
+    /** The number of days (ex. 28) in the current month. */
+    private int mDaysInMonth;
 
-    // The number of days + a spot for week number if it is displayed
-    private int mNumCells = mNumDays;
+    /**
+     * The day of week (ex. Calendar.SUNDAY) for the first day of the current
+     * month.
+     */
+    private int mDayOfWeekStart;
 
-    private int mDayOfWeekStart = 0;
-
-    // First enabled day
+    /** The day of month for the first (inclusive) enabled day. */
     private int mEnabledDayStart = 1;
 
-    // Last enabled day
+    /** The day of month for the last (inclusive) enabled day. */
     private int mEnabledDayEnd = 31;
 
-    private final Calendar mCalendar = Calendar.getInstance();
-    private final Calendar mDayLabelCalendar = Calendar.getInstance();
+    /** The number of week rows needed to display the current month. */
+    private int mNumWeeks = MAX_WEEKS_IN_MONTH;
 
-    private final MonthViewTouchHelper mTouchHelper;
-
-    private int mNumRows = DEFAULT_NUM_ROWS;
-
-    // Optional listener for handling day click actions
+    /** Optional listener for handling day click actions. */
     private OnDayClickListener mOnDayClickListener;
 
-    // Whether to prevent setting the accessibility delegate
-    private boolean mLockAccessibilityDelegate;
-
-    private int mNormalTextColor;
-    private int mDisabledTextColor;
-    private int mSelectedDayColor;
-
     private ColorStateList mDayTextColor;
 
+    private int mTouchedItem = -1;
+
+    private boolean mPrevEnabled;
+    private boolean mNextEnabled;
+
     public SimpleMonthView(Context context) {
         this(context, null);
     }
@@ -155,64 +166,151 @@
         super(context, attrs, defStyleAttr, defStyleRes);
 
         final Resources res = context.getResources();
-        mDayOfWeekTypeface = res.getString(R.string.day_of_week_label_typeface);
-        mMonthTypeface = res.getString(R.string.sans_serif);
+        mDesiredMonthHeight = res.getDimensionPixelSize(R.dimen.date_picker_month_height);
+        mDesiredDayOfWeekHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_of_week_height);
+        mDesiredDayHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_height);
+        mDesiredCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width);
+        mDesiredDaySelectorRadius = res.getDimensionPixelSize(R.dimen.date_picker_day_selector_radius);
 
-        mStringBuilder = new StringBuilder(50);
-        mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
-
-        mDayTextSize = res.getDimensionPixelSize(R.dimen.datepicker_day_number_size);
-        mMonthTextSize = res.getDimensionPixelSize(R.dimen.datepicker_month_label_size);
-        mDayOfWeekTextSize = res.getDimensionPixelSize(
-                R.dimen.datepicker_month_day_label_text_size);
-        mMonthHeaderHeight = res.getDimensionPixelOffset(
-                R.dimen.datepicker_month_list_item_header_height);
-
-        mRowHeight = Math.max(MIN_ROW_HEIGHT,
-                (res.getDimensionPixelOffset(R.dimen.datepicker_view_animator_height)
-                        - mMonthHeaderHeight) / MAX_NUM_ROWS);
+        mPrevDrawable = context.getDrawable(R.drawable.ic_chevron_left);
+        mNextDrawable = context.getDrawable(R.drawable.ic_chevron_right);
+        mPrevHitArea = mPrevDrawable != null ? new Rect() : null;
+        mNextHitArea = mNextDrawable != null ? new Rect() : null;
+        mPrevContentDesc = res.getText(R.string.date_picker_prev_month_button);
+        mNextContentDesc = res.getText(R.string.date_picker_next_month_button);
 
         // Set up accessibility components.
         mTouchHelper = new MonthViewTouchHelper(this);
         setAccessibilityDelegate(mTouchHelper);
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
-        mLockAccessibilityDelegate = true;
 
-        initPaints();
+        final Locale locale = res.getConfiguration().locale;
+        final String titleFormat = DateFormat.getBestDateTimePattern(locale, DEFAULT_TITLE_FORMAT);
+        mTitleFormatter = new SimpleDateFormat(titleFormat, locale);
+        mDayOfWeekFormatter = new SimpleDateFormat(DAY_OF_WEEK_FORMAT, locale);
+
+        setClickable(true);
+        initPaints(res);
+    }
+
+    public void setNextEnabled(boolean enabled) {
+        mNextEnabled = enabled;
+        mTouchHelper.invalidateRoot();
+        invalidate();
+    }
+
+    public void setPrevEnabled(boolean enabled) {
+        mPrevEnabled = enabled;
+        mTouchHelper.invalidateRoot();
+        invalidate();
+    }
+
+    /**
+     * Applies the specified text appearance resource to a paint, returning the
+     * text color if one is set in the text appearance.
+     *
+     * @param p the paint to modify
+     * @param resId the resource ID of the text appearance
+     * @return the text color, if available
+     */
+    private ColorStateList applyTextAppearance(Paint p, int resId) {
+        final TypedArray ta = mContext.obtainStyledAttributes(null,
+                R.styleable.TextAppearance, 0, resId);
+
+        final String fontFamily = ta.getString(R.styleable.TextAppearance_fontFamily);
+        if (fontFamily != null) {
+            p.setTypeface(Typeface.create(fontFamily, 0));
+        }
+
+        p.setTextSize(ta.getDimensionPixelSize(
+                R.styleable.TextAppearance_textSize, (int) p.getTextSize()));
+
+        final ColorStateList textColor = ta.getColorStateList(R.styleable.TextAppearance_textColor);
+        if (textColor != null) {
+            final int enabledColor = textColor.getColorForState(ENABLED_STATE_SET, 0);
+            p.setColor(enabledColor);
+        }
+
+        ta.recycle();
+
+        return textColor;
+    }
+
+    public void setMonthTextAppearance(int resId) {
+        final ColorStateList monthColor = applyTextAppearance(mMonthPaint, resId);
+        if (monthColor != null) {
+            if (mPrevDrawable != null) {
+                mPrevDrawable.setTintList(monthColor);
+            }
+            if (mNextDrawable != null) {
+                mNextDrawable.setTintList(monthColor);
+            }
+        }
+
+        invalidate();
+    }
+
+    public void setDayOfWeekTextAppearance(int resId) {
+        applyTextAppearance(mDayOfWeekPaint, resId);
+        invalidate();
+    }
+
+    public void setDayTextAppearance(int resId) {
+        final ColorStateList textColor = applyTextAppearance(mDayPaint, resId);
+        if (textColor != null) {
+            mDayTextColor = textColor;
+        }
+
+        invalidate();
+    }
+
+    public CharSequence getTitle() {
+        if (mTitle == null) {
+            mTitle = mTitleFormatter.format(mCalendar.getTime());
+        }
+        return mTitle;
     }
 
     /**
      * Sets up the text and style properties for painting.
      */
-    private void initPaints() {
+    private void initPaints(Resources res) {
+        final String monthTypeface = res.getString(R.string.date_picker_month_typeface);
+        final String dayOfWeekTypeface = res.getString(R.string.date_picker_day_of_week_typeface);
+        final String dayTypeface = res.getString(R.string.date_picker_day_typeface);
+
+        final int monthTextSize = res.getDimensionPixelSize(
+                R.dimen.date_picker_month_text_size);
+        final int dayOfWeekTextSize = res.getDimensionPixelSize(
+                R.dimen.date_picker_day_of_week_text_size);
+        final int dayTextSize = res.getDimensionPixelSize(
+                R.dimen.date_picker_day_text_size);
+
         mMonthPaint.setAntiAlias(true);
-        mMonthPaint.setTextSize(mMonthTextSize);
-        mMonthPaint.setTypeface(Typeface.create(mMonthTypeface, Typeface.BOLD));
+        mMonthPaint.setTextSize(monthTextSize);
+        mMonthPaint.setTypeface(Typeface.create(monthTypeface, 0));
         mMonthPaint.setTextAlign(Align.CENTER);
         mMonthPaint.setStyle(Style.FILL);
 
         mDayOfWeekPaint.setAntiAlias(true);
-        mDayOfWeekPaint.setTextSize(mDayOfWeekTextSize);
-        mDayOfWeekPaint.setTypeface(Typeface.create(mDayOfWeekTypeface, Typeface.BOLD));
+        mDayOfWeekPaint.setTextSize(dayOfWeekTextSize);
+        mDayOfWeekPaint.setTypeface(Typeface.create(dayOfWeekTypeface, 0));
         mDayOfWeekPaint.setTextAlign(Align.CENTER);
         mDayOfWeekPaint.setStyle(Style.FILL);
 
-        mDayBackgroundPaint.setAntiAlias(true);
-        mDayBackgroundPaint.setStyle(Style.FILL);
+        mDaySelectorPaint.setAntiAlias(true);
+        mDaySelectorPaint.setStyle(Style.FILL);
+
+        mDayHighlightPaint.setAntiAlias(true);
+        mDayHighlightPaint.setStyle(Style.FILL);
 
         mDayPaint.setAntiAlias(true);
-        mDayPaint.setTextSize(mDayTextSize);
+        mDayPaint.setTextSize(dayTextSize);
+        mDayPaint.setTypeface(Typeface.create(dayTypeface, 0));
         mDayPaint.setTextAlign(Align.CENTER);
         mDayPaint.setStyle(Style.FILL);
     }
 
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
-
-        mDayFormatter = new SimpleDateFormat("EEEEE", newConfig.locale);
-    }
-
     void setMonthTextColor(ColorStateList monthTextColor) {
         final int enabledColor = monthTextColor.getColorForState(ENABLED_STATE_SET, 0);
         mMonthPaint.setColor(enabledColor);
@@ -230,20 +328,18 @@
         invalidate();
     }
 
-    void setDayBackgroundColor(ColorStateList dayBackgroundColor) {
+    void setDaySelectorColor(ColorStateList dayBackgroundColor) {
         final int activatedColor = dayBackgroundColor.getColorForState(
                 StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0);
-        mDayBackgroundPaint.setColor(activatedColor);
+        mDaySelectorPaint.setColor(activatedColor);
         invalidate();
     }
 
-    @Override
-    public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
-        // Workaround for a JB MR1 issue where accessibility delegates on
-        // top-level ListView items are overwritten.
-        if (!mLockAccessibilityDelegate) {
-            super.setAccessibilityDelegate(delegate);
-        }
+    void setDayHighlightColor(ColorStateList dayHighlightColor) {
+        final int pressedColor = dayHighlightColor.getColorForState(
+                StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_PRESSED), 0);
+        mDayHighlightPaint.setColor(pressedColor);
+        invalidate();
     }
 
     public void setOnDayClickListener(OnDayClickListener listener) {
@@ -253,30 +349,147 @@
     @Override
     public boolean dispatchHoverEvent(MotionEvent event) {
         // First right-of-refusal goes the touch exploration helper.
-        if (mTouchHelper.dispatchHoverEvent(event)) {
-            return true;
-        }
-        return super.dispatchHoverEvent(event);
+        return mTouchHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event);
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
+        final int x = (int) (event.getX() + 0.5f);
+        final int y = (int) (event.getY() + 0.5f);
+
         switch (event.getAction()) {
-            case MotionEvent.ACTION_UP:
-                final int day = getDayFromLocation(event.getX(), event.getY());
-                if (day >= 0) {
-                    onDayClick(day);
+            case MotionEvent.ACTION_DOWN:
+            case MotionEvent.ACTION_MOVE:
+                final int touchedItem = getItemAtLocation(x, y);
+                if (mTouchedItem != touchedItem) {
+                    mTouchedItem = touchedItem;
+                    invalidate();
                 }
                 break;
+
+            case MotionEvent.ACTION_UP:
+                final int clickedItem = getItemAtLocation(x, y);
+                onItemClicked(clickedItem, true);
+                // Fall through.
+            case MotionEvent.ACTION_CANCEL:
+                // Reset touched day on stream end.
+                mTouchedItem = -1;
+                invalidate();
+                break;
         }
         return true;
     }
 
     @Override
     protected void onDraw(Canvas canvas) {
-        drawMonthTitle(canvas);
-        drawWeekDayLabels(canvas);
+        final int paddingLeft = getPaddingLeft();
+        final int paddingTop = getPaddingTop();
+        canvas.translate(paddingLeft, paddingTop);
+
+        drawMonth(canvas);
+        drawDaysOfWeek(canvas);
         drawDays(canvas);
+        drawButtons(canvas);
+
+        canvas.translate(-paddingLeft, -paddingTop);
+    }
+
+    private void drawMonth(Canvas canvas) {
+        final float x = mPaddedWidth / 2f;
+
+        // Vertically centered within the month header height.
+        final float lineHeight = mMonthPaint.ascent() + mMonthPaint.descent();
+        final float y = (mMonthHeight - lineHeight) / 2f;
+
+        canvas.drawText(getTitle().toString(), x, y, mMonthPaint);
+    }
+
+    private void drawDaysOfWeek(Canvas canvas) {
+        final TextPaint p = mDayOfWeekPaint;
+        final int headerHeight = mMonthHeight;
+        final int rowHeight = mDayOfWeekHeight;
+        final int colWidth = mCellWidth;
+
+        // Text is vertically centered within the day of week height.
+        final float halfLineHeight = (p.ascent() + p.descent()) / 2f;
+        final int rowCenter = headerHeight + rowHeight / 2;
+
+        for (int col = 0; col < DAYS_IN_WEEK; col++) {
+            final int colCenter = colWidth * col + colWidth / 2;
+            final int dayOfWeek = (col + mWeekStart) % DAYS_IN_WEEK;
+            final String label = getDayOfWeekLabel(dayOfWeek);
+            canvas.drawText(label, colCenter, rowCenter - halfLineHeight, p);
+        }
+    }
+
+    private String getDayOfWeekLabel(int dayOfWeek) {
+        mDayOfWeekLabelCalendar.set(Calendar.DAY_OF_WEEK, dayOfWeek);
+        return mDayOfWeekFormatter.format(mDayOfWeekLabelCalendar.getTime());
+    }
+
+    /**
+     * Draws the month days.
+     */
+    private void drawDays(Canvas canvas) {
+        final TextPaint p = mDayPaint;
+        final int headerHeight = mMonthHeight + mDayOfWeekHeight;
+        final int rowHeight = mDayHeight;
+        final int colWidth = mCellWidth;
+
+        // Text is vertically centered within the row height.
+        final float halfLineHeight = (p.ascent() + p.descent()) / 2f;
+        int rowCenter = headerHeight + rowHeight / 2;
+
+        for (int day = 1, col = findDayOffset(); day <= mDaysInMonth; day++) {
+            final int colCenter = colWidth * col + colWidth / 2;
+            int stateMask = 0;
+
+            if (day >= mEnabledDayStart && day <= mEnabledDayEnd) {
+                stateMask |= StateSet.VIEW_STATE_ENABLED;
+            }
+
+            final boolean isDayActivated = mActivatedDay == day;
+            if (isDayActivated) {
+                stateMask |= StateSet.VIEW_STATE_ACTIVATED;
+
+                // Adjust the circle to be centered on the row.
+                canvas.drawCircle(colCenter, rowCenter, mDaySelectorRadius, mDaySelectorPaint);
+            } else if (mTouchedItem == day) {
+                stateMask |= StateSet.VIEW_STATE_PRESSED;
+
+                // Adjust the circle to be centered on the row.
+                canvas.drawCircle(colCenter, rowCenter, mDaySelectorRadius, mDayHighlightPaint);
+            }
+
+            final boolean isDayToday = mToday == day;
+            final int dayTextColor;
+            if (isDayToday && !isDayActivated) {
+                dayTextColor = mDaySelectorPaint.getColor();
+            } else {
+                final int[] stateSet = StateSet.get(stateMask);
+                dayTextColor = mDayTextColor.getColorForState(stateSet, 0);
+            }
+            p.setColor(dayTextColor);
+
+            canvas.drawText(Integer.toString(day), colCenter, rowCenter - halfLineHeight, p);
+
+            col++;
+
+            if (col == DAYS_IN_WEEK) {
+                col = 0;
+                rowCenter += rowHeight;
+            }
+        }
+    }
+
+    private void drawButtons(Canvas canvas) {
+        if (mPrevEnabled && mPrevDrawable != null) {
+            mPrevDrawable.draw(canvas);
+        }
+
+        if (mNextEnabled && mNextDrawable != null) {
+            mNextDrawable.draw(canvas);
+        }
     }
 
     private static boolean isValidDayOfWeek(int day) {
@@ -288,18 +501,52 @@
     }
 
     /**
-     * Sets all the parameters for displaying this week. Parameters have a default value and
-     * will only update if a new value is included, except for focus month, which will always
-     * default to no focus month if no value is passed in. The only required parameter is the
-     * week start.
+     * Sets the selected day.
      *
-     * @param selectedDay the selected day of the month, or -1 for no selection.
-     * @param month the month.
-     * @param year the year.
-     * @param weekStart which day the week should start on. {@link Calendar#SUNDAY} through
-     *        {@link Calendar#SATURDAY}.
-     * @param enabledDayStart the first enabled day.
-     * @param enabledDayEnd the last enabled day.
+     * @param dayOfMonth the selected day of the month, or {@code -1} to clear
+     *                   the selection
+     */
+    public void setSelectedDay(int dayOfMonth) {
+        mActivatedDay = dayOfMonth;
+
+        // Invalidate cached accessibility information.
+        mTouchHelper.invalidateRoot();
+        invalidate();
+    }
+
+    /**
+     * Sets the first day of the week.
+     *
+     * @param weekStart which day the week should start on, valid values are
+     *                  {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY}
+     */
+    public void setFirstDayOfWeek(int weekStart) {
+        if (isValidDayOfWeek(weekStart)) {
+            mWeekStart = weekStart;
+        } else {
+            mWeekStart = mCalendar.getFirstDayOfWeek();
+        }
+
+        // Invalidate cached accessibility information.
+        mTouchHelper.invalidateRoot();
+        invalidate();
+    }
+
+    /**
+     * Sets all the parameters for displaying this week.
+     * <p>
+     * Parameters have a default value and will only update if a new value is
+     * included, except for focus month, which will always default to no focus
+     * month if no value is passed in. The only required parameter is the week
+     * start.
+     *
+     * @param selectedDay the selected day of the month, or -1 for no selection
+     * @param month the month
+     * @param year the year
+     * @param weekStart which day the week should start on, valid values are
+     *                  {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY}
+     * @param enabledDayStart the first enabled day
+     * @param enabledDayEnd the last enabled day
      */
     void setMonthParams(int selectedDay, int month, int year, int weekStart, int enabledDayStart,
             int enabledDayEnd) {
@@ -310,12 +557,6 @@
         }
         mYear = year;
 
-        // Figure out what day today is
-        final Time today = new Time(Time.getCurrentTimezone());
-        today.setToNow();
-        mHasToday = false;
-        mToday = -1;
-
         mCalendar.set(Calendar.MONTH, mMonth);
         mCalendar.set(Calendar.YEAR, mYear);
         mCalendar.set(Calendar.DAY_OF_MONTH, 1);
@@ -334,15 +575,20 @@
             mEnabledDayEnd = enabledDayEnd;
         }
 
-        mNumCells = getDaysInMonth(mMonth, mYear);
-        for (int i = 0; i < mNumCells; i++) {
+        // Figure out what day today is.
+        final Calendar today = Calendar.getInstance();
+        mToday = -1;
+        mDaysInMonth = getDaysInMonth(mMonth, mYear);
+        for (int i = 0; i < mDaysInMonth; i++) {
             final int day = i + 1;
             if (sameDay(day, today)) {
-                mHasToday = true;
                 mToday = day;
             }
         }
-        mNumRows = calculateNumRows();
+        mNumWeeks = calculateNumRows();
+
+        // Invalidate the old title.
+        mTitle = null;
 
         // Invalidate cached accessibility information.
         mTouchHelper.invalidateRoot();
@@ -371,199 +617,246 @@
     }
 
     public void reuse() {
-        mNumRows = DEFAULT_NUM_ROWS;
+        mNumWeeks = MAX_WEEKS_IN_MONTH;
         requestLayout();
     }
 
     private int calculateNumRows() {
-        int offset = findDayOffset();
-        int dividend = (offset + mNumCells) / mNumDays;
-        int remainder = (offset + mNumCells) % mNumDays;
-        return (dividend + (remainder > 0 ? 1 : 0));
+        final int offset = findDayOffset();
+        final int dividend = (offset + mDaysInMonth) / DAYS_IN_WEEK;
+        final int remainder = (offset + mDaysInMonth) % DAYS_IN_WEEK;
+        return dividend + (remainder > 0 ? 1 : 0);
     }
 
-    private boolean sameDay(int day, Time today) {
-        return mYear == today.year &&
-                mMonth == today.month &&
-                day == today.monthDay;
+    private boolean sameDay(int day, Calendar today) {
+        return mYear == today.get(Calendar.YEAR) && mMonth == today.get(Calendar.MONTH)
+                && day == today.get(Calendar.DAY_OF_MONTH);
     }
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mRowHeight * mNumRows
-                + mMonthHeaderHeight);
+        final int preferredHeight = mDesiredDayHeight * mNumWeeks + mDesiredDayOfWeekHeight
+                + mDesiredMonthHeight + getPaddingTop() + getPaddingBottom();
+        final int preferredWidth = mDesiredCellWidth * DAYS_IN_WEEK
+                + getPaddingStart() + getPaddingEnd();
+        final int resolvedWidth = resolveSize(preferredWidth, widthMeasureSpec);
+        final int resolvedHeight = resolveSize(preferredHeight, heightMeasureSpec);
+        setMeasuredDimension(resolvedWidth, resolvedHeight);
     }
 
     @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        mWidth = w;
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        if (!changed) {
+            return;
+        }
+
+        // Let's initialize a completely reasonable number of variables.
+        final int w = right - left;
+        final int h = bottom - top;
+        final int paddingLeft = getPaddingLeft();
+        final int paddingTop = getPaddingTop();
+        final int paddingRight = getPaddingRight();
+        final int paddingBottom = getPaddingBottom();
+        final int paddedRight = w - paddingRight;
+        final int paddedBottom = h - paddingBottom;
+        final int paddedWidth = paddedRight - paddingLeft;
+        final int paddedHeight = paddedBottom - paddingTop;
+        if (paddedWidth == mPaddedWidth || paddedHeight == mPaddedHeight) {
+            return;
+        }
+
+        mPaddedWidth = paddedWidth;
+        mPaddedHeight = paddedHeight;
+
+        // We may have been laid out smaller than our preferred size. If so,
+        // scale all dimensions to fit.
+        final int measuredPaddedHeight = getMeasuredHeight() - paddingTop - paddingBottom;
+        final float scaleH = paddedHeight / (float) measuredPaddedHeight;
+        final int monthHeight = (int) (mDesiredMonthHeight * scaleH);
+        final int cellWidth = mPaddedWidth / DAYS_IN_WEEK;
+        mMonthHeight = monthHeight;
+        mDayOfWeekHeight = (int) (mDesiredDayOfWeekHeight * scaleH);
+        mDayHeight = (int) (mDesiredDayHeight * scaleH);
+        mCellWidth = cellWidth;
+
+        // Compute the largest day selector radius that's still within the clip
+        // bounds and desired selector radius.
+        final int maxSelectorWidth = cellWidth / 2 + Math.min(paddingLeft, paddingRight);
+        final int maxSelectorHeight = mDayHeight / 2 + paddingBottom;
+        mDaySelectorRadius = Math.min(mDesiredDaySelectorRadius,
+                Math.min(maxSelectorWidth, maxSelectorHeight));
+
+        // Vertically center the previous/next drawables within the month
+        // header, horizontally center within the day cell, then expand the
+        // hit area to ensure it's at least 48x48dp.
+        final Drawable prevDrawable = mPrevDrawable;
+        if (prevDrawable != null) {
+            final int dW = prevDrawable.getIntrinsicWidth();
+            final int dH = prevDrawable.getIntrinsicHeight();
+            final int iconTop = (monthHeight - dH) / 2;
+            final int iconLeft = (cellWidth - dW) / 2;
+
+            // Button bounds don't include padding, but hit area does.
+            prevDrawable.setBounds(iconLeft, iconTop, iconLeft + dW, iconTop + dH);
+            mPrevHitArea.set(0, 0, paddingLeft + cellWidth, paddingTop + monthHeight);
+        }
+
+        final Drawable nextDrawable = mNextDrawable;
+        if (nextDrawable != null) {
+            final int dW = nextDrawable.getIntrinsicWidth();
+            final int dH = nextDrawable.getIntrinsicHeight();
+            final int iconTop = (monthHeight - dH) / 2;
+            final int iconRight = paddedWidth - (cellWidth - dW) / 2;
+
+            // Button bounds don't include padding, but hit area does.
+            nextDrawable.setBounds(iconRight - dW, iconTop, iconRight, iconTop + dH);
+            mNextHitArea.set(paddedRight - cellWidth, 0, w, paddingTop + monthHeight);
+        }
 
         // Invalidate cached accessibility information.
         mTouchHelper.invalidateRoot();
     }
 
-    private String getMonthAndYearString() {
-        int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR
-                | DateUtils.FORMAT_NO_MONTH_DAY;
-        mStringBuilder.setLength(0);
-        long millis = mCalendar.getTimeInMillis();
-        return DateUtils.formatDateRange(getContext(), mFormatter, millis, millis, flags,
-                Time.getCurrentTimezone()).toString();
-    }
-
-    private void drawMonthTitle(Canvas canvas) {
-        final float x = (mWidth + 2 * mPadding) / 2f;
-
-        // Centered on the upper half of the month header.
-        final float lineHeight = mMonthPaint.ascent() + mMonthPaint.descent();
-        final float y = mMonthHeaderHeight * 0.25f - lineHeight / 2f;
-
-        canvas.drawText(getMonthAndYearString(), x, y, mMonthPaint);
-    }
-
-    private void drawWeekDayLabels(Canvas canvas) {
-        final float dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2);
-
-        // Centered on the lower half of the month header.
-        final float lineHeight = mDayOfWeekPaint.ascent() + mDayOfWeekPaint.descent();
-        final float y = mMonthHeaderHeight * 0.75f - lineHeight / 2f;
-
-        for (int i = 0; i < mNumDays; i++) {
-            final int calendarDay = (i + mWeekStart) % mNumDays;
-            mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay);
-
-            final String dayLabel = mDayFormatter.format(mDayLabelCalendar.getTime());
-            final float x = (2 * i + 1) * dayWidthHalf + mPadding;
-            canvas.drawText(dayLabel, x, y, mDayOfWeekPaint);
-        }
-    }
-
-    /**
-     * Draws the month days.
-     */
-    private void drawDays(Canvas canvas) {
-        final int dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2);
-
-        // Centered within the row.
-        final float lineHeight = mDayOfWeekPaint.ascent() + mDayOfWeekPaint.descent();
-        float y = mMonthHeaderHeight + (mRowHeight - lineHeight) / 2f;
-
-        for (int day = 1, j = findDayOffset(); day <= mNumCells; day++) {
-            final int x = (2 * j + 1) * dayWidthHalf + mPadding;
-            int stateMask = 0;
-
-            if (day >= mEnabledDayStart && day <= mEnabledDayEnd) {
-                stateMask |= StateSet.VIEW_STATE_ENABLED;
-            }
-
-            if (mActivatedDay == day) {
-                stateMask |= StateSet.VIEW_STATE_ACTIVATED;
-
-                // Adjust the circle to be centered the row.
-                final float rowCenterY = y + lineHeight / 2;
-                canvas.drawCircle(x, rowCenterY, mRowHeight / 2,
-                        mDayBackgroundPaint);
-            }
-
-            final int[] stateSet = StateSet.get(stateMask);
-            final int dayTextColor = mDayTextColor.getColorForState(stateSet, 0);
-            mDayPaint.setColor(dayTextColor);
-
-            final boolean isDayToday = mHasToday && mToday == day;
-            mDayPaint.setFakeBoldText(isDayToday);
-
-            canvas.drawText(String.format("%d", day), x, y, mDayPaint);
-
-            j++;
-
-            if (j == mNumDays) {
-                j = 0;
-                y += mRowHeight;
-            }
-        }
-    }
-
     private int findDayOffset() {
-        return (mDayOfWeekStart < mWeekStart ? (mDayOfWeekStart + mNumDays) : mDayOfWeekStart)
-                - mWeekStart;
+        final int offset = mDayOfWeekStart - mWeekStart;
+        if (mDayOfWeekStart < mWeekStart) {
+            return offset + DAYS_IN_WEEK;
+        }
+        return offset;
     }
 
     /**
-     * Calculates the day that the given x position is in, accounting for week
-     * number. Returns the day or -1 if the position wasn't in a day.
+     * Calculates the day of the month or item identifier at the specified
+     * touch position. Returns the day of the month or -1 if the position
+     * wasn't in a valid day.
      *
-     * @param x The x position of the touch event
-     * @return The day number, or -1 if the position wasn't in a day
+     * @param x the x position of the touch event
+     * @param y the y position of the touch event
+     * @return the day of the month at (x, y), an item identifier, or -1 if the
+     *         position wasn't in a valid day or item
      */
-    private int getDayFromLocation(float x, float y) {
-        int dayStart = mPadding;
-        if (x < dayStart || x > mWidth - mPadding) {
-            return -1;
+    private int getItemAtLocation(int x, int y) {
+        if (mNextEnabled && mNextDrawable != null && mNextHitArea.contains(x, y)) {
+            return ITEM_ID_NEXT;
+        } else if (mPrevEnabled && mPrevDrawable != null && mPrevHitArea.contains(x, y)) {
+            return ITEM_ID_PREV;
         }
-        // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels
-        int row = (int) (y - mMonthHeaderHeight) / mRowHeight;
-        int column = (int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding));
 
-        int day = column - findDayOffset() + 1;
-        day += row * mNumDays;
-        if (day < 1 || day > mNumCells) {
+        final int paddedX = x - getPaddingLeft();
+        if (paddedX < 0 || paddedX >= mPaddedWidth) {
             return -1;
         }
+
+        final int headerHeight = mMonthHeight + mDayOfWeekHeight;
+        final int paddedY = y - getPaddingTop();
+        if (paddedY < headerHeight || paddedY >= mPaddedHeight) {
+            return -1;
+        }
+
+        final int row = (paddedY - headerHeight) / mDayHeight;
+        final int col = (paddedX * DAYS_IN_WEEK) / mPaddedWidth;
+        final int index = col + row * DAYS_IN_WEEK;
+        final int day = index + 1 - findDayOffset();
+        if (day < 1 || day > mDaysInMonth) {
+            return -1;
+        }
+
         return day;
     }
 
     /**
+     * Calculates the bounds of the specified day.
+     *
+     * @param id the day of the month, or an item identifier
+     * @param outBounds the rect to populate with bounds
+     */
+    private boolean getBoundsForItem(int id, Rect outBounds) {
+        if (mNextEnabled && id == ITEM_ID_NEXT) {
+            if (mNextDrawable != null) {
+                outBounds.set(mNextHitArea);
+                return true;
+            }
+        } else if (mPrevEnabled && id == ITEM_ID_PREV) {
+            if (mPrevDrawable != null) {
+                outBounds.set(mPrevHitArea);
+                return true;
+            }
+        }
+
+        if (id < 1 || id > mDaysInMonth) {
+            return false;
+        }
+
+        final int index = id - 1 + findDayOffset();
+
+        // Compute left edge.
+        final int col = index % DAYS_IN_WEEK;
+        final int colWidth = mCellWidth;
+        final int left = getPaddingLeft() + col * colWidth;
+
+        // Compute top edge.
+        final int row = index / DAYS_IN_WEEK;
+        final int rowHeight = mDayHeight;
+        final int headerHeight = mMonthHeight + mDayOfWeekHeight;
+        final int top = getPaddingTop() + headerHeight + row * rowHeight;
+
+        outBounds.set(left, top, left + colWidth, top + rowHeight);
+        return true;
+    }
+
+    /**
+     * Called when an item is clicked.
+     *
+     * @param id the day number or item identifier
+     */
+    private boolean onItemClicked(int id, boolean animate) {
+        return onNavigationClicked(id, animate) || onDayClicked(id);
+    }
+
+    /**
      * Called when the user clicks on a day. Handles callbacks to the
      * {@link OnDayClickListener} if one is set.
      *
-     * @param day The day that was clicked
+     * @param day the day that was clicked
      */
-    private void onDayClick(int day) {
+    private boolean onDayClicked(int day) {
+        if (day < 0 || day > mDaysInMonth) {
+            return false;
+        }
+
         if (mOnDayClickListener != null) {
-            Calendar date = Calendar.getInstance();
+            final Calendar date = Calendar.getInstance();
             date.set(mYear, mMonth, day);
             mOnDayClickListener.onDayClick(this, date);
         }
 
         // This is a no-op if accessibility is turned off.
         mTouchHelper.sendEventForVirtualView(day, AccessibilityEvent.TYPE_VIEW_CLICKED);
+        return true;
     }
 
     /**
-     * @return The date that has accessibility focus, or {@code null} if no date
-     *         has focus
-     */
-    Calendar getAccessibilityFocus() {
-        final int day = mTouchHelper.getFocusedVirtualView();
-        Calendar date = null;
-        if (day >= 0) {
-            date = Calendar.getInstance();
-            date.set(mYear, mMonth, day);
-        }
-        return date;
-    }
-
-    /**
-     * Clears accessibility focus within the view. No-op if the view does not
-     * contain accessibility focus.
-     */
-    public void clearAccessibilityFocus() {
-        mTouchHelper.clearFocusedVirtualView();
-    }
-
-    /**
-     * Attempts to restore accessibility focus to the specified date.
+     * Called when the user clicks on a navigation button. Handles callbacks to
+     * the {@link OnDayClickListener} if one is set.
      *
-     * @param day The date which should receive focus
-     * @return {@code false} if the date is not valid for this month view, or
-     *         {@code true} if the date received focus
+     * @param id the item identifier
      */
-    boolean restoreAccessibilityFocus(Calendar day) {
-        if ((day.get(Calendar.YEAR) != mYear) || (day.get(Calendar.MONTH) != mMonth) ||
-                (day.get(Calendar.DAY_OF_MONTH) > mNumCells)) {
+    private boolean onNavigationClicked(int id, boolean animate) {
+        final int direction;
+        if (id == ITEM_ID_NEXT) {
+            direction = 1;
+        } else if (id == ITEM_ID_PREV) {
+            direction = -1;
+        } else {
             return false;
         }
-        mTouchHelper.setFocusedVirtualView(day.get(Calendar.DAY_OF_MONTH));
+
+        if (mOnDayClickListener != null) {
+            mOnDayClickListener.onNavigationClick(this, direction, animate);
+        }
+
+        // This is a no-op if accessibility is turned off.
+        mTouchHelper.sendEventForVirtualView(id, AccessibilityEvent.TYPE_VIEW_CLICKED);
         return true;
     }
 
@@ -581,24 +874,9 @@
             super(host);
         }
 
-        public void setFocusedVirtualView(int virtualViewId) {
-            getAccessibilityNodeProvider(SimpleMonthView.this).performAction(
-                    virtualViewId, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
-        }
-
-        public void clearFocusedVirtualView() {
-            final int focusedVirtualView = getFocusedVirtualView();
-            if (focusedVirtualView != ExploreByTouchHelper.INVALID_ID) {
-                getAccessibilityNodeProvider(SimpleMonthView.this).performAction(
-                        focusedVirtualView,
-                        AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS,
-                        null);
-            }
-        }
-
         @Override
         protected int getVirtualViewAt(float x, float y) {
-            final int day = getDayFromLocation(x, y);
+            final int day = getItemAtLocation((int) (x + 0.5f), (int) (y + 0.5f));
             if (day >= 0) {
                 return day;
             }
@@ -607,7 +885,15 @@
 
         @Override
         protected void getVisibleVirtualViews(IntArray virtualViewIds) {
-            for (int day = 1; day <= mNumCells; day++) {
+            if (mNextEnabled && mNextDrawable != null) {
+                virtualViewIds.add(ITEM_ID_PREV);
+            }
+
+            if (mPrevEnabled && mPrevDrawable != null) {
+                virtualViewIds.add(ITEM_ID_NEXT);
+            }
+
+            for (int day = 1; day <= mDaysInMonth; day++) {
                 virtualViewIds.add(day);
             }
         }
@@ -619,14 +905,25 @@
 
         @Override
         protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) {
-            getItemBounds(virtualViewId, mTempRect);
+            final boolean hasBounds = getBoundsForItem(virtualViewId, mTempRect);
 
+            if (!hasBounds) {
+                // The day is invalid, kill the node.
+                mTempRect.setEmpty();
+                node.setContentDescription("");
+                node.setBoundsInParent(mTempRect);
+                node.setVisibleToUser(false);
+                return;
+            }
+
+            node.setText(getItemText(virtualViewId));
             node.setContentDescription(getItemDescription(virtualViewId));
             node.setBoundsInParent(mTempRect);
-            node.addAction(AccessibilityNodeInfo.ACTION_CLICK);
+            node.addAction(AccessibilityAction.ACTION_CLICK);
 
             if (virtualViewId == mActivatedDay) {
-                node.setSelected(true);
+                // TODO: This should use activated once that's supported.
+                node.setChecked(true);
             }
 
         }
@@ -636,51 +933,45 @@
                 Bundle arguments) {
             switch (action) {
                 case AccessibilityNodeInfo.ACTION_CLICK:
-                    onDayClick(virtualViewId);
-                    return true;
+                    return onItemClicked(virtualViewId, false);
             }
 
             return false;
         }
 
         /**
-         * Calculates the bounding rectangle of a given time object.
+         * Generates a description for a given virtual view.
          *
-         * @param day The day to calculate bounds for
-         * @param rect The rectangle in which to store the bounds
+         * @param id the day or item identifier to generate a description for
+         * @return a description of the virtual view
          */
-        private void getItemBounds(int day, Rect rect) {
-            final int offsetX = mPadding;
-            final int offsetY = mMonthHeaderHeight;
-            final int cellHeight = mRowHeight;
-            final int cellWidth = ((mWidth - (2 * mPadding)) / mNumDays);
-            final int index = ((day - 1) + findDayOffset());
-            final int row = (index / mNumDays);
-            final int column = (index % mNumDays);
-            final int x = (offsetX + (column * cellWidth));
-            final int y = (offsetY + (row * cellHeight));
+        private CharSequence getItemDescription(int id) {
+            if (id == ITEM_ID_NEXT) {
+                return mNextContentDesc;
+            } else if (id == ITEM_ID_PREV) {
+                return mPrevContentDesc;
+            } else if (id >= 1 && id <= mDaysInMonth) {
+                mTempCalendar.set(mYear, mMonth, id);
+                return DateFormat.format(DATE_FORMAT, mTempCalendar.getTimeInMillis());
+            }
 
-            rect.set(x, y, (x + cellWidth), (y + cellHeight));
+            return "";
         }
 
         /**
-         * Generates a description for a given time object. Since this
-         * description will be spoken, the components are ordered by descending
-         * specificity as DAY MONTH YEAR.
+         * Generates displayed text for a given virtual view.
          *
-         * @param day The day to generate a description for
-         * @return A description of the time object
+         * @param id the day or item identifier to generate text for
+         * @return the visible text of the virtual view
          */
-        private CharSequence getItemDescription(int day) {
-            mTempCalendar.set(mYear, mMonth, day);
-            final CharSequence date = DateFormat.format(DATE_FORMAT,
-                    mTempCalendar.getTimeInMillis());
-
-            if (day == mActivatedDay) {
-                return getContext().getString(R.string.item_is_selected, date);
+        private CharSequence getItemText(int id) {
+            if (id == ITEM_ID_NEXT || id == ITEM_ID_PREV) {
+                return null;
+            } else if (id >= 1 && id <= mDaysInMonth) {
+                return Integer.toString(id);
             }
 
-            return date;
+            return null;
         }
     }
 
@@ -689,5 +980,6 @@
      */
     public interface OnDayClickListener {
         public void onDayClick(SimpleMonthView view, Calendar day);
+        public void onNavigationClick(SimpleMonthView view, int direction, boolean animate);
     }
 }
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index bb290e7..ae779fe 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -1363,8 +1363,8 @@
     }
 
     @Override
-    public void onProvideAssistStructure(ViewAssistStructure structure, Bundle extras) {
-        super.onProvideAssistStructure(structure, extras);
+    public void onProvideAssistStructure(ViewAssistStructure structure) {
+        super.onProvideAssistStructure(structure);
         CharSequence switchText = isChecked() ? mTextOn : mTextOff;
         if (!TextUtils.isEmpty(switchText)) {
             CharSequence oldText = structure.getText();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 718ef93..9caa584 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -543,6 +543,8 @@
     private float mSpacingMult = 1.0f;
     private float mSpacingAdd = 0.0f;
 
+    private int mBreakStrategy;
+
     private int mMaximum = Integer.MAX_VALUE;
     private int mMaxMode = LINES;
     private int mMinimum = 0;
@@ -680,6 +682,7 @@
         boolean elegant = false;
         float letterSpacing = 0;
         String fontFeatureSettings = null;
+        mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
 
         final Resources.Theme theme = context.getTheme();
 
@@ -1133,6 +1136,9 @@
             case com.android.internal.R.styleable.TextView_fontFeatureSettings:
                 fontFeatureSettings = a.getString(attr);
                 break;
+
+            case com.android.internal.R.styleable.TextView_breakStrategy:
+                mBreakStrategy = a.getInt(attr, Layout.BREAK_STRATEGY_SIMPLE);
             }
         }
         a.recycle();
@@ -2960,6 +2966,35 @@
     }
 
     /**
+     * Sets the break strategy for breaking paragraphs into lines. The default value for
+     * TextView is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}, and the default value for
+     * EditText is {@link Layout#BREAK_STRATEGY_SIMPLE}, the latter to avoid the
+     * text "dancing" when being edited.
+     *
+     * @attr ref android.R.styleable#TextView_breakStrategy
+     * @see #getBreakStrategy()
+     */
+    public void setBreakStrategy(@Layout.BreakStrategy int breakStrategy) {
+        mBreakStrategy = breakStrategy;
+        if (mLayout != null) {
+            nullLayouts();
+            requestLayout();
+            invalidate();
+        }
+    }
+
+    /**
+     * @return the currently set break strategy.
+     *
+     * @attr ref android.R.styleable#TextView_breakStrategy
+     * @see #setBreakStrategy(int)
+     */
+    @Layout.BreakStrategy
+    public int getBreakStrategy() {
+        return mBreakStrategy;
+    }
+
+    /**
      * Sets font feature settings.  The format is the same as the CSS
      * font-feature-settings attribute:
      * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings
@@ -6492,27 +6527,25 @@
                                 hintBoring, mIncludePad, mEllipsize,
                                 ellipsisWidth);
                     }
-                } else if (shouldEllipsize) {
-                    mHintLayout = new StaticLayout(mHint,
-                                0, mHint.length(),
-                                mTextPaint, hintWidth, alignment, mTextDir, mSpacingMult,
-                                mSpacingAdd, mIncludePad, mEllipsize,
-                                ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
-                } else {
-                    mHintLayout = new StaticLayout(mHint, mTextPaint,
-                            hintWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd,
-                            mIncludePad);
                 }
-            } else if (shouldEllipsize) {
-                mHintLayout = new StaticLayout(mHint,
-                            0, mHint.length(),
-                            mTextPaint, hintWidth, alignment, mTextDir, mSpacingMult,
-                            mSpacingAdd, mIncludePad, mEllipsize,
-                            ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
-            } else {
-                mHintLayout = new StaticLayout(mHint, mTextPaint,
-                        hintWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd,
-                        mIncludePad);
+            }
+            // TODO: code duplication with makeSingleLayout()
+            if (mHintLayout == null) {
+                StaticLayout.Builder builder = StaticLayout.Builder.obtain(mHint, 0,
+                        mHint.length(), hintWidth)
+                        .setPaint(mTextPaint)
+                        .setAlignment(alignment)
+                        .setTextDir(mTextDir)
+                        .setSpacingMult(mSpacingMult)
+                        .setSpacingAdd(mSpacingAdd)
+                        .setIncludePad(mIncludePad)
+                        .setBreakStrategy(mBreakStrategy);
+                if (shouldEllipsize) {
+                    builder.setEllipsize(mEllipsize)
+                            .setEllipsizedWidth(ellipsisWidth)
+                            .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
+                }
+                mHintLayout = builder.build();
             }
         }
 
@@ -6544,9 +6577,8 @@
         Layout result = null;
         if (mText instanceof Spannable) {
             result = new DynamicLayout(mText, mTransformed, mTextPaint, wantWidth,
-                    alignment, mTextDir, mSpacingMult,
-                    mSpacingAdd, mIncludePad, getKeyListener() == null ? effectiveEllipsize : null,
-                            ellipsisWidth);
+                    alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad, mBreakStrategy,
+                    getKeyListener() == null ? effectiveEllipsize : null, ellipsisWidth);
         } else {
             if (boring == UNKNOWN_BORING) {
                 boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
@@ -6583,29 +6615,27 @@
                                 boring, mIncludePad, effectiveEllipsize,
                                 ellipsisWidth);
                     }
-                } else if (shouldEllipsize) {
-                    result = new StaticLayout(mTransformed,
-                            0, mTransformed.length(),
-                            mTextPaint, wantWidth, alignment, mTextDir, mSpacingMult,
-                            mSpacingAdd, mIncludePad, effectiveEllipsize,
-                            ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
-                } else {
-                    result = new StaticLayout(mTransformed, mTextPaint,
-                            wantWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd,
-                            mIncludePad);
                 }
-            } else if (shouldEllipsize) {
-                result = new StaticLayout(mTransformed,
-                        0, mTransformed.length(),
-                        mTextPaint, wantWidth, alignment, mTextDir, mSpacingMult,
-                        mSpacingAdd, mIncludePad, effectiveEllipsize,
-                        ellipsisWidth, mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
-            } else {
-                result = new StaticLayout(mTransformed, mTextPaint,
-                        wantWidth, alignment, mTextDir, mSpacingMult, mSpacingAdd,
-                        mIncludePad);
             }
         }
+        if (result == null) {
+            StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed,
+                    0, mTransformed.length(), wantWidth)
+                    .setPaint(mTextPaint)
+                    .setAlignment(alignment)
+                    .setTextDir(mTextDir)
+                    .setSpacingMult(mSpacingMult)
+                    .setSpacingAdd(mSpacingAdd)
+                    .setIncludePad(mIncludePad)
+                    .setBreakStrategy(mBreakStrategy);
+            if (shouldEllipsize) {
+                builder.setEllipsize(effectiveEllipsize)
+                        .setEllipsizedWidth(ellipsisWidth)
+                        .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE);
+            }
+            // TODO: explore always setting maxLines
+            result = builder.build();
+        }
         return result;
     }
 
@@ -8576,8 +8606,8 @@
     }
 
     @Override
-    public void onProvideAssistStructure(ViewAssistStructure structure, Bundle extras) {
-        super.onProvideAssistStructure(structure, extras);
+    public void onProvideAssistStructure(ViewAssistStructure structure) {
+        super.onProvideAssistStructure(structure);
         final boolean isPassword = hasPasswordTransformationMethod();
         if (!isPassword) {
             structure.setText(getText(), getSelectionStart(), getSelectionEnd());
diff --git a/core/java/android/widget/TextViewWithCircularIndicator.java b/core/java/android/widget/TextViewWithCircularIndicator.java
deleted file mode 100644
index d3c786c..0000000
--- a/core/java/android/widget/TextViewWithCircularIndicator.java
+++ /dev/null
@@ -1,87 +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 android.widget;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Typeface;
-import android.util.AttributeSet;
-
-import com.android.internal.R;
-
-class TextViewWithCircularIndicator extends TextView {
-    private final Paint mCirclePaint = new Paint();
-    private final String mItemIsSelectedText;
-
-    public TextViewWithCircularIndicator(Context context) {
-        this(context, null);
-    }
-
-    public TextViewWithCircularIndicator(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public TextViewWithCircularIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public TextViewWithCircularIndicator(Context context, AttributeSet attrs,
-            int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-
-        final Resources res = context.getResources();
-        mItemIsSelectedText = res.getString(R.string.item_is_selected);
-
-        init();
-    }
-
-    private void init() {
-        mCirclePaint.setTypeface(Typeface.create(mCirclePaint.getTypeface(), Typeface.BOLD));
-        mCirclePaint.setAntiAlias(true);
-        mCirclePaint.setTextAlign(Paint.Align.CENTER);
-        mCirclePaint.setStyle(Paint.Style.FILL);
-    }
-
-    public void setCircleColor(int color) {
-        mCirclePaint.setColor(color);
-        invalidate();
-    }
-
-    @Override
-    public void onDraw(Canvas canvas) {
-        if (isActivated()) {
-            final int width = getWidth();
-            final int height = getHeight();
-            final int radius = Math.min(width, height) / 2;
-            canvas.drawCircle(width / 2, height / 2, radius, mCirclePaint);
-        }
-
-        super.onDraw(canvas);
-    }
-
-    @Override
-    public CharSequence getContentDescription() {
-        final CharSequence itemText = getText();
-        if (isActivated()) {
-            return String.format(mItemIsSelectedText, itemText);
-        } else {
-            return itemText;
-        }
-    }
-}
\ No newline at end of file
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 944b491..986c0f8 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -16,6 +16,7 @@
 
 package android.widget;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Widget;
 import android.content.Context;
@@ -29,18 +30,13 @@
 import java.util.Locale;
 
 /**
- * A view for selecting the time of day, in either 24 hour or AM/PM mode. The
- * hour, each minute digit, and AM/PM (if applicable) can be conrolled by
- * vertical spinners. The hour can be entered by keyboard input. Entering in two
- * digit hours can be accomplished by hitting two digits within a timeout of
- * about a second (e.g. '1' then '2' to select 12). The minutes can be entered
- * by entering single digits. Under AM/PM mode, the user can hit 'a', 'A", 'p'
- * or 'P' to pick. For a dialog using this view, see
- * {@link android.app.TimePickerDialog}.
+ * A widget for selecting the time of day, in either 24-hour or AM/PM mode.
  * <p>
- * See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
- * guide.
- * </p>
+ * For a dialog using this view, see {@link android.app.TimePickerDialog}. See
+ * the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
+ * guide for more information.
+ *
+ * @attr ref android.R.styleable#TimePicker_timePickerMode
  */
 @Widget
 public class TimePicker extends FrameLayout {
@@ -96,44 +92,105 @@
     }
 
     /**
-     * Set the current hour.
+     * Sets the currently selected hour using 24-hour time.
+     *
+     * @param hour the hour to set, in the range (0-23)
+     * @see #getHour()
      */
-    public void setCurrentHour(Integer currentHour) {
-        mDelegate.setCurrentHour(currentHour);
+    public void setHour(int hour) {
+        mDelegate.setCurrentHour(hour);
     }
 
     /**
-     * @return The current hour in the range (0-23).
+     * Returns the currently selected hour using 24-hour time.
+     *
+     * @return the currently selected hour, in the range (0-23)
+     * @see #setHour(int)
      */
+    public int getHour() {
+        return mDelegate.getCurrentHour();
+    }
+
+    /**
+     * Sets the currently selected minute..
+     *
+     * @param minute the minute to set, in the range (0-59)
+     * @see #getMinute()
+     */
+    public void setMinute(int minute) {
+        mDelegate.setCurrentMinute(minute);
+    }
+
+    /**
+     * Returns the currently selected minute.
+     *
+     * @return the currently selected minute, in the range (0-59)
+     * @see #setMinute(int)
+     */
+    public int getMinute() {
+        return mDelegate.getCurrentMinute();
+    }
+
+    /**
+     * Sets the current hour.
+     *
+     * @deprecated Use {@link #setHour(int)}
+     */
+    @Deprecated
+    public void setCurrentHour(@NonNull Integer currentHour) {
+        setHour(currentHour);
+    }
+
+    /**
+     * @return the current hour in the range (0-23)
+     * @deprecated Use {@link #getHour()}
+     */
+    @NonNull
+    @Deprecated
     public Integer getCurrentHour() {
         return mDelegate.getCurrentHour();
     }
 
     /**
      * Set the current minute (0-59).
+     *
+     * @deprecated Use {@link #setMinute(int)}
      */
-    public void setCurrentMinute(Integer currentMinute) {
+    @Deprecated
+    public void setCurrentMinute(@NonNull Integer currentMinute) {
         mDelegate.setCurrentMinute(currentMinute);
     }
 
     /**
-     * @return The current minute.
+     * @return the current minute
+     * @deprecated Use {@link #getMinute()}
      */
+    @NonNull
+    @Deprecated
     public Integer getCurrentMinute() {
         return mDelegate.getCurrentMinute();
     }
 
     /**
-     * Set whether in 24 hour or AM/PM mode.
+     * Sets whether this widget displays time in 24-hour mode or 12-hour mode
+     * with an AM/PM picker.
      *
-     * @param is24HourView True = 24 hour mode. False = AM/PM.
+     * @param is24HourView {@code true} to display in 24-hour mode,
+     *                     {@code false} for 12-hour mode with AM/PM
+     * @see #is24HourView()
      */
-    public void setIs24HourView(Boolean is24HourView) {
+    public void setIs24HourView(@NonNull Boolean is24HourView) {
+        if (is24HourView == null) {
+            return;
+        }
+
         mDelegate.setIs24HourView(is24HourView);
     }
 
     /**
-     * @return true if this is in 24 hour view else false.
+     * @return {@code true} if this widget displays time in 24-hour mode,
+     *         {@code false} otherwise}
+     * @see #setIs24HourView(Boolean)
      */
     public boolean is24HourView() {
         return mDelegate.is24HourView();
@@ -210,13 +267,13 @@
      * for the real behavior.
      */
     interface TimePickerDelegate {
-        void setCurrentHour(Integer currentHour);
-        Integer getCurrentHour();
+        void setCurrentHour(int currentHour);
+        int getCurrentHour();
 
-        void setCurrentMinute(Integer currentMinute);
-        Integer getCurrentMinute();
+        void setCurrentMinute(int currentMinute);
+        int getCurrentMinute();
 
-        void setIs24HourView(Boolean is24HourView);
+        void setIs24HourView(boolean is24HourView);
         boolean is24HourView();
 
         void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener);
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 9fdd718..2365b48 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -16,16 +16,22 @@
 
 package android.widget;
 
+import android.annotation.Nullable;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.SpannableStringBuilder;
 import android.text.format.DateFormat;
 import android.text.format.DateUtils;
+import android.text.style.TtsSpan;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.StateSet;
 import android.util.TypedValue;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyCharacterMap;
@@ -48,7 +54,6 @@
  */
 class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate implements
         RadialTimePickerView.OnValueSelectedListener {
-
     private static final String TAG = "TimePickerClockDelegate";
 
     // Index used by RadialPickerLayout
@@ -61,14 +66,16 @@
     // Also NOT a real index, just used for keyboard mode.
     private static final int ENABLE_PICKER_INDEX = 3;
 
+    private static final int[] ATTRS_TEXT_COLOR = new int[] {
+            com.android.internal.R.attr.textColor};
+    private static final int[] ATTRS_DISABLED_ALPHA = new int[] {
+            com.android.internal.R.attr.disabledAlpha};
+
     // LayoutLib relies on these constants. Change TimePickerClockDelegate_Delegate if
     // modifying these.
     static final int AM = 0;
     static final int PM = 1;
 
-    private static final boolean DEFAULT_ENABLED_STATE = true;
-    private boolean mIsEnabled = DEFAULT_ENABLED_STATE;
-
     private static final int HOURS_IN_HALF_DAY = 12;
 
     private final View mHeaderView;
@@ -83,8 +90,7 @@
     private final String mAmText;
     private final String mPmText;
 
-    private final float mDisabledAlpha;
-
+    private boolean mIsEnabled = true;
     private boolean mAllowAutoAdvance;
     private int mInitialHourOfDay;
     private int mInitialMinute;
@@ -134,7 +140,6 @@
         final View mainView = inflater.inflate(layoutResourceId, delegator);
 
         mHeaderView = mainView.findViewById(R.id.time_header);
-        mHeaderView.setBackground(a.getDrawable(R.styleable.TimePicker_headerBackground));
 
         // Set up hour/minute labels.
         mHourView = (TextView) mainView.findViewById(R.id.hours);
@@ -147,42 +152,58 @@
         mMinuteView.setAccessibilityDelegate(
                 new ClickActionDelegate(context, R.string.select_minutes));
 
-        final int headerTimeTextAppearance = a.getResourceId(
-                R.styleable.TimePicker_headerTimeTextAppearance, 0);
-        if (headerTimeTextAppearance != 0) {
-            mHourView.setTextAppearance(context, headerTimeTextAppearance);
-            mSeparatorView.setTextAppearance(context, headerTimeTextAppearance);
-            mMinuteView.setTextAppearance(context, headerTimeTextAppearance);
-        }
-
         // Now that we have text appearances out of the way, make sure the hour
         // and minute views are correctly sized.
         mHourView.setMinWidth(computeStableWidth(mHourView, 24));
         mMinuteView.setMinWidth(computeStableWidth(mMinuteView, 60));
 
+        final SpannableStringBuilder amLabel = new SpannableStringBuilder()
+                .append(amPmStrings[0], new TtsSpan.VerbatimBuilder(amPmStrings[0]).build(), 0);
+
         // Set up AM/PM labels.
         mAmPmLayout = mainView.findViewById(R.id.ampm_layout);
         mAmLabel = (CheckedTextView) mAmPmLayout.findViewById(R.id.am_label);
-        mAmLabel.setText(amPmStrings[0]);
+        mAmLabel.setText(obtainVerbatim(amPmStrings[0]));
         mAmLabel.setOnClickListener(mClickListener);
         mPmLabel = (CheckedTextView) mAmPmLayout.findViewById(R.id.pm_label);
-        mPmLabel.setText(amPmStrings[1]);
+        mPmLabel.setText(obtainVerbatim(amPmStrings[1]));
         mPmLabel.setOnClickListener(mClickListener);
 
-        final int headerAmPmTextAppearance = a.getResourceId(
-                R.styleable.TimePicker_headerAmPmTextAppearance, 0);
-        if (headerAmPmTextAppearance != 0) {
-            mAmLabel.setTextAppearance(context, headerAmPmTextAppearance);
-            mPmLabel.setTextAppearance(context, headerAmPmTextAppearance);
+        // For the sake of backwards compatibility, attempt to extract the text
+        // color from the header time text appearance. If it's set, we'll let
+        // that override the "real" header text color.
+        ColorStateList headerTextColor = null;
+
+        @SuppressWarnings("deprecation")
+        final int timeHeaderTextAppearance = a.getResourceId(
+                R.styleable.TimePicker_headerTimeTextAppearance, 0);
+        if (timeHeaderTextAppearance != 0) {
+            final TypedArray textAppearance = mContext.obtainStyledAttributes(null,
+                    ATTRS_TEXT_COLOR, 0, timeHeaderTextAppearance);
+            final ColorStateList legacyHeaderTextColor = textAppearance.getColorStateList(0);
+            headerTextColor = applyLegacyColorFixes(legacyHeaderTextColor);
+            textAppearance.recycle();
+        }
+
+        if (headerTextColor == null) {
+            headerTextColor = a.getColorStateList(R.styleable.TimePicker_headerTextColor);
+        }
+
+        if (headerTextColor != null) {
+            mHourView.setTextColor(headerTextColor);
+            mSeparatorView.setTextColor(headerTextColor);
+            mMinuteView.setTextColor(headerTextColor);
+            mAmLabel.setTextColor(headerTextColor);
+            mPmLabel.setTextColor(headerTextColor);
+        }
+
+        // Set up header background, if available.
+        if (a.hasValueOrEmpty(R.styleable.TimePicker_headerBackground)) {
+            mHeaderView.setBackground(a.getDrawable(R.styleable.TimePicker_headerBackground));
         }
 
         a.recycle();
 
-        // Pull disabled alpha from theme.
-        final TypedValue outValue = new TypedValue();
-        context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true);
-        mDisabledAlpha = outValue.getFloat();
-
         mRadialTimePickerView = (RadialTimePickerView) mainView.findViewById(
                 R.id.radial_picker);
 
@@ -204,6 +225,59 @@
         initialize(currentHour, currentMinute, false /* 12h */, HOUR_INDEX);
     }
 
+    private static final CharSequence obtainVerbatim(String text) {
+        return new SpannableStringBuilder().append(text,
+                new TtsSpan.VerbatimBuilder(text).build(), 0);
+    }
+
+    /**
+     * The legacy text color might have been poorly defined. Ensures that it
+     * has an appropriate activated state, using the selected state if one
+     * exists or modifying the default text color otherwise.
+     *
+     * @param color a legacy text color, or {@code null}
+     * @return a color state list with an appropriate activated state, or
+     *         {@code null} if a valid activated state could not be generated
+     */
+    @Nullable
+    private ColorStateList applyLegacyColorFixes(@Nullable ColorStateList color) {
+        if (color == null || color.hasState(R.attr.state_activated)) {
+            return color;
+        }
+
+        final int activatedColor;
+        final int defaultColor;
+        if (color.hasState(R.attr.state_selected)) {
+            activatedColor = color.getColorForState(StateSet.get(
+                    StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_SELECTED), 0);
+            defaultColor = color.getColorForState(StateSet.get(
+                    StateSet.VIEW_STATE_ENABLED), 0);
+        } else {
+            activatedColor = color.getDefaultColor();
+
+            // Generate a non-activated color using the disabled alpha.
+            final TypedArray ta = mContext.obtainStyledAttributes(ATTRS_DISABLED_ALPHA);
+            final float disabledAlpha = ta.getFloat(0, 0.30f);
+            defaultColor = multiplyAlphaComponent(activatedColor, disabledAlpha);
+        }
+
+        if (activatedColor == 0 || defaultColor == 0) {
+            // We somehow failed to obtain the colors.
+            return null;
+        }
+
+        final int[][] stateSet = new int[][] {{ R.attr.state_activated }, {}};
+        final int[] colors = new int[] { activatedColor, defaultColor };
+        return new ColorStateList(stateSet, colors);
+    }
+
+    private int multiplyAlphaComponent(int color, float alphaMod) {
+        final int srcRgb = color & 0xFFFFFF;
+        final int srcAlpha = (color >> 24) & 0xFF;
+        final int dstAlpha = (int) (srcAlpha * alphaMod + 0.5f);
+        return srcRgb | (dstAlpha << 24);
+    }
+
     private static class ClickActionDelegate extends AccessibilityDelegate {
         private final AccessibilityAction mClickAction;
 
@@ -312,7 +386,7 @@
      * Set the current hour.
      */
     @Override
-    public void setCurrentHour(Integer currentHour) {
+    public void setCurrentHour(int currentHour) {
         if (mInitialHourOfDay == currentHour) {
             return;
         }
@@ -329,7 +403,7 @@
      * @return The current hour in the range (0-23).
      */
     @Override
-    public Integer getCurrentHour() {
+    public int getCurrentHour() {
         int currentHour = mRadialTimePickerView.getCurrentHour();
         if (mIs24HourView) {
             return currentHour;
@@ -348,7 +422,7 @@
      * Set the current minute (0-59).
      */
     @Override
-    public void setCurrentMinute(Integer currentMinute) {
+    public void setCurrentMinute(int currentMinute) {
         if (mInitialMinute == currentMinute) {
             return;
         }
@@ -363,7 +437,7 @@
      * @return The current minute.
      */
     @Override
-    public Integer getCurrentMinute() {
+    public int getCurrentMinute() {
         return mRadialTimePickerView.getCurrentMinute();
     }
 
@@ -373,7 +447,7 @@
      * @param is24HourView True = 24 hour mode. False = AM/PM.
      */
     @Override
-    public void setIs24HourView(Boolean is24HourView) {
+    public void setIs24HourView(boolean is24HourView) {
         if (is24HourView == mIs24HourView) {
             return;
         }
@@ -604,12 +678,12 @@
 
     private void updateAmPmLabelStates(int amOrPm) {
         final boolean isAm = amOrPm == AM;
+        mAmLabel.setActivated(isAm);
         mAmLabel.setChecked(isAm);
-        mAmLabel.setSelected(isAm);
 
         final boolean isPm = amOrPm == PM;
+        mPmLabel.setActivated(isPm);
         mPmLabel.setChecked(isPm);
-        mPmLabel.setSelected(isPm);
     }
 
     /**
@@ -769,8 +843,8 @@
             }
         }
 
-        mHourView.setSelected(index == HOUR_INDEX);
-        mMinuteView.setSelected(index == MINUTE_INDEX);
+        mHourView.setActivated(index == HOUR_INDEX);
+        mMinuteView.setActivated(index == MINUTE_INDEX);
     }
 
     private void setAmOrPm(int amOrPm) {
@@ -960,9 +1034,9 @@
             String minuteStr = (values[1] == -1) ? mDoublePlaceholderText :
                     String.format(minuteFormat, values[1]).replace(' ', mPlaceholderText);
             mHourView.setText(hourStr);
-            mHourView.setSelected(false);
+            mHourView.setActivated(false);
             mMinuteView.setText(minuteStr);
-            mMinuteView.setSelected(false);
+            mMinuteView.setActivated(false);
             if (!mIs24HourView) {
                 updateAmPmLabelStates(values[2]);
             }
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index 513c55b..df6b0a9 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -279,13 +279,13 @@
     }
 
     @Override
-    public void setCurrentHour(Integer currentHour) {
+    public void setCurrentHour(int currentHour) {
         setCurrentHour(currentHour, true);
     }
 
-    private void setCurrentHour(Integer currentHour, boolean notifyTimeChanged) {
+    private void setCurrentHour(int currentHour, boolean notifyTimeChanged) {
         // why was Integer used in the first place?
-        if (currentHour == null || currentHour == getCurrentHour()) {
+        if (currentHour == getCurrentHour()) {
             return;
         }
         if (!is24HourView()) {
@@ -310,7 +310,7 @@
     }
 
     @Override
-    public Integer getCurrentHour() {
+    public int getCurrentHour() {
         int currentHour = mHourSpinner.getValue();
         if (is24HourView()) {
             return currentHour;
@@ -322,7 +322,7 @@
     }
 
     @Override
-    public void setCurrentMinute(Integer currentMinute) {
+    public void setCurrentMinute(int currentMinute) {
         if (currentMinute == getCurrentMinute()) {
             return;
         }
@@ -331,12 +331,12 @@
     }
 
     @Override
-    public Integer getCurrentMinute() {
+    public int getCurrentMinute() {
         return mMinuteSpinner.getValue();
     }
 
     @Override
-    public void setIs24HourView(Boolean is24HourView) {
+    public void setIs24HourView(boolean is24HourView) {
         if (mIs24HourView == is24HourView) {
             return;
         }
diff --git a/core/java/android/widget/YearPickerView.java b/core/java/android/widget/YearPickerView.java
index 6f0465f..7182414 100644
--- a/core/java/android/widget/YearPickerView.java
+++ b/core/java/android/widget/YearPickerView.java
@@ -17,10 +17,9 @@
 package android.widget;
 
 import android.content.Context;
-import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.util.AttributeSet;
-import android.util.StateSet;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
@@ -32,23 +31,14 @@
 /**
  * Displays a selectable list of years.
  */
-class YearPickerView extends ListView implements AdapterView.OnItemClickListener,
-        OnDateChangedListener {
-    private final Calendar mMinDate = Calendar.getInstance();
-    private final Calendar mMaxDate = Calendar.getInstance();
-
+class YearPickerView extends ListView {
     private final YearAdapter mAdapter;
     private final int mViewSize;
     private final int mChildSize;
 
-    private DatePickerController mController;
+    private OnYearSelectedListener mOnYearSelectedListener;
 
-    private int mSelectedPosition = -1;
-    private int mYearActivatedColor;
-
-    public YearPickerView(Context context) {
-        this(context, null);
-    }
+    private long mCurrentTimeMillis;
 
     public YearPickerView(Context context, AttributeSet attrs) {
         this(context, attrs, R.attr.listViewStyle);
@@ -69,104 +59,170 @@
         mViewSize = res.getDimensionPixelOffset(R.dimen.datepicker_view_animator_height);
         mChildSize = res.getDimensionPixelOffset(R.dimen.datepicker_year_label_height);
 
-        setVerticalFadingEdgeEnabled(true);
-        setFadingEdgeLength(mChildSize / 3);
+        setOnItemClickListener(new OnItemClickListener() {
+            @Override
+            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+                final int year = mAdapter.getYearForPosition(position);
+                mAdapter.setSelection(year);
 
-        final int paddingTop = res.getDimensionPixelSize(
-                R.dimen.datepicker_year_picker_padding_top);
-        setPadding(0, paddingTop, 0, 0);
+                if (mOnYearSelectedListener != null) {
+                    mOnYearSelectedListener.onYearChanged(YearPickerView.this, year);
+                }
+            }
+        });
 
-        setOnItemClickListener(this);
-        setDividerHeight(0);
-
-        mAdapter = new YearAdapter(getContext(), R.layout.year_label_text_view);
+        mAdapter = new YearAdapter(getContext());
         setAdapter(mAdapter);
     }
 
+    public void setOnYearSelectedListener(OnYearSelectedListener listener) {
+        mOnYearSelectedListener = listener;
+    }
+
+    public void setDate(long currentTimeMillis) {
+        mCurrentTimeMillis = currentTimeMillis;
+    }
+
+    /**
+     * Sets the currently selected year. Jumps immediately to the new year.
+     *
+     * @param year the target year
+     */
+    public void setYear(final int year) {
+        mAdapter.setSelection(year);
+
+        post(new Runnable() {
+            @Override
+            public void run() {
+                final int position = mAdapter.getPositionForYear(year);
+                if (position >= 0 && position < getCount()) {
+                    setSelectionCentered(position);
+                }
+            }
+        });
+    }
+
+    public void setSelectionCentered(int position) {
+        final int offset = mViewSize / 2 - mChildSize / 2;
+        setSelectionFromTop(position, offset);
+    }
+
     public void setRange(Calendar min, Calendar max) {
-        mMinDate.setTimeInMillis(min.getTimeInMillis());
-        mMaxDate.setTimeInMillis(max.getTimeInMillis());
-
-        updateAdapterData();
+        mAdapter.setRange(min, max);
     }
 
-    public void init(DatePickerController controller) {
-        mController = controller;
-        mController.registerOnDateChangedListener(this);
+    private static class YearAdapter extends BaseAdapter {
+        private static final int ITEM_LAYOUT = R.layout.year_label_text_view;
+        private static final int ITEM_TEXT_APPEARANCE =
+                R.style.TextAppearance_Material_DatePicker_List_YearLabel;
+        private static final int ITEM_TEXT_ACTIVATED_APPEARANCE =
+                R.style.TextAppearance_Material_DatePicker_List_YearLabel_Activated;
 
-        updateAdapterData();
+        private final LayoutInflater mInflater;
 
-        onDateChanged();
-    }
+        private int mActivatedYear;
+        private int mMinYear;
+        private int mCount;
 
-    public void setYearBackgroundColor(ColorStateList yearBackgroundColor) {
-        mYearActivatedColor = yearBackgroundColor.getColorForState(
-                StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0);
-        invalidate();
-    }
-
-    public void setYearTextAppearance(int resId) {
-        mAdapter.setItemTextAppearance(resId);
-    }
-
-    private void updateAdapterData() {
-        mAdapter.clear();
-
-        final int maxYear = mMaxDate.get(Calendar.YEAR);
-        for (int year = mMinDate.get(Calendar.YEAR); year <= maxYear; year++) {
-            mAdapter.add(year);
+        public YearAdapter(Context context) {
+            mInflater = LayoutInflater.from(context);
         }
-    }
 
-    @Override
-    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-        mController.tryVibrate();
-        if (position != mSelectedPosition) {
-            mSelectedPosition = position;
-            mAdapter.notifyDataSetChanged();
+        public void setRange(Calendar minDate, Calendar maxDate) {
+            final int minYear = minDate.get(Calendar.YEAR);
+            final int count = maxDate.get(Calendar.YEAR) - minYear + 1;
+
+            if (mMinYear != minYear || mCount != count) {
+                mMinYear = minYear;
+                mCount = count;
+                notifyDataSetInvalidated();
+            }
         }
-        mController.onYearSelected(mAdapter.getItem(position));
-    }
 
-    private class YearAdapter extends ArrayAdapter<Integer> {
-        private int mItemTextAppearanceResId;
+        public boolean setSelection(int year) {
+            if (mActivatedYear != year) {
+                mActivatedYear = year;
+                notifyDataSetChanged();
+                return true;
+            }
+            return false;
+        }
 
-        public YearAdapter(Context context, int resource) {
-            super(context, resource);
+        @Override
+        public int getCount() {
+            return mCount;
+        }
+
+        @Override
+        public Integer getItem(int position) {
+            return getYearForPosition(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return getYearForPosition(position);
+        }
+
+        public int getPositionForYear(int year) {
+            return year - mMinYear;
+        }
+
+        public int getYearForPosition(int position) {
+            return mMinYear + position;
+        }
+
+        @Override
+        public boolean hasStableIds() {
+            return true;
         }
 
         @Override
         public View getView(int position, View convertView, ViewGroup parent) {
-            final TextViewWithCircularIndicator v = (TextViewWithCircularIndicator)
-                    super.getView(position, convertView, parent);
-            v.setTextAppearance(v.getContext(), mItemTextAppearanceResId);
-            v.setCircleColor(mYearActivatedColor);
+            if (convertView == null) {
+                convertView = mInflater.inflate(ITEM_LAYOUT, parent, false);
+            }
 
-            final int year = getItem(position);
-            final boolean selected = mController.getSelectedDay().get(Calendar.YEAR) == year;
-            v.setActivated(selected);
+            final int year = getYearForPosition(position);
+            final boolean activated = mActivatedYear == year;
 
+            final int textAppearanceResId;
+            if (activated && ITEM_TEXT_ACTIVATED_APPEARANCE != 0) {
+                textAppearanceResId = ITEM_TEXT_ACTIVATED_APPEARANCE;
+            } else {
+                textAppearanceResId = ITEM_TEXT_APPEARANCE;
+            }
+
+            final TextView v = (TextView) convertView;
+            v.setText("" + year);
+            v.setTextAppearance(v.getContext(), textAppearanceResId);
+            v.setActivated(activated);
             return v;
         }
 
-        public void setItemTextAppearance(int resId) {
-            mItemTextAppearanceResId = resId;
+        @Override
+        public int getItemViewType(int position) {
+            return 0;
         }
-    }
 
-    public void postSetSelectionCentered(final int position) {
-        postSetSelectionFromTop(position, mViewSize / 2 - mChildSize / 2);
-    }
+        @Override
+        public int getViewTypeCount() {
+            return 1;
+        }
 
-    public void postSetSelectionFromTop(final int position, final int offset) {
-        post(new Runnable() {
+        @Override
+        public boolean isEmpty() {
+            return false;
+        }
 
-            @Override
-            public void run() {
-                setSelectionFromTop(position, offset);
-                requestLayout();
-            }
-        });
+        @Override
+        public boolean areAllItemsEnabled() {
+            return true;
+        }
+
+        @Override
+        public boolean isEnabled(int position) {
+            return true;
+        }
     }
 
     public int getFirstPositionOffset() {
@@ -177,22 +233,28 @@
         return firstChild.getTop();
     }
 
-    @Override
-    public void onDateChanged() {
-        updateAdapterData();
-        mAdapter.notifyDataSetChanged();
-        postSetSelectionCentered(
-                mController.getSelectedDay().get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR));
-    }
-
     /** @hide */
     @Override
     public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
         super.onInitializeAccessibilityEventInternal(event);
 
+        // There are a bunch of years, so don't bother.
         if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
             event.setFromIndex(0);
             event.setToIndex(0);
         }
     }
+
+    /**
+     * The callback used to indicate the user changed the year.
+     */
+    public interface OnYearSelectedListener {
+        /**
+         * Called upon a year change.
+         *
+         * @param view The view associated with this listener.
+         * @param year The year that was set.
+         */
+        void onYearChanged(YearPickerView view, int year);
+    }
 }
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 3ceea9d..6b35f3f 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -604,9 +604,10 @@
         if ((mAlwaysUseOption || mAdapter.hasFilteredItem()) && mAdapter.mOrigResolveList != null) {
             // Build a reasonable intent filter, based on what matched.
             IntentFilter filter = new IntentFilter();
+            String action = intent.getAction();
 
-            if (intent.getAction() != null) {
-                filter.addAction(intent.getAction());
+            if (action != null) {
+                filter.addAction(action);
             }
             Set<String> categories = intent.getCategories();
             if (categories != null) {
@@ -688,8 +689,30 @@
                     if (r.match > bestMatch) bestMatch = r.match;
                 }
                 if (alwaysCheck) {
-                    getPackageManager().addPreferredActivity(filter, bestMatch, set,
-                            intent.getComponent());
+                    PackageManager pm = getPackageManager();
+
+                    // Set the preferred Activity
+                    pm.addPreferredActivity(filter, bestMatch, set, intent.getComponent());
+
+                    // Update Domain Verification status
+                    int userId = getUserId();
+                    ComponentName cn = intent.getComponent();
+                    String packageName = cn.getPackageName();
+                    String dataScheme = (data != null) ? data.getScheme() : null;
+
+                    boolean isHttpOrHttps = (dataScheme != null) &&
+                            (dataScheme.equals(IntentFilter.SCHEME_HTTP) ||
+                            dataScheme.equals(IntentFilter.SCHEME_HTTPS));
+
+                    boolean isViewAction = (action != null) && action.equals(Intent.ACTION_VIEW);
+                    boolean hasCategoryBrowsable = (categories != null) &&
+                            categories.contains(Intent.CATEGORY_BROWSABLE);
+
+                    if (isHttpOrHttps && isViewAction && hasCategoryBrowsable) {
+                        pm.updateIntentVerificationStatus(packageName,
+                                PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
+                                userId);
+                    }
                 } else {
                     try {
                         AppGlobals.getPackageManager().setLastChosenActivity(intent,
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 6158a7b..083d6c7 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -238,6 +238,7 @@
 
     long requestFullBackupTime();
     int performFullBackup(in PackageInfo targetPackage, in ParcelFileDescriptor socket);
+    int checkFullBackupSize(long size);
     int sendBackupData(int numBytes);
     void cancelFullBackup();
 
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index 02f675c..f479f4f 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -33,6 +33,7 @@
 import android.content.pm.PackageParser.PackageParserException;
 import android.os.Build;
 import android.os.SELinux;
+import android.os.SystemProperties;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.Slog;
@@ -74,6 +75,7 @@
 
         final long[] apkHandles;
         final boolean multiArch;
+        final boolean extractNativeLibs;
 
         public static Handle create(File packageFile) throws IOException {
             try {
@@ -86,14 +88,16 @@
 
         public static Handle create(Package pkg) throws IOException {
             return create(pkg.getAllCodePaths(),
-                    (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0);
+                    (pkg.applicationInfo.flags & ApplicationInfo.FLAG_MULTIARCH) != 0,
+                    (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) != 0);
         }
 
         public static Handle create(PackageLite lite) throws IOException {
-            return create(lite.getAllCodePaths(), lite.multiArch);
+            return create(lite.getAllCodePaths(), lite.multiArch, lite.extractNativeLibs);
         }
 
-        private static Handle create(List<String> codePaths, boolean multiArch) throws IOException {
+        private static Handle create(List<String> codePaths, boolean multiArch,
+                boolean extractNativeLibs) throws IOException {
             final int size = codePaths.size();
             final long[] apkHandles = new long[size];
             for (int i = 0; i < size; i++) {
@@ -108,12 +112,13 @@
                 }
             }
 
-            return new Handle(apkHandles, multiArch);
+            return new Handle(apkHandles, multiArch, extractNativeLibs);
         }
 
-        Handle(long[] apkHandles, boolean multiArch) {
+        Handle(long[] apkHandles, boolean multiArch, boolean extractNativeLibs) {
             this.apkHandles = apkHandles;
             this.multiArch = multiArch;
+            this.extractNativeLibs = extractNativeLibs;
             mGuard.open("close");
         }
 
@@ -146,8 +151,8 @@
 
     private static native long nativeSumNativeBinaries(long handle, String cpuAbi);
 
-    private native static int nativeCopyNativeBinaries(long handle,
-            String sharedLibraryPath, String abiToCopy);
+    private native static int nativeCopyNativeBinaries(long handle, String sharedLibraryPath,
+            String abiToCopy, boolean extractNativeLibs, boolean hasNativeBridge);
 
     private static long sumNativeBinaries(Handle handle, String abi) {
         long sum = 0;
@@ -167,7 +172,8 @@
      */
     public static int copyNativeBinaries(Handle handle, File sharedLibraryDir, String abi) {
         for (long apkHandle : handle.apkHandles) {
-            int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi);
+            int res = nativeCopyNativeBinaries(apkHandle, sharedLibraryDir.getPath(), abi,
+                    handle.extractNativeLibs, HAS_NATIVE_BRIDGE);
             if (res != INSTALL_SUCCEEDED) {
                 return res;
             }
@@ -218,7 +224,8 @@
     /**
      * Remove the native binaries of a given package. This deletes the files
      */
-    public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot, boolean deleteRootDir) {
+    public static void removeNativeBinariesFromDirLI(File nativeLibraryRoot,
+            boolean deleteRootDir) {
         if (DEBUG_NATIVE) {
             Slog.w(TAG, "Deleting native binaries from: " + nativeLibraryRoot.getPath());
         }
@@ -247,7 +254,8 @@
             // asked to or this will prevent installation of future updates.
             if (deleteRootDir) {
                 if (!nativeLibraryRoot.delete()) {
-                    Slog.w(TAG, "Could not delete native binary directory: " + nativeLibraryRoot.getPath());
+                    Slog.w(TAG, "Could not delete native binary directory: " +
+                            nativeLibraryRoot.getPath());
                 }
             }
         }
@@ -416,6 +424,9 @@
     // We don't care about the other return values for now.
     private static final int BITCODE_PRESENT = 1;
 
+    private static final boolean HAS_NATIVE_BRIDGE =
+            !"0".equals(SystemProperties.get("ro.dalvik.vm.native.bridge", "0"));
+
     private static native int hasRenderscriptBitcode(long apkHandle);
 
     public static boolean hasRenderscriptBitcode(Handle handle) throws IOException {
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index 57fcf57..06bdb24 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -764,17 +764,55 @@
         private int[] mCurrentProfileIds = new int[0];
 
         private static void buildEnabledInputMethodsSettingString(
-                StringBuilder builder, Pair<String, ArrayList<String>> pair) {
-            String id = pair.first;
-            ArrayList<String> subtypes = pair.second;
-            builder.append(id);
+                StringBuilder builder, Pair<String, ArrayList<String>> ime) {
+            builder.append(ime.first);
             // Inputmethod and subtypes are saved in the settings as follows:
             // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
-            for (String subtypeId: subtypes) {
+            for (String subtypeId: ime.second) {
                 builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtypeId);
             }
         }
 
+        public static String buildInputMethodsSettingString(
+                List<Pair<String, ArrayList<String>>> allImeSettingsMap) {
+            final StringBuilder b = new StringBuilder();
+            boolean needsSeparator = false;
+            for (Pair<String, ArrayList<String>> ime : allImeSettingsMap) {
+                if (needsSeparator) {
+                    b.append(INPUT_METHOD_SEPARATER);
+                }
+                buildEnabledInputMethodsSettingString(b, ime);
+                needsSeparator = true;
+            }
+            return b.toString();
+        }
+
+        public static List<Pair<String, ArrayList<String>>> buildInputMethodsAndSubtypeList(
+                String enabledInputMethodsStr,
+                TextUtils.SimpleStringSplitter inputMethodSplitter,
+                TextUtils.SimpleStringSplitter subtypeSplitter) {
+            ArrayList<Pair<String, ArrayList<String>>> imsList =
+                    new ArrayList<Pair<String, ArrayList<String>>>();
+            if (TextUtils.isEmpty(enabledInputMethodsStr)) {
+                return imsList;
+            }
+            inputMethodSplitter.setString(enabledInputMethodsStr);
+            while (inputMethodSplitter.hasNext()) {
+                String nextImsStr = inputMethodSplitter.next();
+                subtypeSplitter.setString(nextImsStr);
+                if (subtypeSplitter.hasNext()) {
+                    ArrayList<String> subtypeHashes = new ArrayList<String>();
+                    // The first element is ime id.
+                    String imeId = subtypeSplitter.next();
+                    while (subtypeSplitter.hasNext()) {
+                        subtypeHashes.add(subtypeSplitter.next());
+                    }
+                    imsList.add(new Pair<String, ArrayList<String>>(imeId, subtypeHashes));
+                }
+            }
+            return imsList;
+        }
+
         public InputMethodSettings(
                 Resources res, ContentResolver resolver,
                 HashMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList,
@@ -875,27 +913,9 @@
         }
 
         public List<Pair<String, ArrayList<String>>> getEnabledInputMethodsAndSubtypeListLocked() {
-            ArrayList<Pair<String, ArrayList<String>>> imsList
-                    = new ArrayList<Pair<String, ArrayList<String>>>();
-            final String enabledInputMethodsStr = getEnabledInputMethodsStr();
-            if (TextUtils.isEmpty(enabledInputMethodsStr)) {
-                return imsList;
-            }
-            mInputMethodSplitter.setString(enabledInputMethodsStr);
-            while (mInputMethodSplitter.hasNext()) {
-                String nextImsStr = mInputMethodSplitter.next();
-                mSubtypeSplitter.setString(nextImsStr);
-                if (mSubtypeSplitter.hasNext()) {
-                    ArrayList<String> subtypeHashes = new ArrayList<String>();
-                    // The first element is ime id.
-                    String imeId = mSubtypeSplitter.next();
-                    while (mSubtypeSplitter.hasNext()) {
-                        subtypeHashes.add(mSubtypeSplitter.next());
-                    }
-                    imsList.add(new Pair<String, ArrayList<String>>(imeId, subtypeHashes));
-                }
-            }
-            return imsList;
+            return buildInputMethodsAndSubtypeList(getEnabledInputMethodsStr(),
+                    mInputMethodSplitter,
+                    mSubtypeSplitter);
         }
 
         public void appendAndPutEnabledInputMethodLocked(String id, boolean reloadInputMethodStr) {
diff --git a/core/java/com/android/internal/logging/EventLogTags.logtags b/core/java/com/android/internal/logging/EventLogTags.logtags
new file mode 100644
index 0000000..870d20d
--- /dev/null
+++ b/core/java/com/android/internal/logging/EventLogTags.logtags
@@ -0,0 +1,7 @@
+# See system/core/logcat/event.logtags for a description of the format of this file.
+
+option java_package com.android.internal.logging;
+
+# interaction logs
+524287 sysui_view_visibility (category|1|5),(visible|1|6)
+524288 sysui_action (category|1|5)
diff --git a/core/java/com/android/internal/logging/MetricsConstants.java b/core/java/com/android/internal/logging/MetricsConstants.java
new file mode 100644
index 0000000..e5cba84
--- /dev/null
+++ b/core/java/com/android/internal/logging/MetricsConstants.java
@@ -0,0 +1,153 @@
+/*
+ * 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.internal.logging;
+
+/**
+ * Constants for mestrics logs.
+ *
+ * @hide
+ */
+public interface MetricsConstants {
+    // These constants must match those in the analytic pipeline.
+    public static final int ACCESSIBILITY = 2;
+    public static final int ACCESSIBILITY_CAPTION_PROPERTIES = 3;
+    public static final int ACCESSIBILITY_SERVICE = 4;
+    public static final int ACCESSIBILITY_TOGGLE_DALTONIZER = 5;
+    public static final int ACCESSIBILITY_TOGGLE_GLOBAL_GESTURE = 6;
+    public static final int ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFICATION = 7;
+    public static final int ACCOUNT = 8;
+    public static final int ACCOUNTS_ACCOUNT_SYNC = 9;
+    public static final int ACCOUNTS_CHOOSE_ACCOUNT_ACTIVITY = 10;
+    public static final int ACCOUNTS_MANAGE_ACCOUNTS = 11;
+    public static final int APN = 12;
+    public static final int APN_EDITOR = 13;
+    public static final int APPLICATION = 16;
+    public static final int APPLICATIONS_APP_LAUNCH = 17;
+    public static final int APPLICATIONS_APP_PERMISSION = 18;
+    public static final int APPLICATIONS_APP_STORAGE = 19;
+    public static final int APPLICATIONS_INSTALLED_APP_DETAILS = 20;
+    public static final int APPLICATIONS_PROCESS_STATS_DETAIL = 21;
+    public static final int APPLICATIONS_PROCESS_STATS_MEM_DETAIL = 22;
+    public static final int APPLICATIONS_PROCESS_STATS_UI = 23;
+    public static final int APP_OPS_DETAILS = 14;
+    public static final int APP_OPS_SUMMARY = 15;
+    public static final int BLUETOOTH = 24;
+    public static final int BLUETOOTH_DEVICE_PICKER = 25;
+    public static final int BLUETOOTH_DEVICE_PROFILES = 26;
+    public static final int CHOOSE_LOCK_GENERIC = 27;
+    public static final int CHOOSE_LOCK_PASSWORD = 28;
+    public static final int CHOOSE_LOCK_PATTERN = 29;
+    public static final int CONFIRM_LOCK_PASSWORD = 30;
+    public static final int CONFIRM_LOCK_PATTERN = 31;
+    public static final int CRYPT_KEEPER = 32;
+    public static final int CRYPT_KEEPER_CONFIRM = 33;
+    public static final int DASHBOARD_SEARCH_RESULTS = 34;
+    public static final int DASHBOARD_SUMMARY = 35;
+    public static final int DATA_USAGE = 36;
+    public static final int DATA_USAGE_SUMMARY = 37;
+    public static final int DATE_TIME = 38;
+    public static final int DEVELOPMENT = 39;
+    public static final int DEVICEINFO = 40;
+    public static final int DEVICEINFO_IMEI_INFORMATION = 41;
+    public static final int DEVICEINFO_MEMORY = 42;
+    public static final int DEVICEINFO_SIM_STATUS = 43;
+    public static final int DEVICEINFO_STATUS = 44;
+    public static final int DEVICEINFO_USB = 45;
+    public static final int DISPLAY = 46;
+    public static final int DREAM = 47;
+    public static final int ENCRYPTION = 48;
+    public static final int FINGERPRINT = 49;
+    public static final int FINGERPRINT_ENROLL = 50;
+    public static final int FUELGAUGE_BATTERY_HISTORY_DETAIL = 51;
+    public static final int FUELGAUGE_BATTERY_SAVER = 52;
+    public static final int FUELGAUGE_POWER_USAGE_DETAIL = 53;
+    public static final int FUELGAUGE_POWER_USAGE_SUMMARY = 54;
+    public static final int HOME = 55;
+    public static final int ICC_LOCK = 56;
+    public static final int INPUTMETHOD_KEYBOARD = 58;
+    public static final int INPUTMETHOD_LANGUAGE = 57;
+    public static final int INPUTMETHOD_SPELL_CHECKERS = 59;
+    public static final int INPUTMETHOD_SUBTYPE_ENABLER = 60;
+    public static final int INPUTMETHOD_USER_DICTIONARY = 61;
+    public static final int INPUTMETHOD_USER_DICTIONARY_ADD_WORD = 62;
+    public static final int LOCATION = 63;
+    public static final int LOCATION_MODE = 64;
+    public static final int MAIN_SETTINGS = 1;
+    public static final int MANAGE_APPLICATIONS = 65;
+    public static final int MASTER_CLEAR = 66;
+    public static final int MASTER_CLEAR_CONFIRM = 67;
+    public static final int NET_DATA_USAGE_METERED = 68;
+    public static final int NFC_BEAM = 69;
+    public static final int NFC_PAYMENT = 70;
+    public static final int NOTIFICATION = 71;
+    public static final int NOTIFICATION_APP_NOTIFICATION = 72;
+    public static final int NOTIFICATION_OTHER_SOUND = 73;
+    public static final int NOTIFICATION_REDACTION = 74;
+    public static final int NOTIFICATION_STATION = 75;
+    public static final int NOTIFICATION_ZEN_MODE = 76;
+    public static final int OWNER_INFO = 77;
+    public static final int PRINT_JOB_SETTINGS = 78;
+    public static final int PRINT_SERVICE_SETTINGS = 79;
+    public static final int PRINT_SETTINGS = 80;
+    public static final int PRIVACY = 81;
+    public static final int PROXY_SELECTOR = 82;
+    public static final int QS_AIRPLANEMODE = 112;
+    public static final int QS_BLUETOOTH = 113;
+    public static final int QS_CAST = 114;
+    public static final int QS_CELLULAR = 115;
+    public static final int QS_COLORINVERSION = 116;
+    public static final int QS_DATAUSAGEDETAIL = 117;
+    public static final int QS_DND = 118;
+    public static final int QS_FLASHLIGHT = 119;
+    public static final int QS_HOTSPOT = 120;
+    public static final int QS_INTENT = 121;
+    public static final int QS_LOCATION = 122;
+    public static final int QS_PANEL = 111;
+    public static final int QS_ROTATIONLOCK = 123;
+    public static final int QS_USERDETAIL = 125;
+    public static final int QS_USERDETAILITE = 124;
+    public static final int QS_WIFI = 126;
+    public static final int RESET_NETWORK = 83;
+    public static final int RESET_NETWORK_CONFIRM = 84;
+    public static final int RUNNING_SERVICE_DETAILS = 85;
+    public static final int SCREEN_PINNING = 86;
+    public static final int SECURITY = 87;
+    public static final int SIM = 88;
+    public static final int TESTING = 89;
+    public static final int TETHER = 90;
+    public static final int TRUSTED_CREDENTIALS = 92;
+    public static final int TRUST_AGENT = 91;
+    public static final int TTS_ENGINE_SETTINGS = 93;
+    public static final int TTS_TEXT_TO_SPEECH = 94;
+    public static final int TYPE_UNKNOWN = 0;
+    public static final int USAGE_ACCESS = 95;
+    public static final int USER = 96;
+    public static final int USERS_APP_RESTRICTIONS = 97;
+    public static final int USER_DETAILS = 98;
+    public static final int VIEW_UNKNOWN = 0;
+    public static final int VOICE_INPUT = 99;
+    public static final int VPN = 100;
+    public static final int WALLPAPER_TYPE = 101;
+    public static final int WFD_WIFI_DISPLAY = 102;
+    public static final int WIFI = 103;
+    public static final int WIFI_ADVANCED = 104;
+    public static final int WIFI_APITEST = 107;
+    public static final int WIFI_CALLING = 105;
+    public static final int WIFI_INFO = 108;
+    public static final int WIFI_P2P = 109;
+    public static final int WIFI_SAVED_ACCESS_POINTS = 106;
+    public static final int WIRELESS = 110;
+}
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
new file mode 100644
index 0000000..9b45e34
--- /dev/null
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -0,0 +1,63 @@
+/*
+ * 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.internal.logging;
+
+
+import android.content.Context;
+import android.os.Build;
+
+/**
+ * Log all the things.
+ *
+ * @hide
+ */
+public class MetricsLogger implements MetricsConstants {
+    // These constants are temporary, they should migrate to MetricsConstants.
+    public static final int APPLICATIONS_ADVANCED = 132;
+    public static final int LOCATION_SCANNING = 133;
+    public static final int MANAGE_APPLICATIONS_ALL = 134;
+    public static final int MANAGE_APPLICATIONS_NOTIFICATIONS = 135;
+
+    public static final int ACTION_WIFI_ADD_NETWORK = 136;
+    public static final int ACTION_WIFI_CONNECT = 137;
+    public static final int ACTION_WIFI_FORCE_SCAN = 138;
+    public static final int ACTION_WIFI_FORGET = 139;
+    public static final int ACTION_WIFI_OFF = 140;
+    public static final int ACTION_WIFI_ON = 141;
+
+    public static final int MANAGE_PERMISSIONS = 142;
+
+    public static void visible(Context context, int category) throws IllegalArgumentException {
+        if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
+            throw new IllegalArgumentException("Must define metric category");
+        }
+        EventLogTags.writeSysuiViewVisibility(category, 100);
+    }
+
+    public static void hidden(Context context, int category) {
+        if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
+            throw new IllegalArgumentException("Must define metric category");
+        }
+        EventLogTags.writeSysuiViewVisibility(category, 0);
+    }
+
+    public static void action(Context context, int category) {
+        if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
+            throw new IllegalArgumentException("Must define metric category");
+        }
+        EventLogTags.writeSysuiAction(category);
+    }
+}
diff --git a/core/java/com/android/internal/midi/EventScheduler.java b/core/java/com/android/internal/midi/EventScheduler.java
new file mode 100644
index 0000000..7b9a48c
--- /dev/null
+++ b/core/java/com/android/internal/midi/EventScheduler.java
@@ -0,0 +1,244 @@
+/*
+ * 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.internal.midi;
+
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Store arbitrary timestamped events using a Long timestamp.
+ * Only one Thread can write into the buffer.
+ * And only one Thread can read from the buffer.
+ */
+public class EventScheduler {
+    private static final long NANOS_PER_MILLI = 1000000;
+
+    private final Object mLock = new Object();
+    private SortedMap<Long, FastEventQueue> mEventBuffer;
+    private FastEventQueue mEventPool = null;
+    private int mMaxPoolSize = 200;
+    private boolean mClosed;
+
+    public EventScheduler() {
+        mEventBuffer = new TreeMap<Long, FastEventQueue>();
+    }
+
+    // If we keep at least one node in the list then it can be atomic
+    // and non-blocking.
+    private class FastEventQueue {
+        // One thread takes from the beginning of the list.
+        volatile SchedulableEvent mFirst;
+        // A second thread returns events to the end of the list.
+        volatile SchedulableEvent mLast;
+        volatile long mEventsAdded;
+        volatile long mEventsRemoved;
+
+        FastEventQueue(SchedulableEvent event) {
+            mFirst = event;
+            mLast = mFirst;
+            mEventsAdded = 1;
+            mEventsRemoved = 0;
+        }
+
+        int size() {
+            return (int)(mEventsAdded - mEventsRemoved);
+        }
+
+        /**
+         * Do not call this unless there is more than one event
+         * in the list.
+         * @return first event in the list
+         */
+        public SchedulableEvent remove() {
+            // Take first event.
+            mEventsRemoved++;
+            SchedulableEvent event = mFirst;
+            mFirst = event.mNext;
+            return event;
+        }
+
+        /**
+         * @param event
+         */
+        public void add(SchedulableEvent event) {
+            event.mNext = null;
+            mLast.mNext = event;
+            mLast = event;
+            mEventsAdded++;
+        }
+    }
+
+    /**
+     * Base class for events that can be stored in the EventScheduler.
+     */
+    public static class SchedulableEvent {
+        private long mTimestamp;
+        private SchedulableEvent mNext = null;
+
+        /**
+         * @param timestamp
+         */
+        public SchedulableEvent(long timestamp) {
+            mTimestamp = timestamp;
+        }
+
+        /**
+         * @return timestamp
+         */
+        public long getTimestamp() {
+            return mTimestamp;
+        }
+
+        /**
+         * The timestamp should not be modified when the event is in the
+         * scheduling buffer.
+         */
+        public void setTimestamp(long timestamp) {
+            mTimestamp = timestamp;
+        }
+    }
+
+    /**
+     * Get an event from the pool.
+     * Always leave at least one event in the pool.
+     * @return event or null
+     */
+    public SchedulableEvent removeEventfromPool() {
+        SchedulableEvent event = null;
+        if (mEventPool != null && (mEventPool.size() > 1)) {
+            event = mEventPool.remove();
+        }
+        return event;
+    }
+
+    /**
+     * Return events to a pool so they can be reused.
+     *
+     * @param event
+     */
+    public void addEventToPool(SchedulableEvent event) {
+        if (mEventPool == null) {
+            mEventPool = new FastEventQueue(event);
+        // If we already have enough items in the pool then just
+        // drop the event. This prevents unbounded memory leaks.
+        } else if (mEventPool.size() < mMaxPoolSize) {
+            mEventPool.add(event);
+        }
+    }
+
+    /**
+     * Add an event to the scheduler. Events with the same time will be
+     * processed in order.
+     *
+     * @param event
+     */
+    public void add(SchedulableEvent event) {
+        synchronized (mLock) {
+            FastEventQueue list = mEventBuffer.get(event.getTimestamp());
+            if (list == null) {
+                long lowestTime = mEventBuffer.isEmpty() ? Long.MAX_VALUE
+                        : mEventBuffer.firstKey();
+                list = new FastEventQueue(event);
+                mEventBuffer.put(event.getTimestamp(), list);
+                // If the event we added is earlier than the previous earliest
+                // event then notify any threads waiting for the next event.
+                if (event.getTimestamp() < lowestTime) {
+                    mLock.notify();
+                }
+            } else {
+                list.add(event);
+            }
+        }
+    }
+
+    private SchedulableEvent removeNextEventLocked(long lowestTime) {
+        SchedulableEvent event;
+        FastEventQueue list = mEventBuffer.get(lowestTime);
+        // Remove list from tree if this is the last node.
+        if ((list.size() == 1)) {
+            mEventBuffer.remove(lowestTime);
+        }
+        event = list.remove();
+        return event;
+    }
+
+    /**
+     * Check to see if any scheduled events are ready to be processed.
+     *
+     * @param timestamp
+     * @return next event or null if none ready
+     */
+    public SchedulableEvent getNextEvent(long time) {
+        SchedulableEvent event = null;
+        synchronized (mLock) {
+            if (!mEventBuffer.isEmpty()) {
+                long lowestTime = mEventBuffer.firstKey();
+                // Is it time for this list to be processed?
+                if (lowestTime <= time) {
+                    event = removeNextEventLocked(lowestTime);
+                }
+            }
+        }
+        // Log.i(TAG, "getNextEvent: event = " + event);
+        return event;
+    }
+
+    /**
+     * Return the next available event or wait until there is an event ready to
+     * be processed. This method assumes that the timestamps are in nanoseconds
+     * and that the current time is System.nanoTime().
+     *
+     * @return event
+     * @throws InterruptedException
+     */
+    public SchedulableEvent waitNextEvent() throws InterruptedException {
+        SchedulableEvent event = null;
+        synchronized (mLock) {
+            while (!mClosed) {
+                long millisToWait = Integer.MAX_VALUE;
+                if (!mEventBuffer.isEmpty()) {
+                    long now = System.nanoTime();
+                    long lowestTime = mEventBuffer.firstKey();
+                    // Is it time for the earliest list to be processed?
+                    if (lowestTime <= now) {
+                        event = removeNextEventLocked(lowestTime);
+                        break;
+                    } else {
+                        // Figure out how long to sleep until next event.
+                        long nanosToWait = lowestTime - now;
+                        // Add 1 millisecond so we don't wake up before it is
+                        // ready.
+                        millisToWait = 1 + (nanosToWait / NANOS_PER_MILLI);
+                        // Clip 64-bit value to 32-bit max.
+                        if (millisToWait > Integer.MAX_VALUE) {
+                            millisToWait = Integer.MAX_VALUE;
+                        }
+                    }
+                }
+                mLock.wait((int) millisToWait);
+            }
+        }
+        return event;
+    }
+
+    public void close() {
+        synchronized (mLock) {
+            mClosed = true;
+            mLock.notify();
+        }
+    }
+}
diff --git a/core/java/com/android/internal/midi/MidiConstants.java b/core/java/com/android/internal/midi/MidiConstants.java
new file mode 100644
index 0000000..87552e4
--- /dev/null
+++ b/core/java/com/android/internal/midi/MidiConstants.java
@@ -0,0 +1,88 @@
+/*
+ * 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.internal.midi;
+
+/**
+ * MIDI related constants and static methods.
+ */
+public class MidiConstants {
+    public static final byte STATUS_COMMAND_MASK = (byte) 0xF0;
+    public static final byte STATUS_CHANNEL_MASK = (byte) 0x0F;
+
+    // Channel voice messages.
+    public static final byte STATUS_NOTE_OFF = (byte) 0x80;
+    public static final byte STATUS_NOTE_ON = (byte) 0x90;
+    public static final byte STATUS_POLYPHONIC_AFTERTOUCH = (byte) 0xA0;
+    public static final byte STATUS_CONTROL_CHANGE = (byte) 0xB0;
+    public static final byte STATUS_PROGRAM_CHANGE = (byte) 0xC0;
+    public static final byte STATUS_CHANNEL_PRESSURE = (byte) 0xD0;
+    public static final byte STATUS_PITCH_BEND = (byte) 0xE0;
+
+    // System Common Messages.
+    public static final byte STATUS_SYSTEM_EXCLUSIVE = (byte) 0xF0;
+    public static final byte STATUS_MIDI_TIME_CODE = (byte) 0xF1;
+    public static final byte STATUS_SONG_POSITION = (byte) 0xF2;
+    public static final byte STATUS_SONG_SELECT = (byte) 0xF3;
+    public static final byte STATUS_TUNE_REQUEST = (byte) 0xF6;
+    public static final byte STATUS_END_SYSEX = (byte) 0xF7;
+
+    // System Real-Time Messages
+    public static final byte STATUS_TIMING_CLOCK = (byte) 0xF8;
+    public static final byte STATUS_START = (byte) 0xFA;
+    public static final byte STATUS_CONTINUE = (byte) 0xFB;
+    public static final byte STATUS_STOP = (byte) 0xFC;
+    public static final byte STATUS_ACTIVE_SENSING = (byte) 0xFE;
+    public static final byte STATUS_RESET = (byte) 0xFF;
+
+    /** Number of bytes in a message nc from 8c to Ec */
+    public final static int CHANNEL_BYTE_LENGTHS[] = { 3, 3, 3, 3, 2, 2, 3 };
+
+    /** Number of bytes in a message Fn from F0 to FF */
+    public final static int SYSTEM_BYTE_LENGTHS[] = { 1, 2, 3, 2, 1, 1, 1, 1, 1,
+            1, 1, 1, 1, 1, 1, 1 };
+
+    /********************************************************************/
+
+    public static int getBytesPerMessage(int command) {
+        if ((command < 0x80) || (command > 0xFF)) {
+            return 0;
+        } else if (command >= 0xF0) {
+            return SYSTEM_BYTE_LENGTHS[command & 0x0F];
+        } else {
+            return CHANNEL_BYTE_LENGTHS[(command >> 4) - 8];
+        }
+    }
+
+    /**
+     * @param msg
+     * @param offset
+     * @param count
+     * @return true if the entire message is ActiveSensing commands
+     */
+    public static boolean isAllActiveSensing(byte[] msg, int offset,
+            int count) {
+        // Count bytes that are not active sensing.
+        int goodBytes = 0;
+        for (int i = 0; i < count; i++) {
+            byte b = msg[offset + i];
+            if (b != MidiConstants.STATUS_ACTIVE_SENSING) {
+                goodBytes++;
+            }
+        }
+        return (goodBytes == 0);
+    }
+}
diff --git a/media/java/android/media/midi/MidiDispatcher.java b/core/java/com/android/internal/midi/MidiDispatcher.java
similarity index 73%
rename from media/java/android/media/midi/MidiDispatcher.java
rename to core/java/com/android/internal/midi/MidiDispatcher.java
index 0868346..377bc68 100644
--- a/media/java/android/media/midi/MidiDispatcher.java
+++ b/core/java/com/android/internal/midi/MidiDispatcher.java
@@ -14,19 +14,20 @@
  * limitations under the License.
  */
 
-package android.media.midi;
+package com.android.internal.midi;
+
+import android.media.midi.MidiReceiver;
+import android.media.midi.MidiSender;
 
 import java.io.IOException;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
- * Utility class for dispatching MIDI data to a list of {@link MidiReceiver}s.
- * This class subclasses {@link MidiReceiver} and dispatches any data it receives
+ * Utility class for dispatching MIDI data to a list of {@link android.media.midi.MidiReceiver}s.
+ * This class subclasses {@link android.media.midi.MidiReceiver} and dispatches any data it receives
  * to its receiver list. Any receivers that throw an exception upon receiving data will
  * be automatically removed from the receiver list, but no IOException will be returned
- * from the dispatcher's {@link #onReceive} in that case.
- *
- * @hide
+ * from the dispatcher's {@link android.media.midi.MidiReceiver#onReceive} in that case.
  */
 public final class MidiDispatcher extends MidiReceiver {
 
@@ -35,7 +36,7 @@
 
     private final MidiSender mSender = new MidiSender() {
         /**
-         * Called to connect a {@link MidiReceiver} to the sender
+         * Called to connect a {@link android.media.midi.MidiReceiver} to the sender
          *
          * @param receiver the receiver to connect
          */
@@ -44,7 +45,7 @@
         }
 
         /**
-         * Called to disconnect a {@link MidiReceiver} from the sender
+         * Called to disconnect a {@link android.media.midi.MidiReceiver} from the sender
          *
          * @param receiver the receiver to disconnect
          */
@@ -54,7 +55,7 @@
     };
 
     /**
-     * Returns the number of {@link MidiReceiver}s this dispatcher contains.
+     * Returns the number of {@link android.media.midi.MidiReceiver}s this dispatcher contains.
      * @return the number of receivers
      */
     public int getReceiverCount() {
@@ -62,7 +63,8 @@
     }
 
     /**
-     * Returns a {@link MidiSender} which is used to add and remove {@link MidiReceiver}s
+     * Returns a {@link android.media.midi.MidiSender} which is used to add and remove
+     * {@link android.media.midi.MidiReceiver}s
      * to the dispatcher's receiver list.
      * @return the dispatcher's MidiSender
      */
diff --git a/core/java/com/android/internal/midi/MidiEventScheduler.java b/core/java/com/android/internal/midi/MidiEventScheduler.java
new file mode 100644
index 0000000..42d70f6
--- /dev/null
+++ b/core/java/com/android/internal/midi/MidiEventScheduler.java
@@ -0,0 +1,146 @@
+/*
+ * 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.internal.midi;
+
+import android.media.midi.MidiReceiver;
+
+import java.io.IOException;
+
+/**
+ * Add MIDI Events to an EventScheduler
+ */
+public class MidiEventScheduler extends EventScheduler {
+    private static final String TAG = "MidiEventScheduler";
+    // Maintain a pool of scheduled events to reduce memory allocation.
+    // This pool increases performance by about 14%.
+    private final static int POOL_EVENT_SIZE = 16;
+
+    private final MidiReceiver[] mReceivers;
+
+    private class SchedulingReceiver extends MidiReceiver {
+        private final int mPortNumber;
+
+        public SchedulingReceiver(int portNumber) {
+            mPortNumber = portNumber;
+        }
+
+        /**
+         * Store these bytes in the EventScheduler to be delivered at the specified
+         * time.
+         */
+        @Override
+        public void onReceive(byte[] msg, int offset, int count, long timestamp)
+                throws IOException {
+            MidiEvent event = createScheduledEvent(msg, offset, count, timestamp);
+            if (event != null) {
+                event.portNumber = mPortNumber;
+                add(event);
+            }
+        }
+    }
+
+    public static class MidiEvent extends SchedulableEvent {
+        public int portNumber;
+        public int count = 0;
+        public byte[] data;
+
+        private MidiEvent(int count) {
+            super(0);
+            data = new byte[count];
+        }
+
+        private MidiEvent(byte[] msg, int offset, int count, long timestamp) {
+            super(timestamp);
+            data = new byte[count];
+            System.arraycopy(msg, offset, data, 0, count);
+            this.count = count;
+        }
+
+        @Override
+        public String toString() {
+            String text = "Event: ";
+            for (int i = 0; i < count; i++) {
+                text += data[i] + ", ";
+            }
+            return text;
+        }
+    }
+
+    public MidiEventScheduler() {
+        this(0);
+    }
+
+    public MidiEventScheduler(int portCount) {
+        mReceivers = new MidiReceiver[portCount];
+        for (int i = 0; i < portCount; i++) {
+            mReceivers[i] = new SchedulingReceiver(i);
+        }
+    }
+
+    /**
+     * Create an event that contains the message.
+     */
+    private MidiEvent createScheduledEvent(byte[] msg, int offset, int count,
+            long timestamp) {
+        MidiEvent event;
+        if (count > POOL_EVENT_SIZE) {
+            event = new MidiEvent(msg, offset, count, timestamp);
+        } else {
+            event = (MidiEvent) removeEventfromPool();
+            if (event == null) {
+                event = new MidiEvent(POOL_EVENT_SIZE);
+            }
+            System.arraycopy(msg, offset, event.data, 0, count);
+            event.count = count;
+            event.setTimestamp(timestamp);
+        }
+        return event;
+    }
+
+    /**
+     * Return events to a pool so they can be reused.
+     *
+     * @param event
+     */
+    @Override
+    public void addEventToPool(SchedulableEvent event) {
+        // Make sure the event is suitable for the pool.
+        if (event instanceof MidiEvent) {
+            MidiEvent midiEvent = (MidiEvent) event;
+            if (midiEvent.data.length == POOL_EVENT_SIZE) {
+                super.addEventToPool(event);
+            }
+        }
+    }
+
+    /**
+     * This MidiReceiver will write date to the scheduling buffer.
+     * @return the MidiReceiver
+     */
+    public MidiReceiver getReceiver() {
+        return mReceivers[0];
+    }
+
+    /**
+     * This MidiReceiver will write date to the scheduling buffer.
+     * @return the MidiReceiver
+     */
+    public MidiReceiver getReceiver(int portNumber) {
+        return mReceivers[portNumber];
+    }
+
+}
diff --git a/core/java/com/android/internal/midi/MidiFramer.java b/core/java/com/android/internal/midi/MidiFramer.java
new file mode 100644
index 0000000..53d71bb
--- /dev/null
+++ b/core/java/com/android/internal/midi/MidiFramer.java
@@ -0,0 +1,92 @@
+/*
+ * 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.internal.midi;
+
+import android.media.midi.MidiReceiver;
+
+import java.io.IOException;
+
+/**
+ * Convert stream of bytes to discrete messages.
+ *
+ * Parses the incoming bytes and then posts individual messages to the receiver
+ * specified in the constructor. Short messages of 1-3 bytes will be complete.
+ * System Exclusive messages may be posted in pieces.
+ *
+ * Resolves Running Status and
+ * interleaved System Real-Time messages.
+ */
+public class MidiFramer extends MidiReceiver {
+
+    public String TAG = "MidiFramer";
+    private MidiReceiver mReceiver;
+    private byte[] mBuffer = new byte[3];
+    private int mCount;
+    private int mRunningStatus;
+    private int mNeeded;
+
+    public MidiFramer(MidiReceiver receiver) {
+        mReceiver = receiver;
+    }
+
+    public static String formatMidiData(byte[] data, int offset, int count) {
+        String text = "MIDI+" + offset + " : ";
+        for (int i = 0; i < count; i++) {
+            text += String.format("0x%02X, ", data[offset + i]);
+        }
+        return text;
+    }
+
+    /*
+     * @see android.midi.MidiReceiver#onPost(byte[], int, int, long)
+     */
+    @Override
+    public void onReceive(byte[] data, int offset, int count, long timestamp)
+            throws IOException {
+        // Log.i(TAG, formatMidiData(data, offset, count));
+        for (int i = 0; i < count; i++) {
+            int b = data[offset] & 0xFF;
+            if (b >= 0x80) { // status byte?
+                if (b < 0xF0) { // channel message?
+                    mRunningStatus = (byte) b;
+                    mCount = 1;
+                    mNeeded = MidiConstants.getBytesPerMessage(b) - 1;
+                } else if (b < 0xF8) { // system common?
+                    mBuffer[0] = (byte) b;
+                    mRunningStatus = 0;
+                    mCount = 1;
+                    mNeeded = MidiConstants.getBytesPerMessage(b) - 1;
+                } else { // real-time?
+                    // Single byte message interleaved with other data.
+                    mReceiver.sendWithTimestamp(data, offset, 1, timestamp);
+                }
+            } else { // data byte
+                mBuffer[mCount++] = (byte) b;
+                if (--mNeeded == 0) {
+                    if (mRunningStatus != 0) {
+                        mBuffer[0] = (byte) mRunningStatus;
+                    }
+                    mReceiver.sendWithTimestamp(mBuffer, 0, mCount, timestamp);
+                    mNeeded = MidiConstants.getBytesPerMessage(mBuffer[0]) - 1;
+                    mCount = 1;
+                }
+            }
+            ++offset;
+        }
+    }
+
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 65a970a..05ed3ab 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -16,18 +16,14 @@
 
 package com.android.internal.os;
 
-import static android.net.NetworkStats.UID_ALL;
-import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
-
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.bluetooth.BluetoothActivityEnergyInfo;
-import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHeadset;
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.NetworkStats;
-import android.net.wifi.IWifiManager;
 import android.net.wifi.WifiActivityEnergyInfo;
 import android.net.wifi.WifiManager;
 import android.os.BadParcelableException;
@@ -42,8 +38,6 @@
 import android.os.ParcelFormatException;
 import android.os.Parcelable;
 import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.WorkSource;
@@ -65,13 +59,14 @@
 import android.util.Xml;
 import android.view.Display;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.net.NetworkStatsFactory;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.XmlUtils;
+import com.android.server.NetworkManagementSocketTagger;
+import libcore.util.EmptyArray;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -109,7 +104,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 120 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 123 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -132,6 +127,9 @@
     static final int MSG_REPORT_POWER_CHANGE = 2;
     static final long DELAY_UPDATE_WAKELOCKS = 5*1000;
 
+    private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
+    private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
+
     public interface BatteryCallback {
         public void batteryNeedsCpuUpdate();
         public void batteryPowerChanged(boolean onBattery);
@@ -160,7 +158,12 @@
         }
     }
 
+    public interface ExternalStatsSync {
+        void scheduleSync();
+    }
+
     public final MyHandler mHandler;
+    private final ExternalStatsSync mExternalSync;
 
     private BatteryCallback mCallback;
 
@@ -310,6 +313,9 @@
     boolean mPowerSaveModeEnabled;
     StopwatchTimer mPowerSaveModeEnabledTimer;
 
+    boolean mDeviceIdling;
+    StopwatchTimer mDeviceIdlingTimer;
+
     boolean mDeviceIdleModeEnabled;
     StopwatchTimer mDeviceIdleModeEnabledTimer;
 
@@ -327,7 +333,7 @@
 
     int mPhoneSignalStrengthBin = -1;
     int mPhoneSignalStrengthBinRaw = -1;
-    final StopwatchTimer[] mPhoneSignalStrengthsTimer = 
+    final StopwatchTimer[] mPhoneSignalStrengthsTimer =
             new StopwatchTimer[SignalStrength.NUM_SIGNAL_STRENGTH_BINS];
 
     StopwatchTimer mPhoneSignalScanningTimer;
@@ -414,6 +420,7 @@
     int mMinDischargeStepLevel;
     final LevelStepTracker mDischargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS);
     final LevelStepTracker mDailyDischargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS*2);
+    ArrayList<PackageChange> mDailyPackageChanges;
 
     int mLastChargeStepLevel;
     int mMaxChargeStepLevel;
@@ -441,18 +448,17 @@
     private int mLoadedNumConnectivityChange;
     private int mUnpluggedNumConnectivityChange;
 
+    private final NetworkStats.Entry mTmpNetworkStatsEntry = new NetworkStats.Entry();
+
     /*
      * Holds a SamplingTimer associated with each kernel wakelock name being tracked.
      */
-    private final HashMap<String, SamplingTimer> mKernelWakelockStats =
-            new HashMap<String, SamplingTimer>();
+    private final HashMap<String, SamplingTimer> mKernelWakelockStats = new HashMap<>();
 
     public Map<String, ? extends Timer> getKernelWakelockStats() {
         return mKernelWakelockStats;
     }
 
-    private static int sKernelWakelockUpdateVersion = 0;
-
     String mLastWakeupReason = null;
     long mLastWakeupUptimeMs = 0;
     private final HashMap<String, SamplingTimer> mWakeupReasonStats = new HashMap<>();
@@ -461,56 +467,12 @@
         return mWakeupReasonStats;
     }
 
-    private static final int[] PROC_WAKELOCKS_FORMAT = new int[] {
-        Process.PROC_TAB_TERM|Process.PROC_OUT_STRING|                // 0: name
-                              Process.PROC_QUOTES,
-        Process.PROC_TAB_TERM|Process.PROC_OUT_LONG,                  // 1: count
-        Process.PROC_TAB_TERM,
-        Process.PROC_TAB_TERM,
-        Process.PROC_TAB_TERM,
-        Process.PROC_TAB_TERM|Process.PROC_OUT_LONG,                  // 5: totalTime
-    };
-
-    private static final int[] WAKEUP_SOURCES_FORMAT = new int[] {
-        Process.PROC_TAB_TERM|Process.PROC_OUT_STRING,                // 0: name
-        Process.PROC_TAB_TERM|Process.PROC_COMBINE|
-                              Process.PROC_OUT_LONG,                  // 1: count
-        Process.PROC_TAB_TERM|Process.PROC_COMBINE,
-        Process.PROC_TAB_TERM|Process.PROC_COMBINE,
-        Process.PROC_TAB_TERM|Process.PROC_COMBINE,
-        Process.PROC_TAB_TERM|Process.PROC_COMBINE,
-        Process.PROC_TAB_TERM|Process.PROC_COMBINE
-                             |Process.PROC_OUT_LONG,                  // 6: totalTime
-    };
-
-    private final String[] mProcWakelocksName = new String[3];
-    private final long[] mProcWakelocksData = new long[3];
-
-    /*
-     * Used as a buffer for reading in data from /proc/wakelocks before it is processed and added
-     * to mKernelWakelockStats.
-     */
-    private final Map<String, KernelWakelockStats> mProcWakelockFileStats =
-            new HashMap<String, KernelWakelockStats>();
-
-    private final NetworkStatsFactory mNetworkStatsFactory = new NetworkStatsFactory();
-    private NetworkStats mCurMobileSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50);
-    private NetworkStats mLastMobileSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50);
-    private NetworkStats mCurWifiSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50);
-    private NetworkStats mLastWifiSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), 50);
-    private NetworkStats mTmpNetworkStats;
-    private final NetworkStats.Entry mTmpNetworkStatsEntry = new NetworkStats.Entry();
-
-    @GuardedBy("this")
-    private String[] mMobileIfaces = new String[0];
-    @GuardedBy("this")
-    private String[] mWifiIfaces = new String[0];
-
     public BatteryStatsImpl() {
         mFile = null;
         mCheckinFile = null;
         mDailyFile = null;
         mHandler = null;
+        mExternalSync = null;
         clearHistoryLocked();
     }
 
@@ -520,7 +482,7 @@
     }
 
     static class TimeBase {
-        private final ArrayList<TimeBaseObs> mObservers = new ArrayList<TimeBaseObs>();
+        private final ArrayList<TimeBaseObs> mObservers = new ArrayList<>();
 
         private long mUptime;
         private long mRealtime;
@@ -1775,147 +1737,6 @@
         return timer;
     }
 
-    private final Map<String, KernelWakelockStats> readKernelWakelockStats() {
-
-        FileInputStream is;
-        byte[] buffer = new byte[32*1024];
-        int len;
-        boolean wakeup_sources;
-
-        try {
-            try {
-                is = new FileInputStream("/d/wakeup_sources");
-                wakeup_sources = true;
-            } catch (java.io.FileNotFoundException e) {
-                try {
-                    is = new FileInputStream("/proc/wakelocks");
-                    wakeup_sources = false;
-                } catch (java.io.FileNotFoundException e2) {
-                    return null;
-                }
-            }
-
-            len = is.read(buffer);
-            is.close();
-        } catch (java.io.IOException e) {
-            return null;
-        }
-
-        if (len > 0) {
-            if (len >= buffer.length) {
-                Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length);
-            }
-            int i;
-            for (i=0; i<len; i++) {
-                if (buffer[i] == '\0') {
-                    len = i;
-                    break;
-                }
-            }
-        }
-
-        return parseProcWakelocks(buffer, len, wakeup_sources);
-    }
-
-    private final Map<String, KernelWakelockStats> parseProcWakelocks(
-            byte[] wlBuffer, int len, boolean wakeup_sources) {
-        String name;
-        int count;
-        long totalTime;
-        int startIndex;
-        int endIndex;
-        int numUpdatedWlNames = 0;
-
-        // Advance past the first line.
-        int i;
-        for (i = 0; i < len && wlBuffer[i] != '\n' && wlBuffer[i] != '\0'; i++);
-        startIndex = endIndex = i + 1;
-
-        synchronized(this) {
-            Map<String, KernelWakelockStats> m = mProcWakelockFileStats;
-
-            sKernelWakelockUpdateVersion++;
-            while (endIndex < len) {
-                for (endIndex=startIndex;
-                        endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0';
-                        endIndex++);
-                endIndex++; // endIndex is an exclusive upper bound.
-                // Don't go over the end of the buffer, Process.parseProcLine might
-                // write to wlBuffer[endIndex]
-                if (endIndex >= (len - 1) ) {
-                    return m;
-                }
-
-                String[] nameStringArray = mProcWakelocksName;
-                long[] wlData = mProcWakelocksData;
-                // Stomp out any bad characters since this is from a circular buffer
-                // A corruption is seen sometimes that results in the vm crashing
-                // This should prevent crashes and the line will probably fail to parse
-                for (int j = startIndex; j < endIndex; j++) {
-                    if ((wlBuffer[j] & 0x80) != 0) wlBuffer[j] = (byte) '?';
-                }
-                boolean parsed = Process.parseProcLine(wlBuffer, startIndex, endIndex,
-                        wakeup_sources ? WAKEUP_SOURCES_FORMAT :
-                                         PROC_WAKELOCKS_FORMAT,
-                        nameStringArray, wlData, null);
-
-                name = nameStringArray[0];
-                count = (int) wlData[1];
-
-                if (wakeup_sources) {
-                        // convert milliseconds to microseconds
-                        totalTime = wlData[2] * 1000;
-                } else {
-                        // convert nanoseconds to microseconds with rounding.
-                        totalTime = (wlData[2] + 500) / 1000;
-                }
-
-                if (parsed && name.length() > 0) {
-                    if (!m.containsKey(name)) {
-                        m.put(name, new KernelWakelockStats(count, totalTime,
-                                sKernelWakelockUpdateVersion));
-                        numUpdatedWlNames++;
-                    } else {
-                        KernelWakelockStats kwlStats = m.get(name);
-                        if (kwlStats.mVersion == sKernelWakelockUpdateVersion) {
-                            kwlStats.mCount += count;
-                            kwlStats.mTotalTime += totalTime;
-                        } else {
-                            kwlStats.mCount = count;
-                            kwlStats.mTotalTime = totalTime;
-                            kwlStats.mVersion = sKernelWakelockUpdateVersion;
-                            numUpdatedWlNames++;
-                        }
-                    }
-                }
-                startIndex = endIndex;
-            }
-
-            if (m.size() != numUpdatedWlNames) {
-                // Don't report old data.
-                Iterator<KernelWakelockStats> itr = m.values().iterator();
-                while (itr.hasNext()) {
-                    if (itr.next().mVersion != sKernelWakelockUpdateVersion) {
-                        itr.remove();
-                    }
-                }
-            }
-            return m;
-        }
-    }
-
-    private class KernelWakelockStats {
-        public int mCount;
-        public long mTotalTime;
-        public int mVersion;
-
-        KernelWakelockStats(int count, long totalTime, int version) {
-            mCount = count;
-            mTotalTime = totalTime;
-            mVersion = version;
-        }
-    }
-
     /*
      * Get the KernelWakelockTimer associated with name, and create a new one if one
      * doesn't already exist.
@@ -3387,7 +3208,7 @@
                 mMobileRadioActivePerAppTimer.startRunningLocked(elapsedRealtime);
             } else {
                 mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs);
-                updateNetworkActivityLocked(NET_UPDATE_MOBILE, realElapsedRealtimeMs);
+                updateMobileRadioStateLocked(realElapsedRealtimeMs);
                 mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs);
             }
         }
@@ -3417,9 +3238,26 @@
     }
 
     public void noteDeviceIdleModeLocked(boolean enabled, boolean fromActive, boolean fromMotion) {
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        boolean nowIdling = enabled;
+        if (mDeviceIdling && !enabled && !fromActive && !fromMotion) {
+            // We don't go out of general idling mode until explicitly taken out of
+            // device idle through going active or significant motion.
+            nowIdling = true;
+        }
+        if (mDeviceIdling != nowIdling) {
+            mDeviceIdling = nowIdling;
+            int stepState = nowIdling ? STEP_LEVEL_MODE_DEVICE_IDLE : 0;
+            mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_DEVICE_IDLE) ^ stepState;
+            mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_DEVICE_IDLE) | stepState;
+            if (enabled) {
+                mDeviceIdlingTimer.startRunningLocked(elapsedRealtime);
+            } else {
+                mDeviceIdlingTimer.stopRunningLocked(elapsedRealtime);
+            }
+        }
         if (mDeviceIdleModeEnabled != enabled) {
-            final long elapsedRealtime = SystemClock.elapsedRealtime();
-            final long uptime = SystemClock.uptimeMillis();
             mDeviceIdleModeEnabled = enabled;
             if (fromMotion) {
                 addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_SIGNIFICANT_MOTION,
@@ -3449,7 +3287,11 @@
         final long uptime = SystemClock.uptimeMillis();
         addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PACKAGE_INSTALLED,
                 pkgName, versionCode);
-        mNumConnectivityChange++;
+        PackageChange pc = new PackageChange();
+        pc.mPackageName = pkgName;
+        pc.mUpdate = true;
+        pc.mVersionCode = versionCode;
+        addPackageChange(pc);
     }
 
     public void notePackageUninstalledLocked(String pkgName) {
@@ -3457,7 +3299,17 @@
         final long uptime = SystemClock.uptimeMillis();
         addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PACKAGE_UNINSTALLED,
                 pkgName, 0);
-        mNumConnectivityChange++;
+        PackageChange pc = new PackageChange();
+        pc.mPackageName = pkgName;
+        pc.mUpdate = true;
+        addPackageChange(pc);
+    }
+
+    private void addPackageChange(PackageChange pc) {
+        if (mDailyPackageChanges == null) {
+            mDailyPackageChanges = new ArrayList<>();
+        }
+        mDailyPackageChanges.add(pc);
     }
 
     public void notePhoneOnLocked() {
@@ -3694,6 +3546,7 @@
             addHistoryRecordLocked(elapsedRealtime, uptime);
             mWifiOn = true;
             mWifiOnTimer.startRunningLocked(elapsedRealtime);
+            scheduleSyncExternalStatsLocked();
         }
     }
 
@@ -3707,6 +3560,7 @@
             addHistoryRecordLocked(elapsedRealtime, uptime);
             mWifiOn = false;
             mWifiOnTimer.stopRunningLocked(elapsedRealtime);
+            scheduleSyncExternalStatsLocked();
         }
     }
 
@@ -3869,6 +3723,7 @@
                 int uid = mapUid(ws.get(i));
                 getUidStatsLocked(uid).noteWifiRunningLocked(elapsedRealtime);
             }
+            scheduleSyncExternalStatsLocked();
         } else {
             Log.w(TAG, "noteWifiRunningLocked -- called while WIFI running");
         }
@@ -3907,6 +3762,7 @@
                 int uid = mapUid(ws.get(i));
                 getUidStatsLocked(uid).noteWifiStoppedLocked(elapsedRealtime);
             }
+            scheduleSyncExternalStatsLocked();
         } else {
             Log.w(TAG, "noteWifiStoppedLocked -- called while WIFI not running");
         }
@@ -3921,6 +3777,7 @@
             }
             mWifiState = wifiState;
             mWifiStateTimer[wifiState].startRunningLocked(elapsedRealtime);
+            scheduleSyncExternalStatsLocked();
         }
     }
 
@@ -3992,6 +3849,7 @@
             addHistoryRecordLocked(elapsedRealtime, uptime);
             mBluetoothOn = true;
             mBluetoothOnTimer.startRunningLocked(elapsedRealtime);
+            scheduleSyncExternalStatsLocked();
         }
     }
 
@@ -4005,6 +3863,7 @@
             addHistoryRecordLocked(elapsedRealtime, uptime);
             mBluetoothOn = false;
             mBluetoothOnTimer.stopRunningLocked(elapsedRealtime);
+            scheduleSyncExternalStatsLocked();
         }
     }
 
@@ -4031,6 +3890,7 @@
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
+            scheduleSyncExternalStatsLocked();
         }
         mWifiFullLockNesting++;
         getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked(elapsedRealtime);
@@ -4046,6 +3906,7 @@
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
+            scheduleSyncExternalStatsLocked();
         }
         getUidStatsLocked(uid).noteFullWifiLockReleasedLocked(elapsedRealtime);
     }
@@ -4103,6 +3964,7 @@
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
+            scheduleSyncExternalStatsLocked();
         }
         mWifiMulticastNesting++;
         getUidStatsLocked(uid).noteWifiMulticastEnabledLocked(elapsedRealtime);
@@ -4118,6 +3980,7 @@
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
+            scheduleSyncExternalStatsLocked();
         }
         getUidStatsLocked(uid).noteWifiMulticastDisabledLocked(elapsedRealtime);
     }
@@ -4225,7 +4088,8 @@
         // During device boot, qtaguid isn't enabled until after the inital
         // loading of battery stats. Now that they're enabled, take our initial
         // snapshot for future delta calculation.
-        updateNetworkActivityLocked(NET_UPDATE_ALL, SystemClock.elapsedRealtime());
+        updateMobileRadioStateLocked(SystemClock.elapsedRealtime());
+        updateWifiStateLocked(null);
     }
 
     @Override public long getScreenOnTime(long elapsedRealtimeUs, int which) {
@@ -4262,6 +4126,14 @@
         return mDeviceIdleModeEnabledTimer.getCountLocked(which);
     }
 
+    @Override public long getDeviceIdlingTime(long elapsedRealtimeUs, int which) {
+        return mDeviceIdlingTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+    }
+
+    @Override public int getDeviceIdlingCount(int which) {
+        return mDeviceIdlingTimer.getCountLocked(which);
+    }
+
     @Override public int getNumConnectivityChange(int which) {
         int val = mNumConnectivityChange;
         if (which == STATS_CURRENT) {
@@ -4564,17 +4436,17 @@
         }
 
         @Override
-        public Map<String, ? extends BatteryStats.Uid.Wakelock> getWakelockStats() {
+        public ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> getWakelockStats() {
             return mWakelockStats.getMap();
         }
 
         @Override
-        public Map<String, ? extends BatteryStats.Timer> getSyncStats() {
+        public ArrayMap<String, ? extends BatteryStats.Timer> getSyncStats() {
             return mSyncStats.getMap();
         }
 
         @Override
-        public Map<String, ? extends BatteryStats.Timer> getJobStats() {
+        public ArrayMap<String, ? extends BatteryStats.Timer> getJobStats() {
             return mJobStats.getMap();
         }
 
@@ -4584,12 +4456,12 @@
         }
 
         @Override
-        public Map<String, ? extends BatteryStats.Uid.Proc> getProcessStats() {
+        public ArrayMap<String, ? extends BatteryStats.Uid.Proc> getProcessStats() {
             return mProcessStats;
         }
 
         @Override
-        public Map<String, ? extends BatteryStats.Uid.Pkg> getPackageStats() {
+        public ArrayMap<String, ? extends BatteryStats.Uid.Pkg> getPackageStats() {
             return mPackageStats;
         }
 
@@ -5907,7 +5779,7 @@
                     Slog.w(TAG, "File corrupt: too many excessive power entries " + N);
                     return false;
                 }
-                
+
                 mExcessivePower = new ArrayList<ExcessivePower>();
                 for (int i=0; i<N; i++) {
                     ExcessivePower ew = new ExcessivePower();
@@ -6110,40 +5982,20 @@
          */
         public final class Pkg extends BatteryStats.Uid.Pkg implements TimeBaseObs {
             /**
-             * Number of times this package has done something that could wake up the
-             * device from sleep.
+             * Number of times wakeup alarms have occurred for this app.
              */
-            int mWakeups;
-
-            /**
-             * Number of things that could wake up the device loaded from a
-             * previous save.
-             */
-            int mLoadedWakeups;
-
-            /**
-             * Number of things that could wake up the device as of the
-             * last run.
-             */
-            int mLastWakeups;
-
-            /**
-             * Number of things that could wake up the device as of the
-             * last run.
-             */
-            int mUnpluggedWakeups;
+            ArrayMap<String, Counter> mWakeupAlarms = new ArrayMap<>();
 
             /**
              * The statics we have collected for this package's services.
              */
-            final HashMap<String, Serv> mServiceStats = new HashMap<String, Serv>();
+            final ArrayMap<String, Serv> mServiceStats = new ArrayMap<>();
 
             Pkg() {
                 mOnBatteryScreenOffTimeBase.add(this);
             }
 
             public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) {
-                mUnpluggedWakeups = mWakeups;
             }
 
             public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) {
@@ -6154,10 +6006,12 @@
             }
 
             void readFromParcelLocked(Parcel in) {
-                mWakeups = in.readInt();
-                mLoadedWakeups = in.readInt();
-                mLastWakeups = 0;
-                mUnpluggedWakeups = in.readInt();
+                int numWA = in.readInt();
+                mWakeupAlarms.clear();
+                for (int i=0; i<numWA; i++) {
+                    String tag = in.readString();
+                    mWakeupAlarms.put(tag, new Counter(mOnBatteryTimeBase, in));
+                }
 
                 int numServs = in.readInt();
                 mServiceStats.clear();
@@ -6171,34 +6025,39 @@
             }
 
             void writeToParcelLocked(Parcel out) {
-                out.writeInt(mWakeups);
-                out.writeInt(mLoadedWakeups);
-                out.writeInt(mUnpluggedWakeups);
+                int numWA = mWakeupAlarms.size();
+                out.writeInt(numWA);
+                for (int i=0; i<numWA; i++) {
+                    out.writeString(mWakeupAlarms.keyAt(i));
+                    mWakeupAlarms.valueAt(i).writeToParcel(out);
+                }
 
-                out.writeInt(mServiceStats.size());
-                for (Map.Entry<String, Uid.Pkg.Serv> servEntry : mServiceStats.entrySet()) {
-                    out.writeString(servEntry.getKey());
-                    Uid.Pkg.Serv serv = servEntry.getValue();
-
+                final int NS = mServiceStats.size();
+                out.writeInt(NS);
+                for (int i=0; i<NS; i++) {
+                    out.writeString(mServiceStats.keyAt(i));
+                    Uid.Pkg.Serv serv = mServiceStats.valueAt(i);
                     serv.writeToParcelLocked(out);
                 }
             }
 
             @Override
-            public Map<String, ? extends BatteryStats.Uid.Pkg.Serv> getServiceStats() {
-                return mServiceStats;
+            public ArrayMap<String, ? extends BatteryStats.Counter> getWakeupAlarmStats() {
+                return mWakeupAlarms;
+            }
+
+            public void noteWakeupAlarmLocked(String tag) {
+                Counter c = mWakeupAlarms.get(tag);
+                if (c == null) {
+                    c = new Counter(mOnBatteryTimeBase);
+                    mWakeupAlarms.put(tag, c);
+                }
+                c.stepAtomic();
             }
 
             @Override
-            public int getWakeups(int which) {
-                int val = mWakeups;
-                if (which == STATS_CURRENT) {
-                    val -= mLoadedWakeups;
-                } else if (which == STATS_SINCE_UNPLUGGED) {
-                    val -= mUnpluggedWakeups;
-                }
-
-                return val;
+            public ArrayMap<String, ? extends BatteryStats.Uid.Pkg.Serv> getServiceStats() {
+                return mServiceStats;
             }
 
             /**
@@ -6440,14 +6299,6 @@
                 }
             }
 
-            public BatteryStatsImpl getBatteryStats() {
-                return BatteryStatsImpl.this;
-            }
-
-            public void incWakeupsLocked() {
-                mWakeups++;
-            }
-
             final Serv newServiceStatsLocked() {
                 return new Serv();
             }
@@ -6706,7 +6557,7 @@
         }
     }
 
-    public BatteryStatsImpl(File systemDir, Handler handler) {
+    public BatteryStatsImpl(File systemDir, Handler handler, ExternalStatsSync externalSync) {
         if (systemDir != null) {
             mFile = new JournaledFile(new File(systemDir, "batterystats.bin"),
                     new File(systemDir, "batterystats.bin.tmp"));
@@ -6715,6 +6566,7 @@
         }
         mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin"));
         mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml"));
+        mExternalSync = externalSync;
         mHandler = new MyHandler(handler.getLooper());
         mStartCount++;
         mScreenOnTimer = new StopwatchTimer(null, -1, null, mOnBatteryTimeBase);
@@ -6724,6 +6576,7 @@
         mInteractiveTimer = new StopwatchTimer(null, -10, null, mOnBatteryTimeBase);
         mPowerSaveModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase);
         mDeviceIdleModeEnabledTimer = new StopwatchTimer(null, -11, null, mOnBatteryTimeBase);
+        mDeviceIdlingTimer = new StopwatchTimer(null, -12, null, mOnBatteryTimeBase);
         mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase);
         for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
             mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i, null,
@@ -6786,6 +6639,7 @@
         mCheckinFile = null;
         mDailyFile = null;
         mHandler = null;
+        mExternalSync = null;
         clearHistoryLocked();
         readFromParcel(p);
     }
@@ -6849,6 +6703,11 @@
                     mDailyChargeStepTracker.mNumStepDurations,
                     mDailyChargeStepTracker.mStepDurations);
         }
+        if (mDailyPackageChanges != null) {
+            hasData = true;
+            item.mPackageChanges = mDailyPackageChanges;
+            mDailyPackageChanges = null;
+        }
         mDailyDischargeStepTracker.init();
         mDailyChargeStepTracker.init();
         updateDailyDeadlineLocked();
@@ -6899,6 +6758,21 @@
             out.attribute(null, "end", Long.toString(dit.mEndTime));
             writeDailyLevelSteps(out, "dis", dit.mDischargeSteps, sb);
             writeDailyLevelSteps(out, "chg", dit.mChargeSteps, sb);
+            if (dit.mPackageChanges != null) {
+                for (int j=0; j<dit.mPackageChanges.size(); j++) {
+                    PackageChange pc = dit.mPackageChanges.get(j);
+                    if (pc.mUpdate) {
+                        out.startTag(null, "upd");
+                        out.attribute(null, "pkg", pc.mPackageName);
+                        out.attribute(null, "ver", Integer.toString(pc.mVersionCode));
+                        out.endTag(null, "upd");
+                    } else {
+                        out.startTag(null, "rem");
+                        out.attribute(null, "pkg", pc.mPackageName);
+                        out.endTag(null, "rem");
+                    }
+                }
+            }
             out.endTag(null, "item");
         }
         out.endTag(null, "daily-items");
@@ -7011,6 +6885,26 @@
                 readDailyItemTagDetailsLocked(parser, dit, false, "dis");
             } else if (tagName.equals("chg")) {
                 readDailyItemTagDetailsLocked(parser, dit, true, "chg");
+            } else if (tagName.equals("upd")) {
+                if (dit.mPackageChanges == null) {
+                    dit.mPackageChanges = new ArrayList<>();
+                }
+                PackageChange pc = new PackageChange();
+                pc.mUpdate = true;
+                pc.mPackageName = parser.getAttributeValue(null, "pkg");
+                String verStr = parser.getAttributeValue(null, "ver");
+                pc.mVersionCode = verStr != null ? Integer.parseInt(verStr) : 0;
+                dit.mPackageChanges.add(pc);
+                XmlUtils.skipCurrentTag(parser);
+            } else if (tagName.equals("rem")) {
+                if (dit.mPackageChanges == null) {
+                    dit.mPackageChanges = new ArrayList<>();
+                }
+                PackageChange pc = new PackageChange();
+                pc.mUpdate = false;
+                pc.mPackageName = parser.getAttributeValue(null, "pkg");
+                dit.mPackageChanges.add(pc);
+                XmlUtils.skipCurrentTag(parser);
             } else {
                 Slog.w(TAG, "Unknown element under <item>: "
                         + parser.getName());
@@ -7295,6 +7189,7 @@
         mInteractiveTimer.reset(false);
         mPowerSaveModeEnabledTimer.reset(false);
         mDeviceIdleModeEnabledTimer.reset(false);
+        mDeviceIdlingTimer.reset(false);
         mPhoneOnTimer.reset(false);
         mAudioOnTimer.reset(false);
         mVideoOnTimer.reset(false);
@@ -7416,21 +7311,233 @@
             mDischargeScreenOffUnplugLevel = mDischargeCurrentLevel;
         }
     }
-    
+
     public void pullPendingStateUpdatesLocked() {
-        updateKernelWakelocksLocked();
-        updateNetworkActivityLocked(NET_UPDATE_ALL, SystemClock.elapsedRealtime());
-        // TODO(adamlesinski): enable when bluedroid stops deadlocking. b/19248786
-        // updateBluetoothControllerActivityLocked();
-        // TODO(adamlesinski): disabled to avoid deadlock. Need to change how external
-        // data is pulled/accessed from BatteryStats. b/19729960
-        // updateWifiControllerActivityLocked();
         if (mOnBatteryInternal) {
             final boolean screenOn = mScreenState == Display.STATE_ON;
             updateDischargeScreenLevelsLocked(screenOn, screenOn);
         }
     }
 
+    private String[] mMobileIfaces = EmptyArray.STRING;
+    private String[] mWifiIfaces = EmptyArray.STRING;
+
+    private final NetworkStatsFactory mNetworkStatsFactory = new NetworkStatsFactory();
+
+    private static final int NETWORK_STATS_LAST = 0;
+    private static final int NETWORK_STATS_NEXT = 1;
+    private static final int NETWORK_STATS_DELTA = 2;
+
+    private final NetworkStats[] mMobileNetworkStats = new NetworkStats[] {
+            new NetworkStats(SystemClock.elapsedRealtime(), 50),
+            new NetworkStats(SystemClock.elapsedRealtime(), 50),
+            new NetworkStats(SystemClock.elapsedRealtime(), 50)
+    };
+
+    private final NetworkStats[] mWifiNetworkStats = new NetworkStats[] {
+            new NetworkStats(SystemClock.elapsedRealtime(), 50),
+            new NetworkStats(SystemClock.elapsedRealtime(), 50),
+            new NetworkStats(SystemClock.elapsedRealtime(), 50)
+    };
+
+    /**
+     * Retrieves the delta of network stats for the given network ifaces. Uses networkStatsBuffer
+     * as a buffer of NetworkStats objects to cycle through when computing deltas.
+     */
+    private NetworkStats getNetworkStatsDeltaLocked(String[] ifaces,
+                                                    NetworkStats[] networkStatsBuffer)
+            throws IOException {
+        if (!SystemProperties.getBoolean(NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED,
+                false)) {
+            return null;
+        }
+
+        final NetworkStats stats = mNetworkStatsFactory.readNetworkStatsDetail(NetworkStats.UID_ALL,
+                ifaces, NetworkStats.TAG_NONE, networkStatsBuffer[NETWORK_STATS_NEXT]);
+        networkStatsBuffer[NETWORK_STATS_DELTA] = NetworkStats.subtract(stats,
+                networkStatsBuffer[NETWORK_STATS_LAST], null, null,
+                networkStatsBuffer[NETWORK_STATS_DELTA]);
+        networkStatsBuffer[NETWORK_STATS_NEXT] = networkStatsBuffer[NETWORK_STATS_LAST];
+        networkStatsBuffer[NETWORK_STATS_LAST] = stats;
+        return networkStatsBuffer[NETWORK_STATS_DELTA];
+    }
+
+    /**
+     * Distribute WiFi energy info and network traffic to apps.
+     * @param info The energy information from the WiFi controller.
+     */
+    public void updateWifiStateLocked(@Nullable final WifiActivityEnergyInfo info) {
+        final NetworkStats delta;
+        try {
+            delta = getNetworkStatsDeltaLocked(mWifiIfaces, mWifiNetworkStats);
+        } catch (IOException e) {
+            Slog.wtf(TAG, "Failed to get wifi network stats", e);
+            return;
+        }
+
+        if (!mOnBatteryInternal) {
+            return;
+        }
+
+        if (delta != null) {
+            final int size = delta.size();
+            for (int i = 0; i < size; i++) {
+                final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
+
+                if (DEBUG) {
+                    Slog.d(TAG, "Wifi uid " + entry.uid + ": delta rx=" + entry.rxBytes
+                            + " tx=" + entry.txBytes);
+                }
+
+                if (entry.rxBytes == 0 || entry.txBytes == 0) {
+                    continue;
+                }
+
+                final Uid u = getUidStatsLocked(mapUid(entry.uid));
+                u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes,
+                        entry.rxPackets);
+                u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes,
+                        entry.txPackets);
+
+                mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
+                        entry.rxBytes);
+                mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
+                        entry.txBytes);
+                mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
+                        entry.rxPackets);
+                mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
+                        entry.txPackets);
+            }
+        }
+
+        if (info != null) {
+            // Update WiFi controller stats.
+            mWifiActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
+                    info.getControllerRxTimeMillis());
+            mWifiActivityCounters[CONTROLLER_TX_TIME].addCountLocked(
+                    info.getControllerTxTimeMillis());
+            mWifiActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
+                    info.getControllerIdleTimeMillis());
+            mWifiActivityCounters[CONTROLLER_ENERGY].addCountLocked(
+                    info.getControllerEnergyUsed());
+        }
+    }
+
+    /**
+     * Distribute Cell radio energy info and network traffic to apps.
+     */
+    public void updateMobileRadioStateLocked(long elapsedRealtimeMs) {
+        final NetworkStats delta;
+
+        try {
+            delta = getNetworkStatsDeltaLocked(mMobileIfaces, mMobileNetworkStats);
+        } catch (IOException e) {
+            Slog.wtf(TAG, "Failed to get mobile network stats", e);
+            return;
+        }
+
+        if (delta == null || !mOnBatteryInternal) {
+            return;
+        }
+
+        long radioTime = mMobileRadioActivePerAppTimer.checkpointRunningLocked(elapsedRealtimeMs);
+        long totalPackets = delta.getTotalPackets();
+
+        final int size = delta.size();
+        for (int i = 0; i < size; i++) {
+            final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
+
+            if (entry.rxBytes == 0 || entry.txBytes == 0) continue;
+
+            final Uid u = getUidStatsLocked(mapUid(entry.uid));
+            u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_DATA, entry.rxBytes,
+                    entry.rxPackets);
+            u.noteNetworkActivityLocked(NETWORK_MOBILE_TX_DATA, entry.txBytes,
+                    entry.txPackets);
+
+            if (radioTime > 0) {
+                // Distribute total radio active time in to this app.
+                long appPackets = entry.rxPackets + entry.txPackets;
+                long appRadioTime = (radioTime*appPackets)/totalPackets;
+                u.noteMobileRadioActiveTimeLocked(appRadioTime);
+                // Remove this app from the totals, so that we don't lose any time
+                // due to rounding.
+                radioTime -= appRadioTime;
+                totalPackets -= appPackets;
+            }
+
+            mNetworkByteActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
+                    entry.rxBytes);
+            mNetworkByteActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
+                    entry.txBytes);
+            mNetworkPacketActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
+                    entry.rxPackets);
+            mNetworkPacketActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
+                    entry.txPackets);
+        }
+
+        if (radioTime > 0) {
+            // Whoops, there is some radio time we can't blame on an app!
+            mMobileRadioActiveUnknownTime.addCountLocked(radioTime);
+            mMobileRadioActiveUnknownCount.addCountLocked(1);
+        }
+    }
+
+    /**
+     * Distribute Bluetooth energy info and network traffic to apps.
+     * @param info The energy information from the bluetooth controller.
+     */
+    public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info) {
+        if (info != null && mOnBatteryInternal) {
+            mBluetoothActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
+                    info.getControllerRxTimeMillis());
+            mBluetoothActivityCounters[CONTROLLER_TX_TIME].addCountLocked(
+                    info.getControllerTxTimeMillis());
+            mBluetoothActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
+                    info.getControllerIdleTimeMillis());
+            mBluetoothActivityCounters[CONTROLLER_ENERGY].addCountLocked(
+                    info.getControllerEnergyUsed());
+        }
+    }
+
+    /**
+     * Read and distribute kernel wake lock use across apps.
+     */
+    public void updateKernelWakelocksLocked() {
+        final KernelWakelockStats wakelockStats = mKernelWakelockReader.readKernelWakelockStats(
+                mTmpWakelockStats);
+        if (wakelockStats == null) {
+            // Not crashing might make board bringup easier.
+            Slog.w(TAG, "Couldn't get kernel wake lock stats");
+            return;
+        }
+
+        for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) {
+            String name = ent.getKey();
+            KernelWakelockStats.Entry kws = ent.getValue();
+
+            SamplingTimer kwlt = mKernelWakelockStats.get(name);
+            if (kwlt == null) {
+                kwlt = new SamplingTimer(mOnBatteryScreenOffTimeBase,
+                        true /* track reported val */);
+                mKernelWakelockStats.put(name, kwlt);
+            }
+            kwlt.updateCurrentReportedCount(kws.mCount);
+            kwlt.updateCurrentReportedTotalTime(kws.mTotalTime);
+            kwlt.setUpdateVersion(kws.mVersion);
+        }
+
+        if (wakelockStats.size() != mKernelWakelockStats.size()) {
+            // Set timers to stale if they didn't appear in /proc/wakelocks this time.
+            for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
+                SamplingTimer st = ent.getValue();
+                if (st.getUpdateVersion() != wakelockStats.kernelWakelockVersion) {
+                    st.setStale();
+                }
+            }
+        }
+    }
+
     void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery,
             final int oldStatus, final int level) {
         boolean doWrite = false;
@@ -7584,340 +7691,132 @@
         }
     }
 
+    private void scheduleSyncExternalStatsLocked() {
+        if (mExternalSync != null) {
+            mExternalSync.scheduleSync();
+        }
+    }
+
     // This should probably be exposed in the API, though it's not critical
-    private static final int BATTERY_PLUGGED_NONE = 0;
+    public static final int BATTERY_PLUGGED_NONE = 0;
 
-    public void setBatteryState(int status, int health, int plugType, int level,
+    public void setBatteryStateLocked(int status, int health, int plugType, int level,
             int temp, int volt) {
-        synchronized(this) {
-            final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
-            final long uptime = SystemClock.uptimeMillis();
-            final long elapsedRealtime = SystemClock.elapsedRealtime();
-            if (!mHaveBatteryLevel) {
-                mHaveBatteryLevel = true;
-                // We start out assuming that the device is plugged in (not
-                // on battery).  If our first report is now that we are indeed
-                // plugged in, then twiddle our state to correctly reflect that
-                // since we won't be going through the full setOnBattery().
-                if (onBattery == mOnBattery) {
-                    if (onBattery) {
-                        mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
-                    } else {
-                        mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
-                    }
-                }
-                mHistoryCur.batteryStatus = (byte)status;
-                mHistoryCur.batteryLevel = (byte)level;
-                mMaxChargeStepLevel = mMinDischargeStepLevel =
-                        mLastChargeStepLevel = mLastDischargeStepLevel = level;
-            } else if (mCurrentBatteryLevel != level || mOnBattery != onBattery) {
-                recordDailyStatsIfNeededLocked(level >= 100 && onBattery);
-            }
-            int oldStatus = mHistoryCur.batteryStatus;
-            if (onBattery) {
-                mDischargeCurrentLevel = level;
-                if (!mRecordingHistory) {
-                    mRecordingHistory = true;
-                    startRecordingHistory(elapsedRealtime, uptime, true);
-                }
-            } else if (level < 96) {
-                if (!mRecordingHistory) {
-                    mRecordingHistory = true;
-                    startRecordingHistory(elapsedRealtime, uptime, true);
-                }
-            }
-            mCurrentBatteryLevel = level;
-            if (mDischargePlugLevel < 0) {
-                mDischargePlugLevel = level;
-            }
-            if (onBattery != mOnBattery) {
-                mHistoryCur.batteryLevel = (byte)level;
-                mHistoryCur.batteryStatus = (byte)status;
-                mHistoryCur.batteryHealth = (byte)health;
-                mHistoryCur.batteryPlugType = (byte)plugType;
-                mHistoryCur.batteryTemperature = (short)temp;
-                mHistoryCur.batteryVoltage = (char)volt;
-                setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level);
-            } else {
-                boolean changed = false;
-                if (mHistoryCur.batteryLevel != level) {
-                    mHistoryCur.batteryLevel = (byte)level;
-                    changed = true;
-                }
-                if (mHistoryCur.batteryStatus != status) {
-                    mHistoryCur.batteryStatus = (byte)status;
-                    changed = true;
-                }
-                if (mHistoryCur.batteryHealth != health) {
-                    mHistoryCur.batteryHealth = (byte)health;
-                    changed = true;
-                }
-                if (mHistoryCur.batteryPlugType != plugType) {
-                    mHistoryCur.batteryPlugType = (byte)plugType;
-                    changed = true;
-                }
-                if (temp >= (mHistoryCur.batteryTemperature+10)
-                        || temp <= (mHistoryCur.batteryTemperature-10)) {
-                    mHistoryCur.batteryTemperature = (short)temp;
-                    changed = true;
-                }
-                if (volt > (mHistoryCur.batteryVoltage+20)
-                        || volt < (mHistoryCur.batteryVoltage-20)) {
-                    mHistoryCur.batteryVoltage = (char)volt;
-                    changed = true;
-                }
-                if (changed) {
-                    addHistoryRecordLocked(elapsedRealtime, uptime);
-                }
-                long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT)
-                        | (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT)
-                        | (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT);
+        final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
+        final long uptime = SystemClock.uptimeMillis();
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        if (!mHaveBatteryLevel) {
+            mHaveBatteryLevel = true;
+            // We start out assuming that the device is plugged in (not
+            // on battery).  If our first report is now that we are indeed
+            // plugged in, then twiddle our state to correctly reflect that
+            // since we won't be going through the full setOnBattery().
+            if (onBattery == mOnBattery) {
                 if (onBattery) {
-                    if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) {
-                        mDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
-                                modeBits, elapsedRealtime);
-                        mDailyDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
-                                modeBits, elapsedRealtime);
-                        mLastDischargeStepLevel = level;
-                        mMinDischargeStepLevel = level;
-                        mInitStepMode = mCurStepMode;
-                        mModStepMode = 0;
-                    }
+                    mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
                 } else {
-                    if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {
-                        mChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
-                                modeBits, elapsedRealtime);
-                        mDailyChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
-                                modeBits, elapsedRealtime);
-                        mLastChargeStepLevel = level;
-                        mMaxChargeStepLevel = level;
-                        mInitStepMode = mCurStepMode;
-                        mModStepMode = 0;
-                    }
+                    mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
                 }
             }
-            if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
-                // We don't record history while we are plugged in and fully charged.
-                // The next time we are unplugged, history will be cleared.
-                mRecordingHistory = DEBUG;
+            mHistoryCur.batteryStatus = (byte)status;
+            mHistoryCur.batteryLevel = (byte)level;
+            mMaxChargeStepLevel = mMinDischargeStepLevel =
+                    mLastChargeStepLevel = mLastDischargeStepLevel = level;
+        } else if (mCurrentBatteryLevel != level || mOnBattery != onBattery) {
+            recordDailyStatsIfNeededLocked(level >= 100 && onBattery);
+        }
+        int oldStatus = mHistoryCur.batteryStatus;
+        if (onBattery) {
+            mDischargeCurrentLevel = level;
+            if (!mRecordingHistory) {
+                mRecordingHistory = true;
+                startRecordingHistory(elapsedRealtime, uptime, true);
+            }
+        } else if (level < 96) {
+            if (!mRecordingHistory) {
+                mRecordingHistory = true;
+                startRecordingHistory(elapsedRealtime, uptime, true);
             }
         }
-    }
-
-    public void updateKernelWakelocksLocked() {
-        Map<String, KernelWakelockStats> m = readKernelWakelockStats();
-
-        if (m == null) {
-            // Not crashing might make board bringup easier.
-            Slog.w(TAG, "Couldn't get kernel wake lock stats");
-            return;
+        mCurrentBatteryLevel = level;
+        if (mDischargePlugLevel < 0) {
+            mDischargePlugLevel = level;
         }
+        if (onBattery != mOnBattery) {
+            mHistoryCur.batteryLevel = (byte)level;
+            mHistoryCur.batteryStatus = (byte)status;
+            mHistoryCur.batteryHealth = (byte)health;
+            mHistoryCur.batteryPlugType = (byte)plugType;
+            mHistoryCur.batteryTemperature = (short)temp;
+            mHistoryCur.batteryVoltage = (char)volt;
+            setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level);
+        } else {
+            boolean changed = false;
+            if (mHistoryCur.batteryLevel != level) {
+                mHistoryCur.batteryLevel = (byte)level;
+                changed = true;
 
-        for (Map.Entry<String, KernelWakelockStats> ent : m.entrySet()) {
-            String name = ent.getKey();
-            KernelWakelockStats kws = ent.getValue();
-
-            SamplingTimer kwlt = mKernelWakelockStats.get(name);
-            if (kwlt == null) {
-                kwlt = new SamplingTimer(mOnBatteryScreenOffTimeBase,
-                        true /* track reported val */);
-                mKernelWakelockStats.put(name, kwlt);
+                // TODO(adamlesinski): Schedule the creation of a HistoryStepDetails record
+                // which will pull external stats.
+                scheduleSyncExternalStatsLocked();
             }
-            kwlt.updateCurrentReportedCount(kws.mCount);
-            kwlt.updateCurrentReportedTotalTime(kws.mTotalTime);
-            kwlt.setUpdateVersion(sKernelWakelockUpdateVersion);
-        }
-
-        if (m.size() != mKernelWakelockStats.size()) {
-            // Set timers to stale if they didn't appear in /proc/wakelocks this time.
-            for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
-                SamplingTimer st = ent.getValue();
-                if (st.getUpdateVersion() != sKernelWakelockUpdateVersion) {
-                    st.setStale();
+            if (mHistoryCur.batteryStatus != status) {
+                mHistoryCur.batteryStatus = (byte)status;
+                changed = true;
+            }
+            if (mHistoryCur.batteryHealth != health) {
+                mHistoryCur.batteryHealth = (byte)health;
+                changed = true;
+            }
+            if (mHistoryCur.batteryPlugType != plugType) {
+                mHistoryCur.batteryPlugType = (byte)plugType;
+                changed = true;
+            }
+            if (temp >= (mHistoryCur.batteryTemperature+10)
+                    || temp <= (mHistoryCur.batteryTemperature-10)) {
+                mHistoryCur.batteryTemperature = (short)temp;
+                changed = true;
+            }
+            if (volt > (mHistoryCur.batteryVoltage+20)
+                    || volt < (mHistoryCur.batteryVoltage-20)) {
+                mHistoryCur.batteryVoltage = (char)volt;
+                changed = true;
+            }
+            if (changed) {
+                addHistoryRecordLocked(elapsedRealtime, uptime);
+            }
+            long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT)
+                    | (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT)
+                    | (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT);
+            if (onBattery) {
+                if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) {
+                    mDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
+                            modeBits, elapsedRealtime);
+                    mDailyDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
+                            modeBits, elapsedRealtime);
+                    mLastDischargeStepLevel = level;
+                    mMinDischargeStepLevel = level;
+                    mInitStepMode = mCurStepMode;
+                    mModStepMode = 0;
+                }
+            } else {
+                if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {
+                    mChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
+                            modeBits, elapsedRealtime);
+                    mDailyChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
+                            modeBits, elapsedRealtime);
+                    mLastChargeStepLevel = level;
+                    mMaxChargeStepLevel = level;
+                    mInitStepMode = mCurStepMode;
+                    mModStepMode = 0;
                 }
             }
         }
-    }
-
-    static final int NET_UPDATE_MOBILE = 1<<0;
-    static final int NET_UPDATE_WIFI = 1<<1;
-    static final int NET_UPDATE_ALL = 0xffff;
-
-    private void updateNetworkActivityLocked(int which, long elapsedRealtimeMs) {
-        if (!SystemProperties.getBoolean(PROP_QTAGUID_ENABLED, false)) return;
-
-        if ((which&NET_UPDATE_MOBILE) != 0 && mMobileIfaces.length > 0) {
-            final NetworkStats snapshot;
-            final NetworkStats last = mCurMobileSnapshot;
-            try {
-                snapshot = mNetworkStatsFactory.readNetworkStatsDetail(UID_ALL,
-                        mMobileIfaces, NetworkStats.TAG_NONE, mLastMobileSnapshot);
-            } catch (IOException e) {
-                Log.wtf(TAG, "Failed to read mobile network stats", e);
-                return;
-            }
-
-            mCurMobileSnapshot = snapshot;
-            mLastMobileSnapshot = last;
-
-            if (mOnBatteryInternal) {
-                final NetworkStats delta = NetworkStats.subtract(snapshot, last,
-                        null, null, mTmpNetworkStats);
-                mTmpNetworkStats = delta;
-
-                long radioTime = mMobileRadioActivePerAppTimer.checkpointRunningLocked(
-                        elapsedRealtimeMs);
-                long totalPackets = delta.getTotalPackets();
-
-                final int size = delta.size();
-                for (int i = 0; i < size; i++) {
-                    final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
-
-                    if (entry.rxBytes == 0 || entry.txBytes == 0) continue;
-
-                    final Uid u = getUidStatsLocked(mapUid(entry.uid));
-                    u.noteNetworkActivityLocked(NETWORK_MOBILE_RX_DATA, entry.rxBytes,
-                            entry.rxPackets);
-                    u.noteNetworkActivityLocked(NETWORK_MOBILE_TX_DATA, entry.txBytes,
-                            entry.txPackets);
-
-                    if (radioTime > 0) {
-                        // Distribute total radio active time in to this app.
-                        long appPackets = entry.rxPackets + entry.txPackets;
-                        long appRadioTime = (radioTime*appPackets)/totalPackets;
-                        u.noteMobileRadioActiveTimeLocked(appRadioTime);
-                        // Remove this app from the totals, so that we don't lose any time
-                        // due to rounding.
-                        radioTime -= appRadioTime;
-                        totalPackets -= appPackets;
-                    }
-
-                    mNetworkByteActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
-                            entry.rxBytes);
-                    mNetworkByteActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
-                            entry.txBytes);
-                    mNetworkPacketActivityCounters[NETWORK_MOBILE_RX_DATA].addCountLocked(
-                            entry.rxPackets);
-                    mNetworkPacketActivityCounters[NETWORK_MOBILE_TX_DATA].addCountLocked(
-                            entry.txPackets);
-                }
-
-                if (radioTime > 0) {
-                    // Whoops, there is some radio time we can't blame on an app!
-                    mMobileRadioActiveUnknownTime.addCountLocked(radioTime);
-                    mMobileRadioActiveUnknownCount.addCountLocked(1);
-                }
-            }
+        if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
+            // We don't record history while we are plugged in and fully charged.
+            // The next time we are unplugged, history will be cleared.
+            mRecordingHistory = DEBUG;
         }
-
-        if ((which&NET_UPDATE_WIFI) != 0 && mWifiIfaces.length > 0) {
-            final NetworkStats snapshot;
-            final NetworkStats last = mCurWifiSnapshot;
-            try {
-                snapshot = mNetworkStatsFactory.readNetworkStatsDetail(UID_ALL,
-                        mWifiIfaces, NetworkStats.TAG_NONE, mLastWifiSnapshot);
-            } catch (IOException e) {
-                Log.wtf(TAG, "Failed to read wifi network stats", e);
-                return;
-            }
-
-            mCurWifiSnapshot = snapshot;
-            mLastWifiSnapshot = last;
-
-            if (mOnBatteryInternal) {
-                final NetworkStats delta = NetworkStats.subtract(snapshot, last,
-                        null, null, mTmpNetworkStats);
-                mTmpNetworkStats = delta;
-
-                final int size = delta.size();
-                for (int i = 0; i < size; i++) {
-                    final NetworkStats.Entry entry = delta.getValues(i, mTmpNetworkStatsEntry);
-
-                    if (DEBUG) {
-                        final NetworkStats.Entry cur = snapshot.getValues(i, null);
-                        Slog.d(TAG, "Wifi uid " + entry.uid + ": delta rx=" + entry.rxBytes
-                                + " tx=" + entry.txBytes + ", cur rx=" + cur.rxBytes
-                                + " tx=" + cur.txBytes);
-                    }
-
-                    if (entry.rxBytes == 0 || entry.txBytes == 0) continue;
-
-                    final Uid u = getUidStatsLocked(mapUid(entry.uid));
-                    u.noteNetworkActivityLocked(NETWORK_WIFI_RX_DATA, entry.rxBytes,
-                            entry.rxPackets);
-                    u.noteNetworkActivityLocked(NETWORK_WIFI_TX_DATA, entry.txBytes,
-                            entry.txPackets);
-
-                    mNetworkByteActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
-                            entry.rxBytes);
-                    mNetworkByteActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
-                            entry.txBytes);
-                    mNetworkPacketActivityCounters[NETWORK_WIFI_RX_DATA].addCountLocked(
-                            entry.rxPackets);
-                    mNetworkPacketActivityCounters[NETWORK_WIFI_TX_DATA].addCountLocked(
-                            entry.txPackets);
-                }
-            }
-        }
-    }
-
-    private void updateBluetoothControllerActivityLocked() {
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        if (adapter == null) {
-            return;
-        }
-
-        // We read the data even if we are not on battery. Each read clears
-        // the previous data, so we must always read to make sure the
-        // data is for the current interval.
-        BluetoothActivityEnergyInfo info = adapter.getControllerActivityEnergyInfo(
-                BluetoothAdapter.ACTIVITY_ENERGY_INFO_REFRESHED);
-        if (info == null || !info.isValid() || !mOnBatteryInternal) {
-            // Bad info or we are not on battery.
-            return;
-        }
-
-        mBluetoothActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
-                info.getControllerRxTimeMillis());
-        mBluetoothActivityCounters[CONTROLLER_TX_TIME].addCountLocked(
-                info.getControllerTxTimeMillis());
-        mBluetoothActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
-                info.getControllerIdleTimeMillis());
-        mBluetoothActivityCounters[CONTROLLER_ENERGY].addCountLocked(
-                info.getControllerEnergyUsed());
-    }
-
-    private void updateWifiControllerActivityLocked() {
-        IWifiManager wifiManager = IWifiManager.Stub.asInterface(
-                ServiceManager.getService(Context.WIFI_SERVICE));
-        if (wifiManager == null) {
-            return;
-        }
-
-        WifiActivityEnergyInfo info;
-        try {
-            // We read the data even if we are not on battery. Each read clears
-            // the previous data, so we must always read to make sure the
-            // data is for the current interval.
-            info = wifiManager.reportActivityInfo();
-        } catch (RemoteException e) {
-            // Nothing to report, WiFi is dead.
-            return;
-        }
-
-        if (info == null || !info.isValid() || !mOnBatteryInternal) {
-            // Bad info or we are not on battery.
-            return;
-        }
-
-        mWifiActivityCounters[CONTROLLER_RX_TIME].addCountLocked(
-                info.getControllerRxTimeMillis());
-        mWifiActivityCounters[CONTROLLER_TX_TIME].addCountLocked(
-                info.getControllerTxTimeMillis());
-        mWifiActivityCounters[CONTROLLER_IDLE_TIME].addCountLocked(
-                info.getControllerIdleTimeMillis());
-        mWifiActivityCounters[CONTROLLER_ENERGY].addCountLocked(
-                info.getControllerEnergyUsed());
     }
 
     public long getAwakeTimeBattery() {
@@ -8083,6 +7982,11 @@
         return mDailyChargeStepTracker;
     }
 
+    @Override
+    public ArrayList<PackageChange> getDailyPackageChanges() {
+        return mDailyPackageChanges;
+    }
+
     long getBatteryUptimeLocked() {
         return mOnBatteryTimeBase.getUptime(SystemClock.uptimeMillis() * 1000);
     }
@@ -8583,6 +8487,20 @@
         mChargeStepTracker.readFromParcel(in);
         mDailyDischargeStepTracker.readFromParcel(in);
         mDailyChargeStepTracker.readFromParcel(in);
+        int NPKG = in.readInt();
+        if (NPKG > 0) {
+            mDailyPackageChanges = new ArrayList<>(NPKG);
+            while (NPKG > 0) {
+                NPKG--;
+                PackageChange pc = new PackageChange();
+                pc.mPackageName = in.readString();
+                pc.mUpdate = in.readInt() != 0;
+                pc.mVersionCode = in.readInt();
+                mDailyPackageChanges.add(pc);
+            }
+        } else {
+            mDailyPackageChanges = null;
+        }
         mDailyStartTime = in.readLong();
         mNextMinDailyDeadline = in.readLong();
         mNextMaxDailyDeadline = in.readLong();
@@ -8599,6 +8517,7 @@
         mPhoneOn = false;
         mPowerSaveModeEnabledTimer.readSummaryFromParcelLocked(in);
         mDeviceIdleModeEnabledTimer.readSummaryFromParcelLocked(in);
+        mDeviceIdlingTimer.readSummaryFromParcelLocked(in);
         mPhoneOnTimer.readSummaryFromParcelLocked(in);
         for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
             mPhoneSignalStrengthsTimer[i].readSummaryFromParcelLocked(in);
@@ -8833,7 +8752,18 @@
             for (int ip = 0; ip < NP; ip++) {
                 String pkgName = in.readString();
                 Uid.Pkg p = u.getPackageStatsLocked(pkgName);
-                p.mWakeups = p.mLoadedWakeups = in.readInt();
+                final int NWA = in.readInt();
+                if (NWA > 1000) {
+                    Slog.w(TAG, "File corrupt: too many wakeup alarms " + NWA);
+                    return;
+                }
+                p.mWakeupAlarms.clear();
+                for (int iwa=0; iwa<NWA; iwa++) {
+                    String tag = in.readString();
+                    Counter c = new Counter(mOnBatteryTimeBase);
+                    c.readSummaryFromParcelLocked(in);
+                    p.mWakeupAlarms.put(tag, c);
+                }
                 NS = in.readInt();
                 if (NS > 1000) {
                     Slog.w(TAG, "File corrupt: too many services " + NS);
@@ -8890,6 +8820,18 @@
         mChargeStepTracker.writeToParcel(out);
         mDailyDischargeStepTracker.writeToParcel(out);
         mDailyChargeStepTracker.writeToParcel(out);
+        if (mDailyPackageChanges != null) {
+            final int NPKG = mDailyPackageChanges.size();
+            out.writeInt(NPKG);
+            for (int i=0; i<NPKG; i++) {
+                PackageChange pc = mDailyPackageChanges.get(i);
+                out.writeString(pc.mPackageName);
+                out.writeInt(pc.mUpdate ? 1 : 0);
+                out.writeInt(pc.mVersionCode);
+            }
+        } else {
+            out.writeInt(0);
+        }
         out.writeLong(mDailyStartTime);
         out.writeLong(mNextMinDailyDeadline);
         out.writeLong(mNextMaxDailyDeadline);
@@ -8901,6 +8843,7 @@
         mInteractiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mPowerSaveModeEnabledTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mDeviceIdleModeEnabledTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+        mDeviceIdlingTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mPhoneOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
             mPhoneSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -9145,20 +9088,22 @@
                     : u.mPackageStats.entrySet()) {
                     out.writeString(ent.getKey());
                     Uid.Pkg ps = ent.getValue();
-                    out.writeInt(ps.mWakeups);
+                    final int NWA = ps.mWakeupAlarms.size();
+                    out.writeInt(NWA);
+                    for (int iwa=0; iwa<NWA; iwa++) {
+                        out.writeString(ps.mWakeupAlarms.keyAt(iwa));
+                        ps.mWakeupAlarms.valueAt(iwa).writeSummaryFromParcelLocked(out);
+                    }
                     NS = ps.mServiceStats.size();
                     out.writeInt(NS);
-                    if (NS > 0) {
-                        for (Map.Entry<String, BatteryStatsImpl.Uid.Pkg.Serv> sent
-                                : ps.mServiceStats.entrySet()) {
-                            out.writeString(sent.getKey());
-                            BatteryStatsImpl.Uid.Pkg.Serv ss = sent.getValue();
-                            long time = ss.getStartTimeToNowLocked(
-                                    mOnBatteryTimeBase.getUptime(NOW_SYS));
-                            out.writeLong(time);
-                            out.writeInt(ss.mStarts);
-                            out.writeInt(ss.mLaunches);
-                        }
+                    for (int is=0; is<NS; is++) {
+                        out.writeString(ps.mServiceStats.keyAt(is));
+                        BatteryStatsImpl.Uid.Pkg.Serv ss = ps.mServiceStats.valueAt(is);
+                        long time = ss.getStartTimeToNowLocked(
+                                mOnBatteryTimeBase.getUptime(NOW_SYS));
+                        out.writeLong(time);
+                        out.writeInt(ss.mStarts);
+                        out.writeInt(ss.mLaunches);
                     }
                 }
             }
@@ -9201,6 +9146,7 @@
         mPhoneOn = false;
         mPowerSaveModeEnabledTimer = new StopwatchTimer(null, -2, null, mOnBatteryTimeBase, in);
         mDeviceIdleModeEnabledTimer = new StopwatchTimer(null, -11, null, mOnBatteryTimeBase, in);
+        mDeviceIdlingTimer = new StopwatchTimer(null, -12, null, mOnBatteryTimeBase, in);
         mPhoneOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase, in);
         for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
             mPhoneSignalStrengthsTimer[i] = new StopwatchTimer(null, -200-i,
@@ -9367,6 +9313,7 @@
         mInteractiveTimer.writeToParcel(out, uSecRealtime);
         mPowerSaveModeEnabledTimer.writeToParcel(out, uSecRealtime);
         mDeviceIdleModeEnabledTimer.writeToParcel(out, uSecRealtime);
+        mDeviceIdlingTimer.writeToParcel(out, uSecRealtime);
         mPhoneOnTimer.writeToParcel(out, uSecRealtime);
         for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
             mPhoneSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
@@ -9507,6 +9454,8 @@
             mPowerSaveModeEnabledTimer.logState(pr, "  ");
             pr.println("*** Device idle mode timer:");
             mDeviceIdleModeEnabledTimer.logState(pr, "  ");
+            pr.println("*** Device idling timer:");
+            mDeviceIdlingTimer.logState(pr, "  ");
             pr.println("*** Phone timer:");
             mPhoneOnTimer.logState(pr, "  ");
             for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java
new file mode 100644
index 0000000..768d586
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelWakelockReader.java
@@ -0,0 +1,192 @@
+/*
+ * 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.internal.os;
+
+import android.os.Process;
+import android.util.Slog;
+
+import java.io.FileInputStream;
+import java.util.Iterator;
+
+/**
+ * Reads and parses wakelock stats from the kernel (/proc/wakelocks).
+ */
+public class KernelWakelockReader {
+    private static final String TAG = "KernelWakelockReader";
+    private static int sKernelWakelockUpdateVersion = 0;
+    private static final String sWakelockFile = "/proc/wakelocks";
+    private static final String sWakeupSourceFile = "/d/wakeup_sources";
+
+    private static final int[] PROC_WAKELOCKS_FORMAT = new int[] {
+        Process.PROC_TAB_TERM|Process.PROC_OUT_STRING|                // 0: name
+                              Process.PROC_QUOTES,
+        Process.PROC_TAB_TERM|Process.PROC_OUT_LONG,                  // 1: count
+        Process.PROC_TAB_TERM,
+        Process.PROC_TAB_TERM,
+        Process.PROC_TAB_TERM,
+        Process.PROC_TAB_TERM|Process.PROC_OUT_LONG,                  // 5: totalTime
+    };
+
+    private static final int[] WAKEUP_SOURCES_FORMAT = new int[] {
+        Process.PROC_TAB_TERM|Process.PROC_OUT_STRING,                // 0: name
+        Process.PROC_TAB_TERM|Process.PROC_COMBINE|
+                              Process.PROC_OUT_LONG,                  // 1: count
+        Process.PROC_TAB_TERM|Process.PROC_COMBINE,
+        Process.PROC_TAB_TERM|Process.PROC_COMBINE,
+        Process.PROC_TAB_TERM|Process.PROC_COMBINE,
+        Process.PROC_TAB_TERM|Process.PROC_COMBINE,
+        Process.PROC_TAB_TERM|Process.PROC_COMBINE
+                             |Process.PROC_OUT_LONG,                  // 6: totalTime
+    };
+
+    private final String[] mProcWakelocksName = new String[3];
+    private final long[] mProcWakelocksData = new long[3];
+
+    /**
+     * Reads kernel wakelock stats and updates the staleStats with the new information.
+     * @param staleStats Existing object to update.
+     * @return the updated data.
+     */
+    public final KernelWakelockStats readKernelWakelockStats(KernelWakelockStats staleStats) {
+        byte[] buffer = new byte[32*1024];
+        int len;
+        boolean wakeup_sources;
+
+        try {
+            FileInputStream is;
+            try {
+                is = new FileInputStream(sWakeupSourceFile);
+                wakeup_sources = true;
+            } catch (java.io.FileNotFoundException e) {
+                try {
+                    is = new FileInputStream(sWakelockFile);
+                    wakeup_sources = false;
+                } catch (java.io.FileNotFoundException e2) {
+                    return null;
+                }
+            }
+
+            len = is.read(buffer);
+            is.close();
+        } catch (java.io.IOException e) {
+            return null;
+        }
+
+        if (len > 0) {
+            if (len >= buffer.length) {
+                Slog.wtf(TAG, "Kernel wake locks exceeded buffer size " + buffer.length);
+            }
+            int i;
+            for (i=0; i<len; i++) {
+                if (buffer[i] == '\0') {
+                    len = i;
+                    break;
+                }
+            }
+        }
+        return parseProcWakelocks(buffer, len, wakeup_sources, staleStats);
+    }
+
+    /**
+     * Reads the wakelocks and updates the staleStats with the new information.
+     */
+    private KernelWakelockStats parseProcWakelocks(byte[] wlBuffer, int len, boolean wakeup_sources,
+                                                   final KernelWakelockStats staleStats) {
+        String name;
+        int count;
+        long totalTime;
+        int startIndex;
+        int endIndex;
+        int numUpdatedWlNames = 0;
+
+        // Advance past the first line.
+        int i;
+        for (i = 0; i < len && wlBuffer[i] != '\n' && wlBuffer[i] != '\0'; i++);
+        startIndex = endIndex = i + 1;
+
+        synchronized(this) {
+            sKernelWakelockUpdateVersion++;
+            while (endIndex < len) {
+                for (endIndex=startIndex;
+                        endIndex < len && wlBuffer[endIndex] != '\n' && wlBuffer[endIndex] != '\0';
+                        endIndex++);
+                endIndex++; // endIndex is an exclusive upper bound.
+                // Don't go over the end of the buffer, Process.parseProcLine might
+                // write to wlBuffer[endIndex]
+                if (endIndex >= (len - 1) ) {
+                    return staleStats;
+                }
+
+                String[] nameStringArray = mProcWakelocksName;
+                long[] wlData = mProcWakelocksData;
+                // Stomp out any bad characters since this is from a circular buffer
+                // A corruption is seen sometimes that results in the vm crashing
+                // This should prevent crashes and the line will probably fail to parse
+                for (int j = startIndex; j < endIndex; j++) {
+                    if ((wlBuffer[j] & 0x80) != 0) wlBuffer[j] = (byte) '?';
+                }
+                boolean parsed = Process.parseProcLine(wlBuffer, startIndex, endIndex,
+                        wakeup_sources ? WAKEUP_SOURCES_FORMAT :
+                                         PROC_WAKELOCKS_FORMAT,
+                        nameStringArray, wlData, null);
+
+                name = nameStringArray[0];
+                count = (int) wlData[1];
+
+                if (wakeup_sources) {
+                        // convert milliseconds to microseconds
+                        totalTime = wlData[2] * 1000;
+                } else {
+                        // convert nanoseconds to microseconds with rounding.
+                        totalTime = (wlData[2] + 500) / 1000;
+                }
+
+                if (parsed && name.length() > 0) {
+                    if (!staleStats.containsKey(name)) {
+                        staleStats.put(name, new KernelWakelockStats.Entry(count, totalTime,
+                                sKernelWakelockUpdateVersion));
+                        numUpdatedWlNames++;
+                    } else {
+                        KernelWakelockStats.Entry kwlStats = staleStats.get(name);
+                        if (kwlStats.mVersion == sKernelWakelockUpdateVersion) {
+                            kwlStats.mCount += count;
+                            kwlStats.mTotalTime += totalTime;
+                        } else {
+                            kwlStats.mCount = count;
+                            kwlStats.mTotalTime = totalTime;
+                            kwlStats.mVersion = sKernelWakelockUpdateVersion;
+                            numUpdatedWlNames++;
+                        }
+                    }
+                }
+                startIndex = endIndex;
+            }
+
+            if (staleStats.size() != numUpdatedWlNames) {
+                // Don't report old data.
+                Iterator<KernelWakelockStats.Entry> itr = staleStats.values().iterator();
+                while (itr.hasNext()) {
+                    if (itr.next().mVersion != sKernelWakelockUpdateVersion) {
+                        itr.remove();
+                    }
+                }
+            }
+
+            staleStats.kernelWakelockVersion = sKernelWakelockUpdateVersion;
+            return staleStats;
+        }
+    }
+}
diff --git a/core/java/com/android/internal/os/KernelWakelockStats.java b/core/java/com/android/internal/os/KernelWakelockStats.java
new file mode 100644
index 0000000..144ea00
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelWakelockStats.java
@@ -0,0 +1,37 @@
+/*
+ * 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.internal.os;
+
+import java.util.HashMap;
+
+/**
+ * Kernel wakelock stats object.
+ */
+public class KernelWakelockStats extends HashMap<String, KernelWakelockStats.Entry> {
+    public static class Entry {
+        public int mCount;
+        public long mTotalTime;
+        public int mVersion;
+
+        Entry(int count, long totalTime, int version) {
+            mCount = count;
+            mTotalTime = totalTime;
+            mVersion = version;
+        }
+    }
+
+    int kernelWakelockVersion;
+}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 8674a21..75b6446 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -41,15 +41,10 @@
     /** enable the JIT compiler */
     public static final int DEBUG_ENABLE_JIT         = 1 << 5;
 
-
     /** No external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_NONE = 0;
-    /** Single-user external storage should be mounted. */
-    public static final int MOUNT_EXTERNAL_SINGLEUSER = 1;
-    /** Multi-user external storage should be mounted. */
-    public static final int MOUNT_EXTERNAL_MULTIUSER = 2;
-    /** All multi-user external storage should be mounted. */
-    public static final int MOUNT_EXTERNAL_MULTIUSER_ALL = 3;
+    /** Default user-specific external storage should be mounted. */
+    public static final int MOUNT_EXTERNAL_DEFAULT = 1;
 
     private static final ZygoteHooks VM_HOOKS = new ZygoteHooks();
 
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 4d405b2..9106ccd 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -514,10 +514,8 @@
                                 "Duplicate arg specified");
                     }
                     niceName = arg.substring(arg.indexOf('=') + 1);
-                } else if (arg.equals("--mount-external-multiuser")) {
-                    mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
-                } else if (arg.equals("--mount-external-multiuser-all")) {
-                    mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL;
+                } else if (arg.equals("--mount-external-default")) {
+                    mountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
                 } else if (arg.equals("--query-abi-list")) {
                     abiListQuery = true;
                 } else if (arg.startsWith("--instruction-set=")) {
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index d9ebc25..a106f48 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -52,6 +52,7 @@
     public static final int BASE_WIFI_RTT_SERVICE                                   = 0x00027300;
     public static final int BASE_WIFI_PASSPOINT_MANAGER                             = 0x00028000;
     public static final int BASE_WIFI_PASSPOINT_SERVICE                             = 0x00028100;
+    public static final int BASE_WIFI_LOGGER                                        = 0x00028300;
     public static final int BASE_DHCP                                               = 0x00030000;
     public static final int BASE_DATA_CONNECTION                                    = 0x00040000;
     public static final int BASE_DATA_CONNECTION_AC                                 = 0x00041000;
diff --git a/core/java/com/android/internal/util/ScreenShapeHelper.java b/core/java/com/android/internal/util/ScreenShapeHelper.java
new file mode 100644
index 0000000..1bcc7a0
--- /dev/null
+++ b/core/java/com/android/internal/util/ScreenShapeHelper.java
@@ -0,0 +1,48 @@
+package com.android.internal.util;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.SystemProperties;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.ViewRootImpl;
+
+import com.android.internal.R;
+
+/**
+ * @hide
+ */
+public class ScreenShapeHelper {
+    private static final boolean IS_EMULATOR = Build.HARDWARE.contains("goldfish");
+
+    /**
+     * Return the bottom pixel window outset of a window given its style attributes.
+     * @param displayMetrics Display metrics of the current device
+     * @param windowStyle Window style attributes for the window.
+     * @return An outset dimension in pixels or 0 if no outset should be applied.
+     */
+    public static int getWindowOutsetBottomPx(DisplayMetrics displayMetrics,
+            TypedArray windowStyle) {
+        if (IS_EMULATOR) {
+            return SystemProperties.getInt(ViewRootImpl.PROPERTY_EMULATOR_WIN_OUTSET_BOTTOM_PX, 0);
+        } else if (windowStyle.hasValue(R.styleable.Window_windowOutsetBottom)) {
+            TypedValue outsetBottom = new TypedValue();
+            windowStyle.getValue(R.styleable.Window_windowOutsetBottom, outsetBottom);
+            return (int) outsetBottom.getDimension(displayMetrics);
+        }
+        return 0;
+    }
+
+    /**
+     * Get whether a device has has a round screen.
+     */
+    public static boolean getWindowIsRound(Resources resources) {
+        if (IS_EMULATOR) {
+            return SystemProperties.getBoolean(ViewRootImpl.PROPERTY_EMULATOR_CIRCULAR, false);
+        } else {
+            return resources.getBoolean(
+                    com.android.internal.R.bool.config_windowIsRound);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/AccessibleDateAnimator.java b/core/java/com/android/internal/widget/AccessibleDateAnimator.java
deleted file mode 100644
index f97a5d1..0000000
--- a/core/java/com/android/internal/widget/AccessibleDateAnimator.java
+++ /dev/null
@@ -1,56 +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.internal.widget;
-
-import android.content.Context;
-import android.text.format.DateUtils;
-import android.util.AttributeSet;
-import android.view.accessibility.AccessibilityEvent;
-import android.widget.ViewAnimator;
-
-/**
- * @hide
- */
-public class AccessibleDateAnimator extends ViewAnimator {
-    private long mDateMillis;
-
-    public AccessibleDateAnimator(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public void setDateMillis(long dateMillis) {
-        mDateMillis = dateMillis;
-    }
-
-    /**
-     * Announce the currently-selected date when launched.
-     */
-    @Override
-    public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
-        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
-            // Clear the event's current text so that only the current date will be spoken.
-            event.getText().clear();
-            int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR |
-                    DateUtils.FORMAT_SHOW_WEEKDAY;
-
-            String dateString = DateUtils.formatDateTime(getContext(), mDateMillis, flags);
-            event.getText().add(dateString);
-            return true;
-        }
-        return super.dispatchPopulateAccessibilityEventInternal(event);
-    }
-}
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
new file mode 100644
index 0000000..be9945d
--- /dev/null
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -0,0 +1,596 @@
+/*
+ * 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.internal.widget;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.PopupWindow;
+
+import com.android.internal.R;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A floating toolbar for showing contextual menu items.
+ * This view shows as many menu item buttons as can fit in the horizontal toolbar and the
+ * the remaining menu items in a vertical overflow view when the overflow button is clicked.
+ * The horizontal toolbar morphs into the vertical overflow view.
+ */
+public final class FloatingToolbar {
+
+    private static final MenuItem.OnMenuItemClickListener NO_OP_MENUITEM_CLICK_LISTENER =
+            new MenuItem.OnMenuItemClickListener() {
+                @Override
+                public boolean onMenuItemClick(MenuItem item) {
+                    return false;
+                }
+            };
+
+    private final Context mContext;
+    private final FloatingToolbarPopup mPopup;
+    private final ViewGroup mMenuItemButtonsContainer;
+    private final View.OnClickListener mMenuItemButtonOnClickListener =
+            new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (v.getTag() instanceof MenuItem) {
+                        mMenuItemClickListener.onMenuItemClick((MenuItem) v.getTag());
+                        mPopup.dismiss();
+                    }
+                }
+            };
+
+    private final Rect mContentRect = new Rect();
+    private final Point mCoordinates = new Point();
+
+    private Menu mMenu;
+    private List<CharSequence> mShowingTitles = new ArrayList<CharSequence>();
+    private MenuItem.OnMenuItemClickListener mMenuItemClickListener = NO_OP_MENUITEM_CLICK_LISTENER;
+    private View mOpenOverflowButton;
+
+    private int mSuggestedWidth;
+
+    /**
+     * Initializes a floating toolbar.
+     */
+    public FloatingToolbar(Context context, Window window) {
+        mContext = Preconditions.checkNotNull(context);
+        mPopup = new FloatingToolbarPopup(Preconditions.checkNotNull(window.getDecorView()));
+        mMenuItemButtonsContainer = createMenuButtonsContainer(context);
+    }
+
+    /**
+     * Sets the menu to be shown in this floating toolbar.
+     * NOTE: Call {@link #updateLayout()} or {@link #show()} to effect visual changes to the
+     * toolbar.
+     */
+    public FloatingToolbar setMenu(Menu menu) {
+        mMenu = Preconditions.checkNotNull(menu);
+        return this;
+    }
+
+    /**
+     * Sets the custom listener for invocation of menu items in this floating
+     * toolbar.
+     */
+    public FloatingToolbar setOnMenuItemClickListener(
+            MenuItem.OnMenuItemClickListener menuItemClickListener) {
+        if (menuItemClickListener != null) {
+            mMenuItemClickListener = menuItemClickListener;
+        } else {
+            mMenuItemClickListener = NO_OP_MENUITEM_CLICK_LISTENER;
+        }
+        return this;
+    }
+
+    /**
+     * Sets the content rectangle. This is the area of the interesting content that this toolbar
+     * should avoid obstructing.
+     * NOTE: Call {@link #updateLayout()} or {@link #show()} to effect visual changes to the
+     * toolbar.
+     */
+    public FloatingToolbar setContentRect(Rect rect) {
+        mContentRect.set(Preconditions.checkNotNull(rect));
+        return this;
+    }
+
+    /**
+     * Sets the suggested width of this floating toolbar.
+     * The actual width will be about this size but there are no guarantees that it will be exactly
+     * the suggested width.
+     * NOTE: Call {@link #updateLayout()} or {@link #show()} to effect visual changes to the
+     * toolbar.
+     */
+    public FloatingToolbar setSuggestedWidth(int suggestedWidth) {
+        mSuggestedWidth = suggestedWidth;
+        return this;
+    }
+
+    /**
+     * Shows this floating toolbar.
+     */
+    public FloatingToolbar show() {
+        List<MenuItem> menuItems = getVisibleAndEnabledMenuItems(mMenu);
+        if (hasContentChanged(menuItems) || hasWidthChanged()) {
+            mPopup.dismiss();
+            layoutMenuItemButtons(menuItems);
+            mShowingTitles = getMenuItemTitles(menuItems);
+        }
+        refreshCoordinates();
+        mPopup.updateCoordinates(mCoordinates.x, mCoordinates.y);
+        if (!mPopup.isShowing()) {
+            mPopup.show(mCoordinates.x, mCoordinates.y);
+        }
+        return this;
+    }
+
+    /**
+     * Updates this floating toolbar to reflect recent position and view updates.
+     * NOTE: This method is a no-op if the toolbar isn't showing.
+     */
+    public FloatingToolbar updateLayout() {
+        if (mPopup.isShowing()) {
+            // show() performs all the logic we need here.
+            show();
+        }
+        return this;
+    }
+
+    /**
+     * Dismisses this floating toolbar.
+     */
+    public void dismiss() {
+        mPopup.dismiss();
+    }
+
+    /**
+     * Returns {@code true} if this popup is currently showing. {@code false} otherwise.
+     */
+    public boolean isShowing() {
+        return mPopup.isShowing();
+    }
+
+    /**
+     * Refreshes {@link #mCoordinates} with values based on {@link #mContentRect}.
+     */
+    private void refreshCoordinates() {
+        int popupWidth = mPopup.getWidth();
+        int popupHeight = mPopup.getHeight();
+        if (!mPopup.isShowing()) {
+            // Popup isn't yet shown, get estimated size from the menu item buttons container.
+            mMenuItemButtonsContainer.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+            popupWidth = mMenuItemButtonsContainer.getMeasuredWidth();
+            popupHeight = mMenuItemButtonsContainer.getMeasuredHeight();
+        }
+        int x = mContentRect.centerX() - popupWidth / 2;
+        int y;
+        if (shouldDisplayAtTopOfContent()) {
+            y = mContentRect.top - popupHeight;
+        } else {
+            y = mContentRect.bottom;
+        }
+        mCoordinates.set(x, y);
+    }
+
+    /**
+     * Returns true if this floating toolbar's menu items have been reordered or changed.
+     */
+    private boolean hasContentChanged(List<MenuItem> menuItems) {
+        return !mShowingTitles.equals(getMenuItemTitles(menuItems));
+    }
+
+    /**
+     * Returns true if there is a significant change in width of the toolbar.
+     */
+    private boolean hasWidthChanged() {
+        int actualWidth = mPopup.getWidth();
+        int difference = Math.abs(actualWidth - mSuggestedWidth);
+        return difference > (actualWidth * 0.2);
+    }
+
+    /**
+     * Returns true if the preferred positioning of the toolbar is above the content rect.
+     */
+    private boolean shouldDisplayAtTopOfContent() {
+        return mContentRect.top - getMinimumOverflowHeight(mContext) > 0;
+    }
+
+    /**
+     * Returns the visible and enabled menu items in the specified menu.
+     * This method is recursive.
+     */
+    private List<MenuItem> getVisibleAndEnabledMenuItems(Menu menu) {
+        List<MenuItem> menuItems = new ArrayList<MenuItem>();
+        for (int i = 0; (menu != null) && (i < menu.size()); i++) {
+            MenuItem menuItem = menu.getItem(i);
+            if (menuItem.isVisible() && menuItem.isEnabled()) {
+                Menu subMenu = menuItem.getSubMenu();
+                if (subMenu != null) {
+                    menuItems.addAll(getVisibleAndEnabledMenuItems(subMenu));
+                } else {
+                    menuItems.add(menuItem);
+                }
+            }
+        }
+        return menuItems;
+    }
+
+    private List<CharSequence> getMenuItemTitles(List<MenuItem> menuItems) {
+        List<CharSequence> titles = new ArrayList<CharSequence>();
+        for (MenuItem menuItem : menuItems) {
+            titles.add(menuItem.getTitle());
+        }
+        return titles;
+    }
+
+    private void layoutMenuItemButtons(List<MenuItem> menuItems) {
+        final int toolbarWidth = getAdjustedToolbarWidth(mContext, mSuggestedWidth)
+                // Reserve space for the "open overflow" button.
+                - getEstimatedOpenOverflowButtonWidth(mContext);
+
+        int availableWidth = toolbarWidth;
+        LinkedList<MenuItem> remainingMenuItems = new LinkedList<MenuItem>(menuItems);
+
+        mMenuItemButtonsContainer.removeAllViews();
+
+        boolean isFirstItem = true;
+        while (!remainingMenuItems.isEmpty()) {
+            final MenuItem menuItem = remainingMenuItems.peek();
+            Button menuItemButton = createMenuItemButton(mContext, menuItem);
+
+            // Adding additional left padding for the first button to even out button spacing.
+            if (isFirstItem) {
+                menuItemButton.setPadding(
+                        2 * menuItemButton.getPaddingLeft(),
+                        menuItemButton.getPaddingTop(),
+                        menuItemButton.getPaddingRight(),
+                        menuItemButton.getPaddingBottom());
+                isFirstItem = false;
+            }
+
+            // Adding additional right padding for the last button to even out button spacing.
+            if (remainingMenuItems.size() == 1) {
+                menuItemButton.setPadding(
+                        menuItemButton.getPaddingLeft(),
+                        menuItemButton.getPaddingTop(),
+                        2 * menuItemButton.getPaddingRight(),
+                        menuItemButton.getPaddingBottom());
+            }
+
+            menuItemButton.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+            int menuItemButtonWidth = Math.min(menuItemButton.getMeasuredWidth(), toolbarWidth);
+            if (menuItemButtonWidth <= availableWidth) {
+                menuItemButton.setTag(menuItem);
+                menuItemButton.setOnClickListener(mMenuItemButtonOnClickListener);
+                mMenuItemButtonsContainer.addView(menuItemButton);
+                menuItemButton.getLayoutParams().width = menuItemButtonWidth;
+                availableWidth -= menuItemButtonWidth;
+                remainingMenuItems.pop();
+            } else {
+                // The "open overflow" button launches the vertical overflow from the
+                // floating toolbar.
+                createOpenOverflowButtonIfNotExists();
+                mMenuItemButtonsContainer.addView(mOpenOverflowButton);
+                break;
+            }
+        }
+        mPopup.setContentView(mMenuItemButtonsContainer);
+    }
+
+    /**
+     * Creates and returns the button that opens the vertical overflow.
+     */
+    private void createOpenOverflowButtonIfNotExists() {
+        mOpenOverflowButton = (ImageButton) LayoutInflater.from(mContext)
+                .inflate(R.layout.floating_popup_open_overflow_button, null);
+        mOpenOverflowButton.setOnClickListener(
+                new View.OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        // Open the overflow.
+                    }
+                });
+    }
+
+    /**
+     * Creates and returns a floating toolbar menu buttons container.
+     */
+    private static ViewGroup createMenuButtonsContainer(Context context) {
+        return (ViewGroup) LayoutInflater.from(context)
+                .inflate(R.layout.floating_popup_container, null);
+    }
+
+    /**
+     * Creates and returns a menu button for the specified menu item.
+     */
+    private static Button createMenuItemButton(Context context, MenuItem menuItem) {
+        Button menuItemButton = (Button) LayoutInflater.from(context)
+                .inflate(R.layout.floating_popup_menu_button, null);
+        menuItemButton.setText(menuItem.getTitle());
+        menuItemButton.setContentDescription(menuItem.getTitle());
+        return menuItemButton;
+    }
+
+    private static int getMinimumOverflowHeight(Context context) {
+        return context.getResources().
+                getDimensionPixelSize(R.dimen.floating_toolbar_minimum_overflow_height);
+    }
+
+    private static int getEstimatedOpenOverflowButtonWidth(Context context) {
+        return context.getResources()
+                .getDimensionPixelSize(R.dimen.floating_toolbar_menu_button_minimum_width);
+    }
+
+    private static int getAdjustedToolbarWidth(Context context, int width) {
+        if (width <= 0 || width > getScreenWidth(context)) {
+            width = context.getResources()
+                    .getDimensionPixelSize(R.dimen.floating_toolbar_default_width);
+        }
+        return width;
+    }
+
+    /**
+     * Returns the device's screen width.
+     */
+    public static int getScreenWidth(Context context) {
+        return context.getResources().getDisplayMetrics().widthPixels;
+    }
+
+    /**
+     * Returns the device's screen height.
+     */
+    public static int getScreenHeight(Context context) {
+        return context.getResources().getDisplayMetrics().heightPixels;
+    }
+
+
+    /**
+     * A popup window used by the floating toolbar.
+     */
+    private static final class FloatingToolbarPopup {
+
+        private final View mParent;
+        private final PopupWindow mPopupWindow;
+        private final ViewGroup mContentContainer;
+        private final Animator.AnimatorListener mOnDismissEnd =
+                new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mPopupWindow.dismiss();
+                        mDismissAnimating = false;
+                    }
+                };
+        private final AnimatorSet mGrowFadeInFromBottomAnimation;
+        private final AnimatorSet mShrinkFadeOutFromBottomAnimation;
+
+        private boolean mDismissAnimating;
+
+        /**
+         * Initializes a new floating bar popup.
+         *
+         * @param parent  A parent view to get the {@link View#getWindowToken()} token from.
+         */
+        public FloatingToolbarPopup(View parent) {
+            mParent = Preconditions.checkNotNull(parent);
+            mContentContainer = createContentContainer(parent.getContext());
+            mPopupWindow = createPopupWindow(mContentContainer);
+            mGrowFadeInFromBottomAnimation = createGrowFadeInFromBottom(mContentContainer);
+            mShrinkFadeOutFromBottomAnimation =
+                    createShrinkFadeOutFromBottomAnimation(mContentContainer, mOnDismissEnd);
+        }
+
+        /**
+         * Shows this popup at the specified coordinates.
+         * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
+         * If this popup is already showing, this will be a no-op.
+         */
+        public void show(int x, int y) {
+            if (isShowing()) {
+                updateCoordinates(x, y);
+                return;
+            }
+
+            mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, 0, 0);
+            positionOnScreen(x, y);
+            growFadeInFromBottom();
+
+            mDismissAnimating = false;
+        }
+
+        /**
+         * Gets rid of this popup. If the popup isn't currently showing, this will be a no-op.
+         */
+        public void dismiss() {
+            if (!isShowing()) {
+                return;
+            }
+
+            if (mDismissAnimating) {
+                // This window is already dismissing. Don't restart the animation.
+                return;
+            }
+            mDismissAnimating = true;
+            shrinkFadeOutFromBottom();
+        }
+
+        /**
+         * Returns {@code true} if this popup is currently showing. {@code false} otherwise.
+         */
+        public boolean isShowing() {
+            return mPopupWindow.isShowing() && !mDismissAnimating;
+        }
+
+        /**
+         * Updates the coordinates of this popup.
+         * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
+         */
+        public void updateCoordinates(int x, int y) {
+            if (isShowing()) {
+                positionOnScreen(x, y);
+            }
+        }
+
+        /**
+         * Sets the content of this popup.
+         */
+        public void setContentView(View view) {
+            Preconditions.checkNotNull(view);
+            mContentContainer.removeAllViews();
+            mContentContainer.addView(view);
+        }
+
+        /**
+         * Returns the width of this popup.
+         */
+        public int getWidth() {
+            return mContentContainer.getWidth();
+        }
+
+        /**
+         * Returns the height of this popup.
+         */
+        public int getHeight() {
+            return mContentContainer.getHeight();
+        }
+
+        /**
+         * Returns the context this popup is running in.
+         */
+        public Context getContext() {
+            return mContentContainer.getContext();
+        }
+
+        private void positionOnScreen(int x, int y) {
+            if (getWidth() == 0) {
+                // content size is yet to be measured.
+                mContentContainer.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+            }
+            x = clamp(x, 0, getScreenWidth(getContext()) - getWidth());
+            y = clamp(y, 0, getScreenHeight(getContext()) - getHeight());
+
+            // Position the view w.r.t. the window.
+            mContentContainer.setX(x);
+            mContentContainer.setY(y);
+        }
+
+        /**
+         * Performs the "grow and fade in from the bottom" animation on the floating popup.
+         */
+        private void growFadeInFromBottom() {
+            setPivot();
+            mGrowFadeInFromBottomAnimation.start();
+        }
+
+        /**
+         * Performs the "shrink and fade out from bottom" animation on the floating popup.
+         */
+        private void shrinkFadeOutFromBottom() {
+            setPivot();
+            mShrinkFadeOutFromBottomAnimation.start();
+        }
+
+        /**
+         * Sets the popup content container's pivot.
+         */
+        private void setPivot() {
+            mContentContainer.setPivotX(mContentContainer.getMeasuredWidth() / 2);
+            mContentContainer.setPivotY(mContentContainer.getMeasuredHeight());
+        }
+
+        private static ViewGroup createContentContainer(Context context) {
+            return (ViewGroup) LayoutInflater.from(context)
+                    .inflate(R.layout.floating_popup_container, null);
+        }
+
+        private static PopupWindow createPopupWindow(View content) {
+            ViewGroup popupContentHolder = new LinearLayout(content.getContext());
+            PopupWindow popupWindow = new PopupWindow(popupContentHolder);
+            popupWindow.setAnimationStyle(0);
+            popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+            popupWindow.setWidth(getScreenWidth(content.getContext()));
+            popupWindow.setHeight(getScreenHeight(content.getContext()));
+            content.setLayoutParams(new ViewGroup.LayoutParams(
+                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+            popupContentHolder.addView(content);
+            return popupWindow;
+        }
+
+        /**
+         * Creates a "grow and fade in from the bottom" animation for the specified view.
+         *
+         * @param view  The view to animate
+         */
+        private static AnimatorSet createGrowFadeInFromBottom(View view) {
+            AnimatorSet growFadeInFromBottomAnimation =  new AnimatorSet();
+            growFadeInFromBottomAnimation.playTogether(
+                    ObjectAnimator.ofFloat(view, View.SCALE_X, 0.5f, 1).setDuration(125),
+                    ObjectAnimator.ofFloat(view, View.SCALE_Y, 0.5f, 1).setDuration(125),
+                    ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(75));
+            return growFadeInFromBottomAnimation;
+        }
+
+        /**
+         * Creates a "shrink and fade out from bottom" animation for the specified view.
+         *
+         * @param view  The view to animate
+         * @param listener  The animation listener
+         */
+        private static AnimatorSet createShrinkFadeOutFromBottomAnimation(
+                View view, Animator.AnimatorListener listener) {
+            AnimatorSet shrinkFadeOutFromBottomAnimation =  new AnimatorSet();
+            shrinkFadeOutFromBottomAnimation.playTogether(
+                    ObjectAnimator.ofFloat(view, View.SCALE_Y, 1, 0.5f).setDuration(125),
+                    ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(75));
+            shrinkFadeOutFromBottomAnimation.setStartDelay(150);
+            shrinkFadeOutFromBottomAnimation.addListener(listener);
+            return shrinkFadeOutFromBottomAnimation;
+        }
+
+        /**
+         * Returns value, restricted to the range min->max (inclusive).
+         * If maximum is less than minimum, the result is undefined.
+         *
+         * @param value  The value to clamp.
+         * @param minimum  The minimum value in the range.
+         * @param maximum  The maximum value in the range. Must not be less than minimum.
+         */
+        private static int clamp(int value, int minimum, int maximum) {
+            return Math.max(minimum, Math.min(value, maximum));
+        }
+    }
+}
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index f916e6f..8d66191 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -42,6 +42,7 @@
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.accessibility.AccessibilityRecord;
 import android.view.animation.Interpolator;
 import android.widget.EdgeEffect;
@@ -137,7 +138,7 @@
     private int mRestoredCurItem = -1;
     private Parcelable mRestoredAdapterState = null;
     private ClassLoader mRestoredClassLoader = null;
-    private Scroller mScroller;
+    private final Scroller mScroller;
     private PagerObserver mObserver;
 
     private int mPageMargin;
@@ -162,9 +163,9 @@
 
     private boolean mIsBeingDragged;
     private boolean mIsUnableToDrag;
-    private int mDefaultGutterSize;
+    private final int mDefaultGutterSize;
     private int mGutterSize;
-    private int mTouchSlop;
+    private final int mTouchSlop;
     /**
      * Position of the last motion event.
      */
@@ -187,10 +188,10 @@
      * Determines speed during touch scrolling
      */
     private VelocityTracker mVelocityTracker;
-    private int mMinimumVelocity;
-    private int mMaximumVelocity;
-    private int mFlingDistance;
-    private int mCloseEnough;
+    private final int mMinimumVelocity;
+    private final int mMaximumVelocity;
+    private final int mFlingDistance;
+    private final int mCloseEnough;
 
     // If the pager is at least this close to its final position, complete the scroll
     // on touch down and let the user interact with the content inside instead of
@@ -200,8 +201,8 @@
     private boolean mFakeDragging;
     private long mFakeDragBeginTime;
 
-    private EdgeEffect mLeftEdge;
-    private EdgeEffect mRightEdge;
+    private final EdgeEffect mLeftEdge;
+    private final EdgeEffect mRightEdge;
 
     private boolean mFirstLayout = true;
     private boolean mNeedCalculatePageOffsets = false;
@@ -339,20 +340,24 @@
     interface Decor {}
 
     public ViewPager(Context context) {
-        super(context);
-        initViewPager();
+        this(context, null);
     }
 
     public ViewPager(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        initViewPager();
+        this(context, attrs, 0);
     }
 
-    void initViewPager() {
+    public ViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public ViewPager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
         setWillNotDraw(false);
         setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
         setFocusable(true);
-        final Context context = getContext();
+
         mScroller = new Scroller(context, sInterpolator);
         final ViewConfiguration configuration = ViewConfiguration.get(context);
         final float density = context.getResources().getDisplayMetrics().density;
@@ -367,8 +372,6 @@
         mCloseEnough = (int) (CLOSE_ENOUGH * density);
         mDefaultGutterSize = (int) (DEFAULT_GUTTER_SIZE * density);
 
-        setAccessibilityDelegate(new MyAccessibilityDelegate());
-
         if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
             setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
         }
@@ -2691,29 +2694,6 @@
     }
 
     @Override
-    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
-        // Dispatch scroll events from this ViewPager.
-        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
-            return super.dispatchPopulateAccessibilityEvent(event);
-        }
-
-        // Dispatch all other accessibility events from the current page.
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() == VISIBLE) {
-                final ItemInfo ii = infoForChild(child);
-                if (ii != null && ii.position == mCurItem &&
-                        child.dispatchPopulateAccessibilityEvent(event)) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    @Override
     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
         return new LayoutParams();
     }
@@ -2733,60 +2713,63 @@
         return new LayoutParams(getContext(), attrs);
     }
 
-    class MyAccessibilityDelegate extends AccessibilityDelegate {
 
-        @Override
-        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
-            super.onInitializeAccessibilityEvent(host, event);
-            event.setClassName(ViewPager.class.getName());
-            final AccessibilityRecord record = AccessibilityRecord.obtain();
-            record.setScrollable(canScroll());
-            if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED
-                    && mAdapter != null) {
-                record.setItemCount(mAdapter.getCount());
-                record.setFromIndex(mCurItem);
-                record.setToIndex(mCurItem);
-            }
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+
+        event.setClassName(ViewPager.class.getName());
+        event.setScrollable(canScroll());
+
+        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED && mAdapter != null) {
+            event.setItemCount(mAdapter.getCount());
+            event.setFromIndex(mCurItem);
+            event.setToIndex(mCurItem);
+        }
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+
+        info.setClassName(ViewPager.class.getName());
+        info.setScrollable(canScroll());
+
+        if (canScrollHorizontally(1)) {
+            info.addAction(AccessibilityAction.ACTION_SCROLL_FORWARD);
         }
 
-        @Override
-        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
-            super.onInitializeAccessibilityNodeInfo(host, info);
-            info.setClassName(ViewPager.class.getName());
-            info.setScrollable(canScroll());
-            if (canScrollHorizontally(1)) {
-                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
-            }
-            if (canScrollHorizontally(-1)) {
-                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
-            }
+        if (canScrollHorizontally(-1)) {
+            info.addAction(AccessibilityAction.ACTION_SCROLL_BACKWARD);
+        }
+    }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle args) {
+        if (super.performAccessibilityAction(action, args)) {
+            return true;
         }
 
-        @Override
-        public boolean performAccessibilityAction(View host, int action, Bundle args) {
-            if (super.performAccessibilityAction(host, action, args)) {
-                return true;
-            }
-            switch (action) {
-                case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
-                    if (canScrollHorizontally(1)) {
-                        setCurrentItem(mCurItem + 1);
-                        return true;
-                    }
-                } return false;
-                case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
-                    if (canScrollHorizontally(-1)) {
-                        setCurrentItem(mCurItem - 1);
-                        return true;
-                    }
-                } return false;
-            }
-            return false;
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+                if (canScrollHorizontally(1)) {
+                    setCurrentItem(mCurItem + 1);
+                    return true;
+                }
+                return false;
+            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+                if (canScrollHorizontally(-1)) {
+                    setCurrentItem(mCurItem - 1);
+                    return true;
+                }
+                return false;
         }
 
-        private boolean canScroll() {
-            return (mAdapter != null) && (mAdapter.getCount() > 1);
-        }
+        return false;
+    }
+
+    private boolean canScroll() {
+        return mAdapter != null && mAdapter.getCount() > 1;
     }
 
     private class PagerObserver extends DataSetObserver {
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index b5f2f37..037fd66 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -104,9 +104,9 @@
         // steps during restore; the restore will happen properly when the individual
         // files are restored piecemeal.
         FullBackup.backupToTar(getPackageName(), FullBackup.ROOT_TREE_TOKEN, null,
-                WALLPAPER_INFO_DIR, WALLPAPER_INFO, output.getData());
+                WALLPAPER_INFO_DIR, WALLPAPER_INFO, output);
         FullBackup.backupToTar(getPackageName(), FullBackup.ROOT_TREE_TOKEN, null,
-                WALLPAPER_IMAGE_DIR, WALLPAPER_IMAGE, output.getData());
+                WALLPAPER_IMAGE_DIR, WALLPAPER_IMAGE, output);
     }
 
     @Override
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 84568e4..cd117eb1 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -151,7 +151,6 @@
     android_util_FileObserver.cpp \
     android/opengl/poly_clip.cpp.arm \
     android/opengl/util.cpp \
-    android_server_FingerprintManager.cpp \
     android_server_NetworkManagementSocketTagger.cpp \
     android_server_Watchdog.cpp \
     android_ddm_DdmHandleNativeHeap.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 441af15..f1b04a5 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -537,7 +537,6 @@
  */
 int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
 {
-    int result = -1;
     JavaVMInitArgs initArgs;
     char propBuf[PROPERTY_VALUE_MAX];
     char stackTraceFileBuf[sizeof("-Xstacktracefile:")-1 + PROPERTY_VALUE_MAX];
@@ -553,12 +552,15 @@
     char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX];
     char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX];
     char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX];
+    char cachePruneBuf[sizeof("-Xzygote-max-boot-retry=")-1 + PROPERTY_VALUE_MAX];
     char dex2oatXmsImageFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
     char dex2oatXmxImageFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
     char dex2oatXmsFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];
     char dex2oatXmxFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
     char dex2oatCompilerFilterBuf[sizeof("--compiler-filter=")-1 + PROPERTY_VALUE_MAX];
     char dex2oatImageCompilerFilterBuf[sizeof("--compiler-filter=")-1 + PROPERTY_VALUE_MAX];
+    char dex2oatThreadsBuf[sizeof("-j")-1 + PROPERTY_VALUE_MAX];
+    char dex2oatThreadsImageBuf[sizeof("-j")-1 + PROPERTY_VALUE_MAX];
     char dex2oatFlagsBuf[PROPERTY_VALUE_MAX];
     char dex2oatImageFlagsBuf[PROPERTY_VALUE_MAX];
     char extraOptsBuf[PROPERTY_VALUE_MAX];
@@ -581,6 +583,7 @@
     char localeOption[sizeof("-Duser.locale=") + PROPERTY_VALUE_MAX];
     char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:")-1 + PROPERTY_VALUE_MAX];
     char nativeBridgeLibrary[sizeof("-XX:NativeBridge=") + PROPERTY_VALUE_MAX];
+    char cpuAbiListBuf[sizeof("--cpu-abilist=") + PROPERTY_VALUE_MAX];
 
     bool checkJni = false;
     property_get("dalvik.vm.checkjni", propBuf, "");
@@ -699,7 +702,7 @@
     if (!hasFile("/system/etc/preloaded-classes")) {
         ALOGE("Missing preloaded-classes file, /system/etc/preloaded-classes not found: %s\n",
               strerror(errno));
-        goto bail;
+        return -1;
     }
     addOption("-Ximage-compiler-option");
     addOption("--image-classes=/system/etc/preloaded-classes");
@@ -733,6 +736,9 @@
         parseCompilerOption("dalvik.vm.dex2oat-filter", dex2oatCompilerFilterBuf,
                             "--compiler-filter=", "-Xcompiler-option");
     }
+    parseCompilerOption("dalvik.vm.dex2oat-threads", dex2oatThreadsBuf, "-j", "-Xcompiler-option");
+    parseCompilerOption("dalvik.vm.image-dex2oat-threads", dex2oatThreadsImageBuf, "-j",
+                        "-Ximage-compiler-option");
     property_get("dalvik.vm.dex2oat-flags", dex2oatFlagsBuf, "");
     parseExtraOpts(dex2oatFlagsBuf, "-Xcompiler-option");
 
@@ -811,6 +817,23 @@
         addOption(nativeBridgeLibrary);
     }
 
+#if defined(__LP64__)
+    const char* cpu_abilist_property_name = "ro.product.cpu.abilist64";
+#else
+    const char* cpu_abilist_property_name = "ro.product.cpu.abilist32";
+#endif  // defined(__LP64__)
+    property_get(cpu_abilist_property_name, propBuf, "");
+    if (propBuf[0] == '\0') {
+        ALOGE("%s is not expected to be empty", cpu_abilist_property_name);
+        return -1;
+    }
+    snprintf(cpuAbiListBuf, sizeof(cpuAbiListBuf), "--cpu-abilist=%s", propBuf);
+    addOption(cpuAbiListBuf);
+
+    // Dalvik-cache pruning counter.
+    parseRuntimeOption("dalvik.vm.zygote.max-boot-retry", cachePruneBuf,
+                       "-Xzygote-max-boot-retry=");
+
     initArgs.version = JNI_VERSION_1_4;
     initArgs.options = mOptions.editArray();
     initArgs.nOptions = mOptions.size();
@@ -825,13 +848,10 @@
      */
     if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
         ALOGE("JNI_CreateJavaVM failed\n");
-        goto bail;
+        return -1;
     }
 
-    result = 0;
-
-bail:
-    return result;
+    return 0;
 }
 
 char* AndroidRuntime::toSlashClassName(const char* className)
diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp
index 8139c24..8bdbff4 100644
--- a/core/jni/android/graphics/MinikinUtils.cpp
+++ b/core/jni/android/graphics/MinikinUtils.cpp
@@ -26,10 +26,10 @@
 
 namespace android {
 
-void MinikinUtils::doLayout(Layout* layout, const Paint* paint, int bidiFlags, TypefaceImpl* typeface,
-        const uint16_t* buf, size_t start, size_t count, size_t bufSize) {
-    TypefaceImpl* resolvedFace = TypefaceImpl_resolveDefault(typeface);
-    layout->setFontCollection(resolvedFace->fFontCollection);
+FontStyle MinikinUtils::prepareMinikinPaint(MinikinPaint* minikinPaint, FontCollection** pFont,
+        const Paint* paint, TypefaceImpl* typeface) {
+    const TypefaceImpl* resolvedFace = TypefaceImpl_resolveDefault(typeface);
+    *pFont = resolvedFace->fFontCollection;
     FontStyle resolved = resolvedFace->fStyle;
 
     /* Prepare minikin FontStyle */
@@ -40,15 +40,26 @@
     FontStyle minikinStyle(minikinLang, minikinVariant, resolved.getWeight(), resolved.getItalic());
 
     /* Prepare minikin Paint */
-    MinikinPaint minikinPaint;
-    minikinPaint.size = (int)/*WHY?!*/paint->getTextSize();
-    minikinPaint.scaleX = paint->getTextScaleX();
-    minikinPaint.skewX = paint->getTextSkewX();
-    minikinPaint.letterSpacing = paint->getLetterSpacing();
-    minikinPaint.paintFlags = MinikinFontSkia::packPaintFlags(paint);
-    minikinPaint.fontFeatureSettings = paint->getFontFeatureSettings();
-    minikinPaint.hyphenEdit = HyphenEdit(paint->getHyphenEdit());
+    // Note: it would be nice to handle fractional size values (it would improve smooth zoom
+    // behavior), but historically size has been treated as an int.
+    // TODO: explore whether to enable fractional sizes, possibly when linear text flag is set.
+    minikinPaint->size = (int)paint->getTextSize();
+    minikinPaint->scaleX = paint->getTextScaleX();
+    minikinPaint->skewX = paint->getTextSkewX();
+    minikinPaint->letterSpacing = paint->getLetterSpacing();
+    minikinPaint->paintFlags = MinikinFontSkia::packPaintFlags(paint);
+    minikinPaint->fontFeatureSettings = paint->getFontFeatureSettings();
+    minikinPaint->hyphenEdit = HyphenEdit(paint->getHyphenEdit());
+    return minikinStyle;
+}
 
+void MinikinUtils::doLayout(Layout* layout, const Paint* paint, int bidiFlags,
+        TypefaceImpl* typeface, const uint16_t* buf, size_t start, size_t count,
+        size_t bufSize) {
+    FontCollection *font;
+    MinikinPaint minikinPaint;
+    FontStyle minikinStyle = prepareMinikinPaint(&minikinPaint, &font, paint, typeface);
+    layout->setFontCollection(font);
     layout->doLayout(buf, start, count, bufSize, bidiFlags, minikinStyle, minikinPaint);
 }
 
diff --git a/core/jni/android/graphics/MinikinUtils.h b/core/jni/android/graphics/MinikinUtils.h
index 236f1fd..1ee6245 100644
--- a/core/jni/android/graphics/MinikinUtils.h
+++ b/core/jni/android/graphics/MinikinUtils.h
@@ -31,22 +31,14 @@
 
 namespace android {
 
-// TODO: these should be defined in Minikin's Layout.h
-enum {
-    kBidi_LTR = 0,
-    kBidi_RTL = 1,
-    kBidi_Default_LTR = 2,
-    kBidi_Default_RTL = 3,
-    kBidi_Force_LTR = 4,
-    kBidi_Force_RTL = 5,
-
-    kBidi_Mask = 0x7
-};
-
 class MinikinUtils {
 public:
-    static void doLayout(Layout* layout, const Paint* paint, int bidiFlags, TypefaceImpl* typeface,
-            const uint16_t* buf, size_t start, size_t count, size_t bufSize);
+    static FontStyle prepareMinikinPaint(MinikinPaint* minikinPaint, FontCollection** pFont,
+            const Paint* paint, TypefaceImpl* typeface);
+
+    static void doLayout(Layout* layout, const Paint* paint, int bidiFlags,
+            TypefaceImpl* typeface, const uint16_t* buf, size_t start, size_t count,
+            size_t bufSize);
 
     static float xOffsetForTextAlign(Paint* paint, const Layout& layout);
 
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index f0131b4..873b516 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -23,6 +23,7 @@
 #include "GraphicsJNI.h"
 #include "core_jni_helpers.h"
 #include <ScopedUtfChars.h>
+#include <ScopedStringChars.h>
 
 #include "SkBlurDrawLooper.h"
 #include "SkColorFilter.h"
@@ -41,6 +42,8 @@
 #include "Paint.h"
 #include "TypefaceImpl.h"
 
+#include <vector>
+
 // temporary for debugging
 #include <Caches.h>
 #include <utils/Log.h>
@@ -101,6 +104,16 @@
     }
 
     static jlong init(JNIEnv* env, jobject clazz) {
+        SK_COMPILE_ASSERT(1 <<  0 == SkPaint::kAntiAlias_Flag,          paint_flags_mismatch);
+        SK_COMPILE_ASSERT(1 <<  2 == SkPaint::kDither_Flag,             paint_flags_mismatch);
+        SK_COMPILE_ASSERT(1 <<  3 == SkPaint::kUnderlineText_Flag,      paint_flags_mismatch);
+        SK_COMPILE_ASSERT(1 <<  4 == SkPaint::kStrikeThruText_Flag,     paint_flags_mismatch);
+        SK_COMPILE_ASSERT(1 <<  5 == SkPaint::kFakeBoldText_Flag,       paint_flags_mismatch);
+        SK_COMPILE_ASSERT(1 <<  6 == SkPaint::kLinearText_Flag,         paint_flags_mismatch);
+        SK_COMPILE_ASSERT(1 <<  7 == SkPaint::kSubpixelText_Flag,       paint_flags_mismatch);
+        SK_COMPILE_ASSERT(1 <<  8 == SkPaint::kDevKernText_Flag,        paint_flags_mismatch);
+        SK_COMPILE_ASSERT(1 << 10 == SkPaint::kEmbeddedBitmapText_Flag, paint_flags_mismatch);
+
         Paint* obj = new Paint();
         defaultSettingsForAndroid(obj);
         return reinterpret_cast<jlong>(obj);
@@ -962,6 +975,68 @@
                                       JNI_ABORT);
     }
 
+    static jboolean layoutContainsNotdef(const Layout& layout) {
+        for (size_t i = 0; i < layout.nGlyphs(); i++) {
+            if (layout.getGlyphId(i) == 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    static jboolean hasGlyphVariation(const Paint* paint, TypefaceImpl* typeface, jint bidiFlags,
+            const jchar* chars, size_t size) {
+        // TODO: query font for whether character has variation selector; requires a corresponding
+        // function in Minikin.
+        return false;
+    }
+
+    static jboolean hasGlyph(JNIEnv *env, jclass, jlong paintHandle, jlong typefaceHandle,
+            jint bidiFlags, jstring string) {
+        const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+        ScopedStringChars str(env, string);
+
+        /* start by rejecting variation selectors (not supported yet) */
+        size_t nChars = 0;
+        for (size_t i = 0; i < str.size(); i++) {
+            jchar c = str[i];
+            if (0xDC00 <= c && c <= 0xDFFF) {
+                // invalid UTF-16, unpaired trailing surrogate
+                return false;
+            } else if (0xD800 <= c && c <= 0xDBFF) {
+                if (i + 1 == str.size()) {
+                    // invalid UTF-16, unpaired leading surrogate at end of string
+                    return false;
+                }
+                i++;
+                jchar c2 = str[i];
+                if (!(0xDC00 <= c2 && c2 <= 0xDFFF)) {
+                    // invalid UTF-16, unpaired leading surrogate
+                    return false;
+                }
+                // UTF-16 encoding of range U+E0100..U+E01EF is DB40 DD00 .. DB40 DDEF
+                if (c == 0xDB40 && 0xDD00 <= c2 && c2 <= 0xDDEF) {
+                    return hasGlyphVariation(paint, typeface, bidiFlags, str.get(), str.size());
+                }
+            } else if (0xFE00 <= c && c <= 0xFE0F) {
+                return hasGlyphVariation(paint, typeface, bidiFlags, str.get(), str.size());
+            }
+            nChars++;
+        }
+        Layout layout;
+        MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, str.get(), 0, str.size(),
+                str.size());
+        size_t nGlyphs = layout.nGlyphs();
+        if (nGlyphs != 1 && nChars > 1) {
+            // multiple-character input, and was not a ligature
+            // TODO: handle ZWJ/ZWNJ characters specially so we can detect certain ligatures
+            // in joining scripts, such as Arabic and Mongolian.
+            return false;
+        }
+        return nGlyphs > 0 && !layoutContainsNotdef(layout);
+    }
+
 };
 
 static JNINativeMethod methods[] = {
@@ -1047,6 +1122,7 @@
                                         (void*) PaintGlue::getStringBounds },
     {"nativeGetCharArrayBounds", "(JJ[CIIILandroid/graphics/Rect;)V",
                                     (void*) PaintGlue::getCharArrayBounds },
+    {"native_hasGlyph",           "(JJILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph },
 
     {"native_setShadowLayer", "!(JFFFI)V", (void*)PaintGlue::setShadowLayer},
     {"native_hasShadowLayer", "!(J)Z", (void*)PaintGlue::hasShadowLayer}
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index b092e44..e0cbc9e 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -18,7 +18,7 @@
 #include "core_jni_helpers.h"
 
 #include "GraphicsJNI.h"
-#include <ScopedPrimitiveArray.h>
+#include "ScopedPrimitiveArray.h"
 #include "SkTypeface.h"
 #include "TypefaceImpl.h"
 #include <android_runtime/android_util_AssetManager.h>
diff --git a/core/jni/android/graphics/TypefaceImpl.h b/core/jni/android/graphics/TypefaceImpl.h
index d36f83a..4b14917 100644
--- a/core/jni/android/graphics/TypefaceImpl.h
+++ b/core/jni/android/graphics/TypefaceImpl.h
@@ -62,4 +62,4 @@
 
 }
 
-#endif  // _ANDROID_GRAPHICS_TYPEFACE_IMPL_H_
\ No newline at end of file
+#endif  // _ANDROID_GRAPHICS_TYPEFACE_IMPL_H_
diff --git a/core/jni/android_app_backup_FullBackup.cpp b/core/jni/android_app_backup_FullBackup.cpp
index 2c02b37..63b2e2a 100644
--- a/core/jni/android_app_backup_FullBackup.cpp
+++ b/core/jni/android_app_backup_FullBackup.cpp
@@ -15,6 +15,8 @@
  */
 
 #define LOG_TAG "FullBackup_native"
+#include <sys/stat.h>
+
 #include <utils/Log.h>
 #include <utils/String8.h>
 
@@ -30,6 +32,12 @@
 namespace android
 {
 
+// android.app.backup.FullBackupDataOutput
+static struct {
+    jfieldID mData;         // type android.app.backup.BackupDataOutput
+    jmethodID addSize;
+} sFullBackupDataOutput;
+
 // android.app.backup.BackupDataOutput
 static struct {
     // This is actually a native pointer to the underlying BackupDataWriter instance
@@ -70,7 +78,7 @@
  * linkdomain:  where a symlink points for purposes of rewriting; current unused
  * rootpath:    prefix to be snipped from full path when encoding in tar
  * path:        absolute path to the file to be saved
- * dataOutput:  the BackupDataOutput object that we're saving into
+ * dataOutput:  the FullBackupDataOutput object that we're saving into
  */
 static jint backupToTar(JNIEnv* env, jobject clazz, jstring packageNameObj,
         jstring domainObj, jstring linkdomain,
@@ -91,15 +99,11 @@
     if (rootchars) env->ReleaseStringUTFChars(rootpathObj, rootchars);
     if (packagenamechars) env->ReleaseStringUTFChars(packageNameObj, packagenamechars);
 
-    // Extract the data output fd
-    BackupDataWriter* writer = (BackupDataWriter*) env->GetLongField(dataOutputObj,
-            sBackupDataOutput.mBackupWriter);
-
-    // Validate
-    if (!writer) {
-        ALOGE("No output stream provided [%s]", path.string());
-        return (jint) -1;
-    }
+    // Extract the data output fd.  'writer' ends up NULL in the measure-only case.
+    jobject bdo = env->GetObjectField(dataOutputObj, sFullBackupDataOutput.mData);
+    BackupDataWriter* writer = (bdo != NULL)
+            ? (BackupDataWriter*) env->GetLongField(bdo, sBackupDataOutput.mBackupWriter)
+            : NULL;
 
     if (path.length() < rootpath.length()) {
         ALOGE("file path [%s] shorter than root path [%s]",
@@ -107,20 +111,30 @@
         return (jint) -1;
     }
 
-    return (jint) write_tarfile(packageName, domain, rootpath, path, writer);
+    off_t tarSize = 0;
+    jint err = write_tarfile(packageName, domain, rootpath, path, &tarSize, writer);
+    if (!err) {
+        //ALOGI("measured [%s] at %lld", path.string(), (long long) tarSize);
+        env->CallVoidMethod(dataOutputObj, sFullBackupDataOutput.addSize, (jlong) tarSize);
+    }
+
+    return err;
 }
 
 static const JNINativeMethod g_methods[] = {
     { "backupToTar",
-            "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/backup/BackupDataOutput;)I",
+            "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/backup/FullBackupDataOutput;)I",
             (void*)backupToTar },
 };
 
 int register_android_app_backup_FullBackup(JNIEnv* env)
 {
-    jclass clazz = FindClassOrDie(env, "android/app/backup/BackupDataOutput");
+    jclass fbdoClazz = FindClassOrDie(env, "android/app/backup/FullBackupDataOutput");
+    sFullBackupDataOutput.mData = GetFieldIDOrDie(env, fbdoClazz, "mData", "Landroid/app/backup/BackupDataOutput;");
+    sFullBackupDataOutput.addSize = GetMethodIDOrDie(env, fbdoClazz, "addSize", "(J)V");
 
-    sBackupDataOutput.mBackupWriter = GetFieldIDOrDie(env, clazz, "mBackupWriter", "J");
+    jclass bdoClazz = FindClassOrDie(env, "android/app/backup/BackupDataOutput");
+    sBackupDataOutput.mBackupWriter = GetFieldIDOrDie(env, bdoClazz, "mBackupWriter", "J");
 
     return RegisterMethodsOrDie(env, "android/app/backup/FullBackup", g_methods, NELEM(g_methods));
 }
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 49ee6c5..a2c1609 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -86,20 +86,26 @@
     return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
 }
 
-static void restore(JNIEnv* env, jobject, jlong canvasHandle) {
+static void restore(JNIEnv* env, jobject, jlong canvasHandle, jboolean throwOnUnderflow) {
     Canvas* canvas = get_canvas(canvasHandle);
     if (canvas->getSaveCount() <= 1) {  // cannot restore anymore
-        // fail silently on underflow, so as not to break existing apps that miscount
-        return;
+        if (throwOnUnderflow) {
+            doThrowISE(env, "Underflow in restore - more restores than saves");
+        }
+        return; // compat behavior - return without throwing
     }
     canvas->restore();
 }
 
-static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle, jint restoreCount) {
+static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle, jint restoreCount,
+        jboolean throwOnUnderflow) {
     Canvas* canvas = get_canvas(canvasHandle);
     if (restoreCount < 1 || restoreCount > canvas->getSaveCount()) {
-        // fail silently on underflow, so as not to break existing apps that miscount
-        return;
+        if (throwOnUnderflow) {
+            doThrowIAE(env, "Underflow in restoreToCount - more restores than saves");
+            return;
+        }
+        restoreCount = 1; // compat behavior - restore as far as possible
     }
     canvas->restoreToCount(restoreCount);
 }
@@ -661,8 +667,8 @@
     {"native_saveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
     {"native_saveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
     {"native_getSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
-    {"native_restore","(J)V", (void*) CanvasJNI::restore},
-    {"native_restoreToCount","(JI)V", (void*) CanvasJNI::restoreToCount},
+    {"native_restore","(JZ)V", (void*) CanvasJNI::restore},
+    {"native_restoreToCount","(JIZ)V", (void*) CanvasJNI::restoreToCount},
     {"native_getCTM", "(JJ)V", (void*)CanvasJNI::getCTM},
     {"native_setMatrix","(JJ)V", (void*) CanvasJNI::setMatrix},
     {"native_concat","(JJ)V", (void*) CanvasJNI::concat},
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 0b737a7..7d12230 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -18,6 +18,8 @@
 
 #include <map>
 
+#include <ScopedUtfChars.h>
+
 #include <utils/Log.h>
 #include <utils/Looper.h>
 
@@ -99,6 +101,9 @@
         if (string1 == NULL) {
             return string2 != NULL;
         }
+        if (string2 == NULL) {
+            return false;
+        }
         return string1->compare(*string2) < 0;
     }
 };
@@ -264,9 +269,12 @@
     }
 };
 
-static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jobject eventQ, jobject msgQ, jfloatArray scratch) {
+static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jobject eventQ, jobject msgQ,
+        jfloatArray scratch, jstring packageName) {
     SensorManager& mgr(SensorManager::getInstance());
-    sp<SensorEventQueue> queue(mgr.createEventQueue());
+    ScopedUtfChars packageUtf(env, packageName);
+    String8 clientName(packageUtf.c_str());
+    sp<SensorEventQueue> queue(mgr.createEventQueue(clientName));
 
     sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, msgQ);
     if (messageQueue == NULL) {
@@ -280,10 +288,11 @@
 }
 
 static jint nativeEnableSensor(JNIEnv *env, jclass clazz, jlong eventQ, jint handle, jint rate_us,
-                               jint maxBatchReportLatency, jint reservedFlags) {
+                               jint maxBatchReportLatency) {
     sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));
+
     return receiver->getSensorEventQueue()->enableSensor(handle, rate_us, maxBatchReportLatency,
-                                                         reservedFlags);
+                                                         0);
 }
 
 static jint nativeDisableSensor(JNIEnv *env, jclass clazz, jlong eventQ, jint handle) {
@@ -316,11 +325,11 @@
 
 static JNINativeMethod gBaseEventQueueMethods[] = {
     {"nativeInitBaseEventQueue",
-            "(Landroid/hardware/SystemSensorManager$BaseEventQueue;Landroid/os/MessageQueue;[F)J",
-            (void*)nativeInitSensorEventQueue },
+     "(Landroid/hardware/SystemSensorManager$BaseEventQueue;Landroid/os/MessageQueue;[FLjava/lang/String;)J",
+     (void*)nativeInitSensorEventQueue },
 
     {"nativeEnableSensor",
-            "(JIIII)I",
+            "(JIII)I",
             (void*)nativeEnableSensor },
 
     {"nativeDisableSensor",
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 4a98f27..fc05a6d 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -142,7 +142,14 @@
 
 static const char* const kEventHandlerClassPathName =
         "android/media/AudioPortEventHandler";
-static jmethodID gPostEventFromNative;
+static struct {
+    jfieldID    mJniCallback;
+} gEventHandlerFields;
+static struct {
+    jmethodID    postEventFromNative;
+} gAudioPortEventHandlerMethods;
+
+static Mutex gLock;
 
 enum AudioError {
     kAudioStatusOk = 0,
@@ -173,14 +180,14 @@
 private:
     void sendEvent(int event);
 
-    jclass      mClass;     // Reference to AudioPortEventHandlerDelegate class
-    jobject     mObject;    // Weak ref to AudioPortEventHandlerDelegate Java object to call on
+    jclass      mClass;     // Reference to AudioPortEventHandler class
+    jobject     mObject;    // Weak ref to AudioPortEventHandler Java object to call on
 };
 
 JNIAudioPortCallback::JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject weak_thiz)
 {
 
-    // Hold onto the SoundTriggerModule class for use in calling the static method
+    // Hold onto the AudioPortEventHandler class for use in calling the static method
     // that posts events to the application thread.
     jclass clazz = env->GetObjectClass(thiz);
     if (clazz == NULL) {
@@ -189,7 +196,7 @@
     }
     mClass = (jclass)env->NewGlobalRef(clazz);
 
-    // We use a weak reference so the SoundTriggerModule object can be garbage collected.
+    // We use a weak reference so the AudioPortEventHandler object can be garbage collected.
     // The reference is only used as a proxy for callbacks.
     mObject  = env->NewGlobalRef(weak_thiz);
 }
@@ -211,7 +218,7 @@
     if (env == NULL) {
         return;
     }
-    env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject,
+    env->CallStaticVoidMethod(mClass, gAudioPortEventHandlerMethods.postEventFromNative, mObject,
                               event, 0, 0, NULL);
     if (env->ExceptionCheck()) {
         ALOGW("An exception occurred while notifying an event.");
@@ -234,6 +241,23 @@
     sendEvent(AUDIOPORT_EVENT_SERVICE_DIED);
 }
 
+static sp<JNIAudioPortCallback> setJniCallback(JNIEnv* env,
+                                       jobject thiz,
+                                       const sp<JNIAudioPortCallback>& callback)
+{
+    Mutex::Autolock l(gLock);
+    sp<JNIAudioPortCallback> old =
+            (JNIAudioPortCallback*)env->GetLongField(thiz, gEventHandlerFields.mJniCallback);
+    if (callback.get()) {
+        callback->incStrong((void*)setJniCallback);
+    }
+    if (old != 0) {
+        old->decStrong((void*)setJniCallback);
+    }
+    env->SetLongField(thiz, gEventHandlerFields.mJniCallback, (jlong)callback.get());
+    return old;
+}
+
 static int check_AudioSystem_Command(status_t status)
 {
     switch (status) {
@@ -1355,7 +1379,9 @@
 
     sp<JNIAudioPortCallback> callback = new JNIAudioPortCallback(env, thiz, weak_this);
 
-    AudioSystem::setAudioPortCallback(callback);
+    if (AudioSystem::addAudioPortCallback(callback) == NO_ERROR) {
+        setJniCallback(env, thiz, callback);
+    }
 }
 
 static void
@@ -1363,9 +1389,11 @@
 {
     ALOGV("eventHandlerFinalize");
 
-    sp<JNIAudioPortCallback> callback;
+    sp<JNIAudioPortCallback> callback = setJniCallback(env, thiz, 0);
 
-    AudioSystem::setAudioPortCallback(callback);
+    if (callback != 0) {
+        AudioSystem::removeAudioPortCallback(callback);
+    }
 }
 
 static jint
@@ -1636,9 +1664,11 @@
                                                 "Landroid/media/AudioHandle;");
 
     jclass eventHandlerClass = FindClassOrDie(env, kEventHandlerClassPathName);
-    gPostEventFromNative = GetStaticMethodIDOrDie(env, eventHandlerClass, "postEventFromNative",
-                                                  "(Ljava/lang/Object;IIILjava/lang/Object;)V");
-
+    gAudioPortEventHandlerMethods.postEventFromNative = GetStaticMethodIDOrDie(
+                                                    env, eventHandlerClass, "postEventFromNative",
+                                                    "(Ljava/lang/Object;IIILjava/lang/Object;)V");
+    gEventHandlerFields.mJniCallback = GetFieldIDOrDie(env,
+                                                    eventHandlerClass, "mJniCallback", "J");
 
     jclass audioMixClass = FindClassOrDie(env, "android/media/audiopolicy/AudioMix");
     gAudioMixClass = MakeGlobalRefOrDie(env, audioMixClass);
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 8800f0b..87c58d6 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -21,6 +21,7 @@
 #include "unicode/brkiter.h"
 #include "utils/misc.h"
 #include "utils/Log.h"
+#include "ScopedStringChars.h"
 #include "ScopedPrimitiveArray.h"
 #include "JNIHelp.h"
 #include "core_jni_helpers.h"
@@ -34,6 +35,7 @@
 #include "MinikinSkia.h"
 #include "MinikinUtils.h"
 #include "Paint.h"
+#include "minikin/LineBreaker.h"
 
 namespace android {
 
@@ -46,636 +48,119 @@
 static jclass gLineBreaks_class;
 static JLineBreaksID gLineBreaks_fieldID;
 
-class Builder {
-    public:
-        ~Builder() {
-            utext_close(&mUText);
-            delete mBreakIterator;
-        }
+// set text and set a number of parameters for creating a layout (width, tabstops, strategy)
+static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray text, jint length,
+        jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth,
+        jintArray variableTabStops, jint defaultTabStop, jint strategy) {
+    LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
+    b->resize(length);
+    env->GetCharArrayRegion(text, 0, length, b->buffer());
+    b->setText();
+    b->setLineWidths(firstWidth, firstWidthLineLimit, restWidth);
+    if (variableTabStops == nullptr) {
+        b->setTabStops(nullptr, 0, defaultTabStop);
+    } else {
+        ScopedIntArrayRO stops(env, variableTabStops);
+        b->setTabStops(stops.get(), stops.size(), defaultTabStop);
+    }
+    b->setStrategy(static_cast<BreakStrategy>(strategy));
+}
 
-        void setLocale(const icu::Locale& locale) {
-            delete mBreakIterator;
-            UErrorCode status = U_ZERO_ERROR;
-            mBreakIterator = icu::BreakIterator::createLineInstance(locale, status);
-            // TODO: check status
-        }
-
-        void resize(size_t size) {
-            mTextBuf.resize(size);
-            mWidthBuf.resize(size);
-        }
-
-        size_t size() const {
-            return mTextBuf.size();
-        }
-
-        uint16_t* buffer() {
-            return mTextBuf.data();
-        }
-
-        float* widths() {
-            return mWidthBuf.data();
-        }
-
-        // set text to current contents of buffer
-        void setText() {
-            UErrorCode status = U_ZERO_ERROR;
-            utext_openUChars(&mUText, mTextBuf.data(), mTextBuf.size(), &status);
-            mBreakIterator->setText(&mUText, status);
-        }
-
-        void finish() {
-            if (mTextBuf.size() > MAX_TEXT_BUF_RETAIN) {
-                mTextBuf.clear();
-                mTextBuf.shrink_to_fit();
-                mWidthBuf.clear();
-                mWidthBuf.shrink_to_fit();
-            }
-        }
-
-        icu::BreakIterator* breakIterator() const {
-            return mBreakIterator;
-        }
-
-        float measureStyleRun(Paint* paint, TypefaceImpl* typeface, size_t start, size_t end,
-                bool isRtl);
-
-        void addReplacement(size_t start, size_t end, float width);
-
-    private:
-        const size_t MAX_TEXT_BUF_RETAIN = 32678;
-        icu::BreakIterator* mBreakIterator = nullptr;
-        UText mUText = UTEXT_INITIALIZER;
-        std::vector<uint16_t>mTextBuf;
-        std::vector<float>mWidthBuf;
-};
-
-static const int CHAR_SPACE = 0x20;
-static const int CHAR_TAB = 0x09;
-static const int CHAR_NEWLINE = 0x0a;
-static const int CHAR_ZWSP = 0x200b;
-
-class TabStops {
-    public:
-        // specified stops must be a sorted array (allowed to be null)
-        TabStops(JNIEnv* env, jintArray stops, jint defaultTabWidth) :
-            mStops(env), mTabWidth(defaultTabWidth) {
-                if (stops != nullptr) {
-                    mStops.reset(stops);
-                    mNumStops = mStops.size();
-                } else {
-                    mNumStops = 0;
-                }
-            }
-        float width(float widthSoFar) const {
-            const jint* mStopsArray = mStops.get();
-            for (int i = 0; i < mNumStops; i++) {
-                if (mStopsArray[i] > widthSoFar) {
-                    return mStopsArray[i];
-                }
-            }
-            // find the next tabstop after widthSoFar
-            return static_cast<int>((widthSoFar + mTabWidth) / mTabWidth) * mTabWidth;
-        }
-    private:
-        ScopedIntArrayRO mStops;
-        const int mTabWidth;
-        int mNumStops;
-
-        // disable copying and assignment
-        TabStops(const TabStops&);
-        void operator=(const TabStops&);
-};
-
-enum PrimitiveType {
-    kPrimitiveType_Box,
-    kPrimitiveType_Glue,
-    kPrimitiveType_Penalty,
-    kPrimitiveType_Variable,
-    kPrimitiveType_Wordbreak
-};
-
-static const float PENALTY_INFINITY = 1e7; // forced non-break, negative infinity is forced break
-
-struct Primitive {
-    PrimitiveType type;
-    int location;
-    // 'Box' has width
-    // 'Glue' has width
-    // 'Penalty' has width and penalty
-    // 'Variable' has tabStop
-    // 'Wordbreak' has penalty
-    union {
-        struct {
-            float width;
-            float penalty;
-        };
-        const TabStops* tabStop;
-    };
-};
-
-class LineWidth {
-    public:
-        LineWidth(float firstWidth, int firstWidthLineCount, float restWidth) :
-                mFirstWidth(firstWidth), mFirstWidthLineCount(firstWidthLineCount),
-                mRestWidth(restWidth) {}
-        float getLineWidth(int line) const {
-            return (line < mFirstWidthLineCount) ? mFirstWidth : mRestWidth;
-        }
-    private:
-        const float mFirstWidth;
-        const int mFirstWidthLineCount;
-        const float mRestWidth;
-};
-
-class LineBreaker {
-    public:
-        LineBreaker(const std::vector<Primitive>& primitives,
-                    const LineWidth& lineWidth) :
-                mPrimitives(primitives), mLineWidth(lineWidth) {}
-        virtual ~LineBreaker() {}
-        virtual void computeBreaks(std::vector<int>* breaks, std::vector<float>* widths,
-                                   std::vector<unsigned char>* flags) const = 0;
-    protected:
-        const std::vector<Primitive>& mPrimitives;
-        const LineWidth& mLineWidth;
-};
-
-class OptimizingLineBreaker : public LineBreaker {
-    public:
-        OptimizingLineBreaker(const std::vector<Primitive>& primitives, const LineWidth& lineWidth) :
-                LineBreaker(primitives, lineWidth) {}
-        void computeBreaks(std::vector<int>* breaks, std::vector<float>* widths,
-                           std::vector<unsigned char>* flags) const {
-            int numBreaks = mPrimitives.size();
-            Node* opt = new Node[numBreaks];
-            opt[0].prev = -1;
-            opt[0].prevCount = 0;
-            opt[0].width = 0;
-            opt[0].demerits = 0;
-            opt[0].flags = false;
-            opt[numBreaks - 1].prev = -1;
-            opt[numBreaks - 1].prevCount = 0;
-
-            std::list<int> active;
-            active.push_back(0);
-            int lastBreak = 0;
-            for (int i = 0; i < numBreaks; i++) {
-                const Primitive& p = mPrimitives[i];
-                if (p.type == kPrimitiveType_Penalty) {
-                    const bool finalBreak = (i + 1 == numBreaks);
-                    bool breakFound = false;
-                    Node bestBreak;
-                    for (std::list<int>::iterator it = active.begin(); it != active.end(); /* incrementing done in loop */) {
-                        const int pos = *it;
-                        bool flags;
-                        float width, printedWidth;
-                        const int lines = opt[pos].prevCount;
-                        const float maxWidth = mLineWidth.getLineWidth(lines);
-                        // we have to compute metrics every time --
-                        // we can't really precompute this stuff and just deal with breaks
-                        // because of the way tab characters work, this makes it computationally
-                        // harder, but this way, we can still optimize while treating tab characters
-                        // correctly
-                        computeMetrics(pos, i, &width, &printedWidth, &flags);
-                        if (printedWidth <= maxWidth) {
-                            float demerits = computeDemerits(maxWidth, printedWidth,
-                                    finalBreak, p.penalty) + opt[pos].demerits;
-                            if (!breakFound || demerits < bestBreak.demerits) {
-                                bestBreak.prev = pos;
-                                bestBreak.prevCount = opt[pos].prevCount + 1;
-                                bestBreak.demerits = demerits;
-                                bestBreak.width = printedWidth;
-                                bestBreak.flags = flags;
-                                breakFound = true;
-                            }
-                            ++it;
-                        } else {
-                            active.erase(it++); // safe to delete like this
-                        }
-                    }
-                    if (p.penalty == -PENALTY_INFINITY) {
-                        active.clear();
-                    }
-                    if (breakFound) {
-                        opt[i] = bestBreak;
-                        active.push_back(i);
-                        lastBreak = i;
-                    }
-                    if (active.empty()) {
-                        // we can't give up!
-                        float width, printedWidth;
-                        bool flags;
-                        const int lines = opt[lastBreak].prevCount;
-                        const float maxWidth = mLineWidth.getLineWidth(lines);
-                        const int breakIndex = desperateBreak(lastBreak, numBreaks, maxWidth, &width, &printedWidth, &flags);
-
-                        opt[breakIndex].prev = lastBreak;
-                        opt[breakIndex].prevCount = lines + 1;
-                        opt[breakIndex].demerits = 0; // doesn't matter, it's the only one
-                        opt[breakIndex].width = width;
-                        opt[breakIndex].flags = flags;
-
-                        active.push_back(breakIndex);
-                        lastBreak = breakIndex;
-                        i = breakIndex; // incremented by i++
-                    }
-                }
-            }
-
-            int idx = numBreaks - 1;
-            int count = opt[idx].prevCount;
-            breaks->resize(count);
-            widths->resize(count);
-            flags->resize(count);
-            while (opt[idx].prev != -1) {
-                --count;
-
-                (*breaks)[count] = mPrimitives[idx].location;
-                (*widths)[count] = opt[idx].width;
-                (*flags)[count] = opt[idx].flags;
-
-                idx = opt[idx].prev;
-            }
-            delete[] opt;
-        }
-    private:
-        inline void computeMetrics(int start, int end, float* width, float* printedWidth, bool* flags) const {
-            bool f = false;
-            float w = 0, pw = 0;
-            for (int i = start; i < end; i++) {
-                const Primitive& p = mPrimitives[i];
-                if (p.type == kPrimitiveType_Box || p.type == kPrimitiveType_Glue) {
-                    w += p.width;
-                    if (p.type == kPrimitiveType_Box) {
-                        pw = w;
-                    }
-                } else if (p.type == kPrimitiveType_Variable) {
-                    w = p.tabStop->width(w);
-                    f = true;
-                }
-            }
-            *width = w;
-            *printedWidth = pw;
-            *flags = f;
-        }
-
-        inline float computeDemerits(float maxWidth, float width, bool finalBreak, float penalty) const {
-            float deviation = finalBreak ? 0 : maxWidth - width;
-            return (deviation * deviation) + penalty;
-        }
-
-        // returns end pos (chosen break), -1 if fail
-        inline int desperateBreak(int start, int limit, float maxWidth, float* width, float* printedWidth, bool* flags) const {
-            float w = 0, pw = 0;
-            bool breakFound = false;
-            int breakIndex = 0, firstTabIndex = INT_MAX;
-            for (int i = start; i < limit; i++) {
-                const Primitive& p = mPrimitives[i];
-
-                if (p.type == kPrimitiveType_Box || p.type == kPrimitiveType_Glue) {
-                    w += p.width;
-                    if (p.type == kPrimitiveType_Box) {
-                        pw = w;
-                    }
-                } else if (p.type == kPrimitiveType_Variable) {
-                    w = p.tabStop->width(w);
-                    firstTabIndex = std::min(firstTabIndex, i);
-                }
-
-                if (pw > maxWidth) {
-                    if (breakFound) {
-                        break;
-                    } else {
-                        // no choice, keep going
-                    }
-                }
-
-                // must make progress
-                if (i > start && (p.type == kPrimitiveType_Penalty || p.type == kPrimitiveType_Wordbreak)) {
-                    breakFound = true;
-                    breakIndex = i;
-                }
-            }
-
-            if (breakFound) {
-                *width = w;
-                *printedWidth = pw;
-                *flags = (start <= firstTabIndex && firstTabIndex < breakIndex);
-                return breakIndex;
-            } else {
-                return -1;
-            }
-        }
-
-        struct Node {
-            int prev; // set to sentinel value (-1) for initial node
-            int prevCount; // number of breaks so far
-            float demerits;
-            float width;
-            bool flags;
-        };
-};
-
-class GreedyLineBreaker : public LineBreaker {
-    public:
-        GreedyLineBreaker(const std::vector<Primitive>& primitives, const LineWidth& lineWidth) :
-            LineBreaker(primitives, lineWidth) {}
-        void computeBreaks(std::vector<int>* breaks, std::vector<float>* widths,
-                           std::vector<unsigned char>* flags) const {
-            int lineNum = 0;
-            float width = 0, printedWidth = 0;
-            bool breakFound = false, goodBreakFound = false;
-            int breakIndex = 0, goodBreakIndex = 0;
-            float breakWidth = 0, goodBreakWidth = 0;
-            int firstTabIndex = INT_MAX;
-
-            float maxWidth = mLineWidth.getLineWidth(lineNum);
-
-            const int numPrimitives = mPrimitives.size();
-            // greedily fit as many characters as possible on each line
-            // loop over all primitives, and choose the best break point
-            // (if possible, a break point without splitting a word)
-            // after going over the maximum length
-            for (int i = 0; i < numPrimitives; i++) {
-                const Primitive& p = mPrimitives[i];
-
-                // update the current line width
-                if (p.type == kPrimitiveType_Box || p.type == kPrimitiveType_Glue) {
-                    width += p.width;
-                    if (p.type == kPrimitiveType_Box) {
-                        printedWidth = width;
-                    }
-                } else if (p.type == kPrimitiveType_Variable) {
-                    width = p.tabStop->width(width);
-                    // keep track of first tab character in the region we are examining
-                    // so we can determine whether or not a line contains a tab
-                    firstTabIndex = std::min(firstTabIndex, i);
-                }
-
-                // find the best break point for the characters examined so far
-                if (printedWidth > maxWidth) {
-                    if (breakFound || goodBreakFound) {
-                        if (goodBreakFound) {
-                            // a true line break opportunity existed in the characters examined so far,
-                            // so there is no need to split a word
-                            i = goodBreakIndex; // no +1 because of i++
-                            lineNum++;
-                            maxWidth = mLineWidth.getLineWidth(lineNum);
-                            breaks->push_back(mPrimitives[goodBreakIndex].location);
-                            widths->push_back(goodBreakWidth);
-                            flags->push_back(firstTabIndex < goodBreakIndex);
-                            firstTabIndex = INT_MAX;
-                        } else {
-                            // must split a word because there is no other option
-                            i = breakIndex; // no +1 because of i++
-                            lineNum++;
-                            maxWidth = mLineWidth.getLineWidth(lineNum);
-                            breaks->push_back(mPrimitives[breakIndex].location);
-                            widths->push_back(breakWidth);
-                            flags->push_back(firstTabIndex < breakIndex);
-                            firstTabIndex = INT_MAX;
-                        }
-                        printedWidth = width = 0;
-                        goodBreakFound = breakFound = false;
-                        goodBreakWidth = breakWidth = 0;
-                        continue;
-                    } else {
-                        // no choice, keep going... must make progress by putting at least one
-                        // character on a line, even if part of that character is cut off --
-                        // there is no other option
-                    }
-                }
-
-                // update possible break points
-                if (p.type == kPrimitiveType_Penalty && p.penalty < PENALTY_INFINITY) {
-                    // this does not handle penalties with width
-
-                    // handle forced line break
-                    if (p.penalty == -PENALTY_INFINITY) {
-                        lineNum++;
-                        maxWidth = mLineWidth.getLineWidth(lineNum);
-                        breaks->push_back(p.location);
-                        widths->push_back(printedWidth);
-                        flags->push_back(firstTabIndex < i);
-                        firstTabIndex = INT_MAX;
-                        printedWidth = width = 0;
-                        goodBreakFound = breakFound = false;
-                        goodBreakWidth = breakWidth = 0;
-                        continue;
-                    }
-                    if (i > breakIndex && (printedWidth <= maxWidth || breakFound == false)) {
-                        breakFound = true;
-                        breakIndex = i;
-                        breakWidth = printedWidth;
-                    }
-                    if (i > goodBreakIndex && printedWidth <= maxWidth) {
-                        goodBreakFound = true;
-                        goodBreakIndex = i;
-                        goodBreakWidth = printedWidth;
-                    }
-                } else if (p.type == kPrimitiveType_Wordbreak) {
-                    // only do this if necessary -- we don't want to break words
-                    // when possible, but sometimes it is unavoidable
-                    if (i > breakIndex && (printedWidth <= maxWidth || breakFound == false)) {
-                        breakFound = true;
-                        breakIndex = i;
-                        breakWidth = printedWidth;
-                    }
-                }
-            }
-
-            if (breakFound || goodBreakFound) {
-                // output last break if there are more characters to output
-                if (goodBreakFound) {
-                    breaks->push_back(mPrimitives[goodBreakIndex].location);
-                    widths->push_back(goodBreakWidth);
-                    flags->push_back(firstTabIndex < goodBreakIndex);
-                } else {
-                    breaks->push_back(mPrimitives[breakIndex].location);
-                    widths->push_back(breakWidth);
-                    flags->push_back(firstTabIndex < breakIndex);
-                }
-            }
-        }
-};
-
-static jint recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
-                        jfloatArray recycleWidths, jbooleanArray recycleFlags,
-                        jint recycleLength, const std::vector<jint>& breaks,
-                        const std::vector<jfloat>& widths, const std::vector<jboolean>& flags) {
-    int bufferLength = breaks.size();
-    if (recycleLength < bufferLength) {
+static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
+                        jfloatArray recycleWidths, jintArray recycleFlags,
+                        jint recycleLength, size_t nBreaks, const jint* breaks,
+                        const jfloat* widths, const jint* flags) {
+    if ((size_t)recycleLength < nBreaks) {
         // have to reallocate buffers
-        recycleBreaks = env->NewIntArray(bufferLength);
-        recycleWidths = env->NewFloatArray(bufferLength);
-        recycleFlags = env->NewBooleanArray(bufferLength);
+        recycleBreaks = env->NewIntArray(nBreaks);
+        recycleWidths = env->NewFloatArray(nBreaks);
+        recycleFlags = env->NewIntArray(nBreaks);
 
         env->SetObjectField(recycle, gLineBreaks_fieldID.breaks, recycleBreaks);
         env->SetObjectField(recycle, gLineBreaks_fieldID.widths, recycleWidths);
         env->SetObjectField(recycle, gLineBreaks_fieldID.flags, recycleFlags);
     }
     // copy data
-    env->SetIntArrayRegion(recycleBreaks, 0, breaks.size(), &breaks.front());
-    env->SetFloatArrayRegion(recycleWidths, 0, widths.size(), &widths.front());
-    env->SetBooleanArrayRegion(recycleFlags, 0, flags.size(), &flags.front());
-
-    return bufferLength;
-}
-
-void computePrimitives(const jchar* textArr, const jfloat* widthsArr, jint length, const std::vector<int>& breaks,
-                       const TabStops& tabStopCalculator, std::vector<Primitive>* primitives) {
-    int breaksSize = breaks.size();
-    int breakIndex = 0;
-    Primitive p;
-    for (int i = 0; i < length; i++) {
-        p.location = i;
-        jchar c = textArr[i];
-        if (c == CHAR_SPACE || c == CHAR_ZWSP) {
-            p.type = kPrimitiveType_Glue;
-            p.width = widthsArr[i];
-            primitives->push_back(p);
-        } else if (c == CHAR_TAB) {
-            p.type = kPrimitiveType_Variable;
-            p.tabStop = &tabStopCalculator; // shared between all variable primitives
-            primitives->push_back(p);
-        } else if (c != CHAR_NEWLINE) {
-            while (breakIndex < breaksSize && breaks[breakIndex] < i) breakIndex++;
-            p.width = 0;
-            if (breakIndex < breaksSize && breaks[breakIndex] == i) {
-                p.type = kPrimitiveType_Penalty;
-                p.penalty = 0;
-            } else {
-                p.type = kPrimitiveType_Wordbreak;
-            }
-            if (widthsArr[i] != 0) {
-                primitives->push_back(p);
-            }
-
-            p.type = kPrimitiveType_Box;
-            p.width = widthsArr[i];
-            primitives->push_back(p);
-        }
-    }
-    // final break at end of everything
-    p.location = length;
-    p.type = kPrimitiveType_Penalty;
-    p.width = 0;
-    p.penalty = -PENALTY_INFINITY;
-    primitives->push_back(p);
-}
-
-// sets the text on the builder
-static void nSetText(JNIEnv* env, jclass, jlong nativePtr, jcharArray text, int length) {
-    Builder* b = reinterpret_cast<Builder*>(nativePtr);
-    b->resize(length);
-    env->GetCharArrayRegion(text, 0, length, b->buffer());
-    b->setText();
+    env->SetIntArrayRegion(recycleBreaks, 0, nBreaks, breaks);
+    env->SetFloatArrayRegion(recycleWidths, 0, nBreaks, widths);
+    env->SetIntArrayRegion(recycleFlags, 0, nBreaks, flags);
 }
 
 static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
-                               jint length,
-                               jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth,
-                               jintArray variableTabStops, jint defaultTabStop, jboolean optimize,
                                jobject recycle, jintArray recycleBreaks,
-                               jfloatArray recycleWidths, jbooleanArray recycleFlags,
+                               jfloatArray recycleWidths, jintArray recycleFlags,
                                jint recycleLength) {
-    Builder* b = reinterpret_cast<Builder*>(nativePtr);
+    LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
 
-    std::vector<int> breaks;
+    size_t nBreaks = b->computeBreaks();
 
-    icu::BreakIterator* breakIterator = b->breakIterator();
-    int loc = breakIterator->first();
-    while ((loc = breakIterator->next()) != icu::BreakIterator::DONE) {
-        breaks.push_back(loc);
-    }
+    recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleFlags, recycleLength,
+            nBreaks, b->getBreaks(), b->getWidths(), b->getFlags());
 
-    // TODO: all these allocations can be moved into the builder
-    std::vector<Primitive> primitives;
-    TabStops tabStops(env, variableTabStops, defaultTabStop);
-    computePrimitives(b->buffer(), b->widths(), length, breaks, tabStops, &primitives);
-
-    LineWidth lineWidth(firstWidth, firstWidthLineLimit, restWidth);
-    std::vector<int> computedBreaks;
-    std::vector<float> computedWidths;
-    std::vector<unsigned char> computedFlags;
-
-    if (optimize) {
-        OptimizingLineBreaker breaker(primitives, lineWidth);
-        breaker.computeBreaks(&computedBreaks, &computedWidths, &computedFlags);
-    } else {
-        GreedyLineBreaker breaker(primitives, lineWidth);
-        breaker.computeBreaks(&computedBreaks, &computedWidths, &computedFlags);
-    }
     b->finish();
 
-    return recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleFlags, recycleLength,
-            computedBreaks, computedWidths, computedFlags);
+    return static_cast<jint>(nBreaks);
 }
 
 static jlong nNewBuilder(JNIEnv*, jclass) {
-    return reinterpret_cast<jlong>(new Builder);
+    return reinterpret_cast<jlong>(new LineBreaker);
 }
 
 static void nFreeBuilder(JNIEnv*, jclass, jlong nativePtr) {
-    delete reinterpret_cast<Builder*>(nativePtr);
+    delete reinterpret_cast<LineBreaker*>(nativePtr);
 }
 
 static void nFinishBuilder(JNIEnv*, jclass, jlong nativePtr) {
-    Builder* b = reinterpret_cast<Builder*>(nativePtr);
+    LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
     b->finish();
 }
 
-static void nSetLocale(JNIEnv* env, jclass, jlong nativePtr, jstring javaLocaleName) {
+static jlong nLoadHyphenator(JNIEnv* env, jclass, jstring patternData) {
+    ScopedStringChars str(env, patternData);
+    Hyphenator* hyphenator = Hyphenator::load(str.get(), str.size());
+    return reinterpret_cast<jlong>(hyphenator);
+}
+
+static void nSetLocale(JNIEnv* env, jclass, jlong nativePtr, jstring javaLocaleName,
+        jlong nativeHyphenator) {
     ScopedIcuLocale icuLocale(env, javaLocaleName);
-    Builder* b = reinterpret_cast<Builder*>(nativePtr);
+    LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
+    Hyphenator* hyphenator = reinterpret_cast<Hyphenator*>(nativeHyphenator);
 
     if (icuLocale.valid()) {
-        b->setLocale(icuLocale.locale());
+        b->setLocale(icuLocale.locale(), hyphenator);
     }
 }
 
-float Builder::measureStyleRun(Paint* paint, TypefaceImpl* typeface, size_t start, size_t end,
-        bool isRtl) {
-    Layout layout;
-    int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
-    // TODO: should we provide more context?
-    MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, mTextBuf.data() + start, 0,
-            end - start, end - start);
-    layout.getAdvances(mWidthBuf.data() + start);
-    return layout.getAdvance();
-}
-
-void Builder::addReplacement(size_t start, size_t end, float width) {
-    mWidthBuf[start] = width;
-    std::fill(&mWidthBuf[start + 1], &mWidthBuf[end], 0.0f);
-}
-
 // Basically similar to Paint.getTextRunAdvances but with C++ interface
 static jfloat nAddStyleRun(JNIEnv* env, jclass, jlong nativePtr,
         jlong nativePaint, jlong nativeTypeface, jint start, jint end, jboolean isRtl) {
-    Builder* b = reinterpret_cast<Builder*>(nativePtr);
+    LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
     Paint* paint = reinterpret_cast<Paint*>(nativePaint);
     TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(nativeTypeface);
-    return b->measureStyleRun(paint, typeface, start, end, isRtl);
+    FontCollection *font;
+    MinikinPaint minikinPaint;
+    FontStyle style = MinikinUtils::prepareMinikinPaint(&minikinPaint, &font, paint, typeface);
+    return b->addStyleRun(&minikinPaint, font, style, start, end, isRtl);
 }
 
 // Accept width measurements for the run, passed in from Java
 static void nAddMeasuredRun(JNIEnv* env, jclass, jlong nativePtr,
         jint start, jint end, jfloatArray widths) {
-    Builder* b = reinterpret_cast<Builder*>(nativePtr);
-    env->GetFloatArrayRegion(widths, start, end - start, b->widths() + start);
+    LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
+    env->GetFloatArrayRegion(widths, start, end - start, b->charWidths() + start);
+    b->addStyleRun(nullptr, nullptr, FontStyle{}, start, end, false);
 }
 
 static void nAddReplacementRun(JNIEnv* env, jclass, jlong nativePtr,
         jint start, jint end, jfloat width) {
-    Builder* b = reinterpret_cast<Builder*>(nativePtr);
+    LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
     b->addReplacement(start, end, width);
 }
 
 static void nGetWidths(JNIEnv* env, jclass, jlong nativePtr, jfloatArray widths) {
-    Builder* b = reinterpret_cast<Builder*>(nativePtr);
-    env->SetFloatArrayRegion(widths, 0, b->size(), b->widths());
+    LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
+    env->SetFloatArrayRegion(widths, 0, b->size(), b->charWidths());
 }
 
 static JNINativeMethod gMethods[] = {
@@ -683,13 +168,14 @@
     {"nNewBuilder", "()J", (void*) nNewBuilder},
     {"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
     {"nFinishBuilder", "(J)V", (void*) nFinishBuilder},
-    {"nSetLocale", "(JLjava/lang/String;)V", (void*) nSetLocale},
-    {"nSetText", "(J[CI)V", (void*) nSetText},
+    {"nLoadHyphenator", "(Ljava/lang/String;)J", (void*) nLoadHyphenator},
+    {"nSetLocale", "(JLjava/lang/String;J)V", (void*) nSetLocale},
+    {"nSetupParagraph", "(J[CIFIF[III)V", (void*) nSetupParagraph},
     {"nAddStyleRun", "(JJJIIZ)F", (void*) nAddStyleRun},
     {"nAddMeasuredRun", "(JII[F)V", (void*) nAddMeasuredRun},
     {"nAddReplacementRun", "(JIIF)V", (void*) nAddReplacementRun},
     {"nGetWidths", "(J[F)V", (void*) nGetWidths},
-    {"nComputeLineBreaks", "(JIFIF[IIZLandroid/text/StaticLayout$LineBreaks;[I[F[ZI)I",
+    {"nComputeLineBreaks", "(JLandroid/text/StaticLayout$LineBreaks;[I[F[II)I",
         (void*) nComputeLineBreaks}
 };
 
@@ -700,7 +186,7 @@
 
     gLineBreaks_fieldID.breaks = GetFieldIDOrDie(env, gLineBreaks_class, "breaks", "[I");
     gLineBreaks_fieldID.widths = GetFieldIDOrDie(env, gLineBreaks_class, "widths", "[F");
-    gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[Z");
+    gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[I");
 
     return RegisterMethodsOrDie(env, "android/text/StaticLayout", gMethods, NELEM(gMethods));
 }
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index fff8604..f1c90ea 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -192,6 +192,9 @@
         case HAL_PIXEL_FORMAT_YCbCr_422_I:
             // Name differs, though the value is the same
             return PublicFormat::YUY2;
+        case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+            // Name differs, though the value is the same
+            return PublicFormat::PRIVATE;
         case HAL_PIXEL_FORMAT_Y16:
             // Dataspace-dependent
             switch (dataSpace) {
@@ -216,7 +219,6 @@
             break;
         case HAL_PIXEL_FORMAT_BGRA_8888:
         case HAL_PIXEL_FORMAT_RAW_OPAQUE:
-        case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
             // Not defined in public API
             return PublicFormat::UNKNOWN;
 
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 3d9a9ed..11b3805 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -21,6 +21,7 @@
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
 #include "core_jni_helpers.h"
+#include <ScopedPrimitiveArray.h>
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
@@ -35,6 +36,7 @@
 #include <Animator.h>
 #include <AnimationContext.h>
 #include <IContextFactory.h>
+#include <JankTracker.h>
 #include <RenderNode.h>
 #include <renderthread/CanvasContext.h>
 #include <renderthread/RenderProxy.h>
@@ -219,6 +221,12 @@
     proxy->setTextureAtlas(buffer, map, len);
 }
 
+static void android_view_ThreadedRenderer_setProcessStatsBuffer(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jint fd) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setProcessStatsBuffer(fd);
+}
+
 static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
     RootRenderNode* node = new RootRenderNode(env);
     node->incStrong(0);
@@ -403,6 +411,16 @@
     proxy->dumpProfileInfo(fd, dumpFlags);
 }
 
+static void android_view_ThreadedRenderer_dumpProfileData(JNIEnv* env, jobject clazz,
+        jbyteArray jdata, jobject javaFileDescriptor) {
+    int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
+    ScopedByteArrayRO buffer(env, jdata);
+    if (buffer.get()) {
+        JankTracker::dumpBuffer(buffer.get(), buffer.size(), fd);
+    }
+}
+
+
 // ----------------------------------------------------------------------------
 // Shaders
 // ----------------------------------------------------------------------------
@@ -423,6 +441,7 @@
 
 static JNINativeMethod gMethods[] = {
     { "nSetAtlas", "(JLandroid/view/GraphicBuffer;[J)V",   (void*) android_view_ThreadedRenderer_setAtlas },
+    { "nSetProcessStatsBuffer", "(JI)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer },
     { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
     { "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy },
     { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy },
@@ -449,6 +468,7 @@
     { "nStopDrawing", "(J)V", (void*) android_view_ThreadedRenderer_stopDrawing },
     { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
     { "nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo },
+    { "nDumpProfileData", "([BLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileData },
     { "setupShadersDiskCache", "(Ljava/lang/String;)V",
                 (void*) android_view_ThreadedRenderer_setupShadersDiskCache },
 };
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 3c1993e..9307ff9 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -33,6 +33,7 @@
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
+#include <inttypes.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
@@ -173,7 +174,11 @@
 static install_status_t
 copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntry, const char* fileName)
 {
-    jstring* javaNativeLibPath = (jstring*) arg;
+    void** args = reinterpret_cast<void**>(arg);
+    jstring* javaNativeLibPath = (jstring*) args[0];
+    jboolean extractNativeLibs = *(jboolean*) args[1];
+    jboolean hasNativeBridge = *(jboolean*) args[2];
+
     ScopedUtfChars nativeLibPath(env, *javaNativeLibPath);
 
     size_t uncompLen;
@@ -181,13 +186,31 @@
     long crc;
     time_t modTime;
 
-    if (!zipFile->getEntryInfo(zipEntry, NULL, &uncompLen, NULL, NULL, &when, &crc)) {
+    int method;
+    off64_t offset;
+
+    if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, NULL, &offset, &when, &crc)) {
         ALOGD("Couldn't read zip entry info\n");
         return INSTALL_FAILED_INVALID_APK;
-    } else {
-        struct tm t;
-        ZipUtils::zipTimeToTimespec(when, &t);
-        modTime = mktime(&t);
+    }
+
+    if (!extractNativeLibs) {
+        // check if library is uncompressed and page-aligned
+        if (method != ZipFileRO::kCompressStored) {
+            ALOGD("Library '%s' is compressed - will not be able to open it directly from apk.\n",
+                fileName);
+            return INSTALL_FAILED_INVALID_APK;
+        }
+
+        if (offset % PAGE_SIZE != 0) {
+            ALOGD("Library '%s' is not page-aligned - will not be able to open it directly from"
+                " apk.\n", fileName);
+            return INSTALL_FAILED_INVALID_APK;
+        }
+
+        if (!hasNativeBridge) {
+          return INSTALL_SUCCEEDED;
+        }
     }
 
     // Build local file path
@@ -208,6 +231,9 @@
     }
 
     // Only copy out the native file if it's different.
+    struct tm t;
+    ZipUtils::zipTimeToTimespec(when, &t);
+    modTime = mktime(&t);
     struct stat64 st;
     if (!isFileDifferent(localFileName, uncompLen, modTime, crc, &st)) {
         return INSTALL_SUCCEEDED;
@@ -465,10 +491,12 @@
 
 static jint
 com_android_internal_content_NativeLibraryHelper_copyNativeBinaries(JNIEnv *env, jclass clazz,
-        jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi)
+        jlong apkHandle, jstring javaNativeLibPath, jstring javaCpuAbi,
+        jboolean extractNativeLibs, jboolean hasNativeBridge)
 {
+    void* args[] = { &javaNativeLibPath, &extractNativeLibs, &hasNativeBridge };
     return (jint) iterateOverNativeFiles(env, apkHandle, javaCpuAbi,
-            copyFileIfChanged, &javaNativeLibPath);
+            copyFileIfChanged, reinterpret_cast<void*>(args));
 }
 
 static jlong
@@ -548,7 +576,7 @@
             "(J)V",
             (void *)com_android_internal_content_NativeLibraryHelper_close},
     {"nativeCopyNativeBinaries",
-            "(JLjava/lang/String;Ljava/lang/String;)I",
+            "(JLjava/lang/String;Ljava/lang/String;ZZ)I",
             (void *)com_android_internal_content_NativeLibraryHelper_copyNativeBinaries},
     {"nativeSumNativeBinaries",
             "(JLjava/lang/String;)J",
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 2bfeadb..76db5d3 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -65,9 +65,7 @@
 // Must match values in com.android.internal.os.Zygote.
 enum MountExternalKind {
   MOUNT_EXTERNAL_NONE = 0,
-  MOUNT_EXTERNAL_SINGLEUSER = 1,
-  MOUNT_EXTERNAL_MULTIUSER = 2,
-  MOUNT_EXTERNAL_MULTIUSER_ALL = 3,
+  MOUNT_EXTERNAL_DEFAULT = 1,
 };
 
 static void RuntimeAbort(JNIEnv* env) {
@@ -269,57 +267,16 @@
   // See storage config details at http://source.android.com/tech/storage/
   userid_t user_id = multiuser_get_user_id(uid);
 
-  // Create bind mounts to expose external storage
-  if (mount_mode == MOUNT_EXTERNAL_MULTIUSER || mount_mode == MOUNT_EXTERNAL_MULTIUSER_ALL) {
-    // These paths must already be created by init.rc
-    const char* source = getenv("EMULATED_STORAGE_SOURCE");
-    const char* target = getenv("EMULATED_STORAGE_TARGET");
-    const char* legacy = getenv("EXTERNAL_STORAGE");
-    if (source == NULL || target == NULL || legacy == NULL) {
-      ALOGW("Storage environment undefined; unable to provide external storage");
-      return false;
-    }
+  // Bind mount user-specific storage into place
+  const String8 source(String8::format("/mnt/user/%d", user_id));
+  const String8 target(String8::format("/storage/self"));
 
-    // Prepare source paths
+  if (fs_prepare_dir(source.string(), 0755, 0, 0) == -1) {
+    return false;
+  }
 
-    // /mnt/shell/emulated/0
-    const String8 source_user(String8::format("%s/%d", source, user_id));
-    // /storage/emulated/0
-    const String8 target_user(String8::format("%s/%d", target, user_id));
-
-    if (fs_prepare_dir(source_user.string(), 0000, 0, 0) == -1
-        || fs_prepare_dir(target_user.string(), 0000, 0, 0) == -1) {
-      return false;
-    }
-
-    if (mount_mode == MOUNT_EXTERNAL_MULTIUSER_ALL) {
-      // Mount entire external storage tree for all users
-      if (TEMP_FAILURE_RETRY(mount(source, target, NULL, MS_BIND, NULL)) == -1) {
-        ALOGW("Failed to mount %s to %s: %s", source, target, strerror(errno));
-        return false;
-      }
-    } else {
-      // Only mount user-specific external storage
-      if (TEMP_FAILURE_RETRY(mount(source_user.string(), target_user.string(), NULL,
-                                   MS_BIND, NULL)) == -1) {
-        ALOGW("Failed to mount %s to %s: %s", source_user.string(), target_user.string(),
-              strerror(errno));
-        return false;
-      }
-    }
-
-    if (fs_prepare_dir(legacy, 0000, 0, 0) == -1) {
-        return false;
-    }
-
-    // Finally, mount user-specific path into place for legacy users
-    if (TEMP_FAILURE_RETRY(
-            mount(target_user.string(), legacy, NULL, MS_BIND | MS_REC, NULL)) == -1) {
-      ALOGW("Failed to mount %s to %s: %s", target_user.string(), legacy, strerror(errno));
-      return false;
-    }
-  } else {
-    ALOGW("Mount mode %d unsupported", mount_mode);
+  if (TEMP_FAILURE_RETRY(mount(source.string(), target.string(), NULL, MS_BIND, NULL)) == -1) {
+    ALOGW("Failed to mount %s to %s: %s", source.string(), target.string(), strerror(errno));
     return false;
   }
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4d6b5f6..b0771dd 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -73,10 +73,13 @@
     <protected-broadcast android:name="android.intent.action.USER_BACKGROUND" />
     <protected-broadcast android:name="android.intent.action.USER_FOREGROUND" />
     <protected-broadcast android:name="android.intent.action.USER_SWITCHED" />
+    <protected-broadcast android:name="android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION" />
 
     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED" />
     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGING" />
 
+    <protected-broadcast android:name="android.os.action.SCREEN_BRIGHTNESS_BOOST_CHANGED" />
+
     <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
     <protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" />
     <protected-broadcast android:name="android.app.action.ENTER_DESK_MODE" />
@@ -1218,6 +1221,14 @@
         android:label="@string/permlab_cameraDisableTransmitLed"
         android:description="@string/permdesc_cameraDisableTransmitLed" />
 
+    <!-- Allows sending the camera service notifications about system-wide events.
+        @hide -->
+    <permission android:name="android.permission.CAMERA_SEND_SYSTEM_EVENTS"
+        android:permissionGroup="android.permission-group.CAMERA"
+        android:protectionLevel="signature|system"
+        android:label="@string/permdesc_cameraSendSystemEvent"
+        android:description="@string/permdesc_cameraSendSystemEvent" />
+
     <!-- =========================================== -->
     <!-- Permissions associated with telephony state -->
     <!-- =========================================== -->
@@ -2590,6 +2601,12 @@
         android:description="@string/permdesc_performCdmaProvisioning"
         android:protectionLevel="signature|system" />
 
+    <!-- @SystemApi Allows an application to perform SIM Activation @hide -->
+    <permission android:name="android.permission.PERFORM_SIM_ACTIVATION"
+        android:label="@string/permlab_performSimActivation"
+        android:description="@string/permdesc_performSimActivation"
+        android:protectionLevel="signature|system" />
+
     <!-- @SystemApi Allows enabling/disabling location update notifications from
          the radio.
          <p>Not for use by third-party applications. -->
@@ -2607,7 +2624,8 @@
         android:protectionLevel="signature|system" />
 
     <!-- @SystemApi Allows an application to collect component usage
-         statistics @hide -->
+         statistics
+         <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.PACKAGE_USAGE_STATS"
         android:label="@string/permlab_pkgUsageStats"
         android:description="@string/permdesc_pkgUsageStats"
@@ -2796,6 +2814,23 @@
         android:description="@string/permdesc_bindPackageVerifier"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi @hide Intent filter verifier needs to have this permission before the
+         PackageManager will trust it to verify intent filters.
+    -->
+    <permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"
+        android:label="@string/permlab_intentFilterVerificationAgent"
+        android:description="@string/permdesc_intentFilterVerificationAgent"
+        android:protectionLevel="signature|system" />
+
+    <!-- Must be required by intent filter verifier receiver, to ensure that only the
+         system can interact with it.
+         @hide
+    -->
+    <permission android:name="android.permission.BIND_INTENT_FILTER_VERIFIER"
+        android:label="@string/permlab_bindIntentFilterVerifier"
+        android:description="@string/permdesc_bindIntentFilterVerifier"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows applications to access serial ports via the SerialManager.
          @hide -->
     <permission android:name="android.permission.SERIAL_PORT"
@@ -3158,9 +3193,9 @@
             </intent-filter>
         </receiver>
 
-        <receiver android:name="com.android.server.updates.TZInfoInstallReceiver" >
+        <receiver android:name="com.android.server.updates.TzDataInstallReceiver" >
             <intent-filter>
-                <action android:name="android.intent.action.UPDATE_TZINFO" />
+                <action android:name="android.intent.action.UPDATE_TZDATA" />
                 <data android:scheme="content" android:host="*" android:mimeType="*/*" />
             </intent-filter>
         </receiver>
diff --git a/core/res/res/anim/ic_checkbox_checked_box_inner_merged_animation.xml b/core/res/res/anim/ic_checkbox_checked_box_inner_merged_animation.xml
new file mode 100644
index 0000000..7be32af
--- /dev/null
+++ b/core/res/res/anim/ic_checkbox_checked_box_inner_merged_animation.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="166"
+            android:propertyName="pathData"
+            android:valueFrom="M 0.0,-1.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M 0.0,-1.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="333"
+            android:propertyName="pathData"
+            android:valueFrom="M 0.0,-1.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueTo="M -7.0,-7.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,14.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-14.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+            android:valueType="pathType"
+            android:interpolator="@interpolator/ic_checkbox_checked_animation_interpolator_1" />
+    </set>
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="133"
+            android:propertyName="fillAlpha"
+            android:valueFrom="0.0"
+            android:valueTo="0.0"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="33"
+            android:propertyName="fillAlpha"
+            android:valueFrom="0.0"
+            android:valueTo="1.0"
+            android:interpolator="@interpolator/ic_checkbox_checked_animation_interpolator_0" />
+    </set>
+</set>
diff --git a/core/res/res/anim/ic_checkbox_checked_check_path_merged_animation.xml b/core/res/res/anim/ic_checkbox_checked_check_path_merged_animation.xml
new file mode 100644
index 0000000..fcba2c8
--- /dev/null
+++ b/core/res/res/anim/ic_checkbox_checked_check_path_merged_animation.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="166"
+        android:propertyName="pathData"
+        android:valueFrom="M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M -2.0,5.00001525879 c 0.0,0.0 -5.0,-5.00001525879 -5.0,-5.00001525879 c 0.0,0.0 1.41409301758,-1.41409301758 1.41409301758,-1.41409301758 c 0.0,0.0 3.58590698242,3.58601379395 3.58590698242,3.58601379395 c 0.0,0.0 7.58590698242,-7.58601379395 7.58590698242,-7.58601379395 c 0.0,0.0 1.41409301758,1.41409301758 1.41409301758,1.41409301758 c 0.0,0.0 -9.0,9.00001525879 -9.0,9.00001525879 Z"
+        android:valueTo="M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M 0.0,1.42500305176 c 0.0,0.0 -1.4234161377,-1.40159606934 -1.4234161377,-1.40159606934 c 0.0,0.0 1.41409301758,-1.41409301758 1.41409301758,-1.41409301758 c 0.0,0.0 0.00932312011719,-0.0124053955078 0.00932312011719,-0.0124053955078 c 0.0,0.0 0.0234069824219,-0.0235137939453 0.0234069824219,-0.0235137939453 c 0.0,0.0 1.41409301758,1.41409301758 1.41409301758,1.41409301758 c 0.0,0.0 -1.4375,1.43751525879 -1.4375,1.43751525879 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/ic_checkbox_checked_animation_interpolator_1" />
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="133"
+            android:propertyName="fillAlpha"
+            android:valueFrom="1.0"
+            android:valueTo="1.0"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="33"
+            android:propertyName="fillAlpha"
+            android:valueFrom="1.0"
+            android:valueTo="0.0"
+            android:interpolator="@interpolator/ic_checkbox_checked_animation_interpolator_0" />
+    </set>
+</set>
diff --git a/core/res/res/anim/ic_checkbox_checked_icon_null_animation.xml b/core/res/res/anim/ic_checkbox_checked_icon_null_animation.xml
new file mode 100644
index 0000000..312003f
--- /dev/null
+++ b/core/res/res/anim/ic_checkbox_checked_icon_null_animation.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="166"
+            android:propertyName="scaleX"
+            android:valueFrom="0.2"
+            android:valueTo="0.18"
+            android:interpolator="@interpolator/ic_checkbox_checked_animation_interpolator_1" />
+        <objectAnimator
+            android:duration="333"
+            android:propertyName="scaleX"
+            android:valueFrom="0.18"
+            android:valueTo="0.2"
+            android:interpolator="@interpolator/ic_checkbox_checked_animation_interpolator_1" />
+    </set>
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="166"
+            android:propertyName="scaleY"
+            android:valueFrom="0.2"
+            android:valueTo="0.18"
+            android:interpolator="@interpolator/ic_checkbox_checked_animation_interpolator_1" />
+        <objectAnimator
+            android:duration="333"
+            android:propertyName="scaleY"
+            android:valueFrom="0.18"
+            android:valueTo="0.2"
+            android:interpolator="@interpolator/ic_checkbox_checked_animation_interpolator_1" />
+    </set>
+</set>
diff --git a/core/res/res/anim/ic_checkbox_unchecked_box_inner_merged_animation.xml b/core/res/res/anim/ic_checkbox_unchecked_box_inner_merged_animation.xml
new file mode 100644
index 0000000..b5ad5e9d
--- /dev/null
+++ b/core/res/res/anim/ic_checkbox_unchecked_box_inner_merged_animation.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="166"
+        android:propertyName="pathData"
+        android:valueFrom="M -7.0,-7.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,14.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-14.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueTo="M 0.0,-0.05 l 0.0,0.0 c 0.02761423749,0.0 0.05,0.02238576251 0.05,0.05 l 0.0,0.0 c 0.0,0.02761423749 -0.02238576251,0.05 -0.05,0.05 l 0.0,0.0 c -0.02761423749,0.0 -0.05,-0.02238576251 -0.05,-0.05 l 0.0,0.0 c 0.0,-0.02761423749 0.02238576251,-0.05 0.05,-0.05 Z M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+        android:valueType="pathType"
+        android:interpolator="@interpolator/ic_checkbox_unchecked_animation_interpolator_1" />
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="166"
+            android:propertyName="fillAlpha"
+            android:valueFrom="1.0"
+            android:valueTo="1.0"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="33"
+            android:propertyName="fillAlpha"
+            android:valueFrom="1.0"
+            android:valueTo="0.0"
+            android:interpolator="@interpolator/ic_checkbox_unchecked_animation_interpolator_0" />
+    </set>
+</set>
diff --git a/core/res/res/anim/ic_checkbox_unchecked_box_outer_merged_animation.xml b/core/res/res/anim/ic_checkbox_unchecked_box_outer_merged_animation.xml
new file mode 100644
index 0000000..066971a
--- /dev/null
+++ b/core/res/res/anim/ic_checkbox_unchecked_box_outer_merged_animation.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="200"
+            android:propertyName="pathData"
+            android:valueFrom="M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M -2.0,5.00001525879 c 0.0,0.0 -1.4234161377,-1.40159606934 -1.4234161377,-1.40159606934 c 0.0,0.0 1.41409301758,-1.41409301758 1.41409301758,-1.41409301758 c 0.0,0.0 0.00932312011719,-0.0124053955078 0.00932312011719,-0.0124053955078 c 0.0,0.0 0.0234069824219,-0.0235137939453 0.0234069824219,-0.0235137939453 c 0.0,0.0 1.41409301758,1.41409301758 1.41409301758,1.41409301758 c 0.0,0.0 -1.4375,1.43751525879 -1.4375,1.43751525879 Z"
+            android:valueTo="M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M -2.0,5.00001525879 c 0.0,0.0 -1.4234161377,-1.40159606934 -1.4234161377,-1.40159606934 c 0.0,0.0 1.41409301758,-1.41409301758 1.41409301758,-1.41409301758 c 0.0,0.0 0.00932312011719,-0.0124053955078 0.00932312011719,-0.0124053955078 c 0.0,0.0 0.0234069824219,-0.0235137939453 0.0234069824219,-0.0235137939453 c 0.0,0.0 1.41409301758,1.41409301758 1.41409301758,1.41409301758 c 0.0,0.0 -1.4375,1.43751525879 -1.4375,1.43751525879 Z"
+            android:valueType="pathType"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="300"
+            android:propertyName="pathData"
+            android:valueFrom="M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M -2.0,5.00001525879 c 0.0,0.0 -1.4234161377,-1.40159606934 -1.4234161377,-1.40159606934 c 0.0,0.0 1.41409301758,-1.41409301758 1.41409301758,-1.41409301758 c 0.0,0.0 0.00932312011719,-0.0124053955078 0.00932312011719,-0.0124053955078 c 0.0,0.0 0.0234069824219,-0.0235137939453 0.0234069824219,-0.0235137939453 c 0.0,0.0 1.41409301758,1.41409301758 1.41409301758,1.41409301758 c 0.0,0.0 -1.4375,1.43751525879 -1.4375,1.43751525879 Z"
+            android:valueTo="M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M -2.0,5.00001525879 c 0.0,0.0 -5.0,-5.00001525879 -5.0,-5.00001525879 c 0.0,0.0 1.41409301758,-1.41409301758 1.41409301758,-1.41409301758 c 0.0,0.0 3.58590698242,3.58601379395 3.58590698242,3.58601379395 c 0.0,0.0 7.58590698242,-7.58601379395 7.58590698242,-7.58601379395 c 0.0,0.0 1.41409301758,1.41409301758 1.41409301758,1.41409301758 c 0.0,0.0 -9.0,9.00001525879 -9.0,9.00001525879 Z"
+            android:valueType="pathType"
+            android:interpolator="@interpolator/ic_checkbox_unchecked_animation_interpolator_1" />
+    </set>
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="166"
+            android:propertyName="fillAlpha"
+            android:valueFrom="0.0"
+            android:valueTo="0.0"
+            android:interpolator="@android:interpolator/linear" />
+        <objectAnimator
+            android:duration="33"
+            android:propertyName="fillAlpha"
+            android:valueFrom="0.0"
+            android:valueTo="1.0"
+            android:interpolator="@interpolator/ic_checkbox_unchecked_animation_interpolator_0" />
+    </set>
+</set>
diff --git a/core/res/res/anim/ic_checkbox_unchecked_icon_null_animation.xml b/core/res/res/anim/ic_checkbox_unchecked_icon_null_animation.xml
new file mode 100644
index 0000000..fc40d47
--- /dev/null
+++ b/core/res/res/anim/ic_checkbox_unchecked_icon_null_animation.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<set
+    xmlns:android="http://schemas.android.com/apk/res/android" >
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="200"
+            android:propertyName="scaleX"
+            android:valueFrom="0.2"
+            android:valueTo="0.18"
+            android:interpolator="@interpolator/ic_checkbox_unchecked_animation_interpolator_1" />
+        <objectAnimator
+            android:duration="300"
+            android:propertyName="scaleX"
+            android:valueFrom="0.18"
+            android:valueTo="0.2"
+            android:interpolator="@interpolator/ic_checkbox_unchecked_animation_interpolator_1" />
+    </set>
+    <set
+        android:ordering="sequentially" >
+        <objectAnimator
+            android:duration="200"
+            android:propertyName="scaleY"
+            android:valueFrom="0.2"
+            android:valueTo="0.18"
+            android:interpolator="@interpolator/ic_checkbox_unchecked_animation_interpolator_1" />
+        <objectAnimator
+            android:duration="300"
+            android:propertyName="scaleY"
+            android:valueFrom="0.18"
+            android:valueTo="0.2"
+            android:interpolator="@interpolator/ic_checkbox_unchecked_animation_interpolator_1" />
+    </set>
+</set>
diff --git a/core/res/res/color/primary_text_secondary_when_activated_material.xml b/core/res/res/color/primary_text_secondary_when_activated_material.xml
new file mode 100644
index 0000000..7ab4a2e
--- /dev/null
+++ b/core/res/res/color/primary_text_secondary_when_activated_material.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_activated="true"
+        android:color="?attr/textColorPrimary" />
+    <item
+        android:color="?attr/textColorSecondary" />
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_header_text_material.xml b/core/res/res/color/primary_text_secondary_when_activated_material_inverse.xml
similarity index 86%
rename from core/res/res/color/date_picker_header_text_material.xml
rename to core/res/res/color/primary_text_secondary_when_activated_material_inverse.xml
index cda894b..baa8958 100644
--- a/core/res/res/color/date_picker_header_text_material.xml
+++ b/core/res/res/color/primary_text_secondary_when_activated_material_inverse.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item
-        android:state_selected="true"
+        android:state_activated="true"
         android:color="?attr/textColorPrimaryInverse" />
     <item
         android:color="?attr/textColorSecondaryInverse" />
diff --git a/core/res/res/color/time_picker_header_text_material.xml b/core/res/res/color/time_picker_header_text_material.xml
deleted file mode 100644
index cda894b..0000000
--- a/core/res/res/color/time_picker_header_text_material.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item
-        android:state_selected="true"
-        android:color="?attr/textColorPrimaryInverse" />
-    <item
-        android:color="?attr/textColorSecondaryInverse" />
-</selector>
\ No newline at end of file
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_000.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_000.png
deleted file mode 100644
index 3cb4073..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_001.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_001.png
deleted file mode 100644
index 8fd1480..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_002.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_002.png
deleted file mode 100644
index d35b579..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_003.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_003.png
deleted file mode 100644
index 543c6bc..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_004.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_004.png
deleted file mode 100644
index 4fc3c40..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_005.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_005.png
deleted file mode 100644
index c184535..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_006.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_006.png
deleted file mode 100644
index 9f9dd43..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_007.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_007.png
deleted file mode 100644
index 8c629ce..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_008.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_008.png
deleted file mode 100644
index 81134b5..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_009.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_009.png
deleted file mode 100644
index baa5860..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_010.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_010.png
deleted file mode 100644
index d7e28366..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_011.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_011.png
deleted file mode 100644
index 6f24795..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_012.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_012.png
deleted file mode 100644
index 22f997d..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_013.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_013.png
deleted file mode 100644
index 85f4471..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_014.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_014.png
deleted file mode 100644
index ad483c9..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_015.png b/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_015.png
deleted file mode 100644
index f24c2fb..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_off_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_000.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_000.png
deleted file mode 100644
index 7a9e9bd..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_001.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_001.png
deleted file mode 100644
index af04902..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_002.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_002.png
deleted file mode 100644
index 32a6e94..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_003.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_003.png
deleted file mode 100644
index c1b4b37..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_004.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_004.png
deleted file mode 100644
index 34d3ade..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_005.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_005.png
deleted file mode 100644
index 3d5db53..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_006.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_006.png
deleted file mode 100644
index ea35437..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_007.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_007.png
deleted file mode 100644
index 48744f8..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_008.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_008.png
deleted file mode 100644
index f654517..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_009.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_009.png
deleted file mode 100644
index 16f959a..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_010.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_010.png
deleted file mode 100644
index 98c754b..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_011.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_011.png
deleted file mode 100644
index 5827dc2..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_012.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_012.png
deleted file mode 100644
index 9850d74..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_013.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_013.png
deleted file mode 100644
index 03ab06b..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_014.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_014.png
deleted file mode 100644
index 11cdd88..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_015.png b/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_015.png
deleted file mode 100644
index 874edbf..0000000
--- a/core/res/res/drawable-hdpi/btn_check_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_000.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_000.png
deleted file mode 100644
index 9759818..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_001.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_001.png
deleted file mode 100644
index 4eb2c4f..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_002.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_002.png
deleted file mode 100644
index e6d6b42..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_003.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_003.png
deleted file mode 100644
index 03cb23a..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_004.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_004.png
deleted file mode 100644
index bfe3c3d..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_005.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_005.png
deleted file mode 100644
index 65bdf42..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_006.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_006.png
deleted file mode 100644
index 44f9614b..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_007.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_007.png
deleted file mode 100644
index cf8ec38..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_008.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_008.png
deleted file mode 100644
index 4d624b3..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_009.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_009.png
deleted file mode 100644
index 7c4eb7f..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_010.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_010.png
deleted file mode 100644
index e90dd31..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_011.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_011.png
deleted file mode 100644
index 831c0e8..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_012.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_012.png
deleted file mode 100644
index 7355dfd..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_013.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_013.png
deleted file mode 100644
index be71a69..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_014.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_014.png
deleted file mode 100644
index a4a185b..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_015.png b/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_015.png
deleted file mode 100644
index 8d0386f..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_off_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_000.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_000.png
deleted file mode 100644
index 70793c4..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_001.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_001.png
deleted file mode 100644
index 632082b..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_002.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_002.png
deleted file mode 100644
index e7fc5fb..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_003.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_003.png
deleted file mode 100644
index 91a0a33..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_004.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_004.png
deleted file mode 100644
index 3bd90d6..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_005.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_005.png
deleted file mode 100644
index 5ac39ec..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_006.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_006.png
deleted file mode 100644
index 4181983..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_007.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_007.png
deleted file mode 100644
index c8b04df..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_008.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_008.png
deleted file mode 100644
index b7b3a9f..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_009.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_009.png
deleted file mode 100644
index 62bc4ed..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_010.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_010.png
deleted file mode 100644
index ac463ad..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_011.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_011.png
deleted file mode 100644
index 12b605d..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_012.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_012.png
deleted file mode 100644
index 63a3c6a..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_013.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_013.png
deleted file mode 100644
index 17660c4..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_014.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_014.png
deleted file mode 100644
index 7d9de3d..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_015.png b/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_015.png
deleted file mode 100644
index 8aa1be2..0000000
--- a/core/res/res/drawable-mdpi/btn_check_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_000.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_000.png
deleted file mode 100644
index 2347643..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_001.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_001.png
deleted file mode 100644
index 70aaa01..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_002.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_002.png
deleted file mode 100644
index 01e498a..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_003.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_003.png
deleted file mode 100644
index 71d1cf7..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_004.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_004.png
deleted file mode 100644
index d1e7b1d..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_005.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_005.png
deleted file mode 100644
index 7db7d06..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_006.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_006.png
deleted file mode 100644
index dadb62e..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_007.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_007.png
deleted file mode 100644
index f87f744..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_008.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_008.png
deleted file mode 100644
index be99d87..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_009.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_009.png
deleted file mode 100644
index f83bc05..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_010.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_010.png
deleted file mode 100644
index 870071d..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_011.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_011.png
deleted file mode 100644
index 3a18414..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_012.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_012.png
deleted file mode 100644
index f3d1187..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_013.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_013.png
deleted file mode 100644
index 4078cca..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_014.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_014.png
deleted file mode 100644
index d4849b5..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_015.png b/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_015.png
deleted file mode 100644
index 6e2af72..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_off_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_000.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_000.png
deleted file mode 100644
index 9244174..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_001.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_001.png
deleted file mode 100644
index 8c7fe95..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_002.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_002.png
deleted file mode 100644
index 71eb1d0..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_003.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_003.png
deleted file mode 100644
index 613f38a..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_004.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_004.png
deleted file mode 100644
index 2d20ccc..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_005.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_005.png
deleted file mode 100644
index 407f78d..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_006.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_006.png
deleted file mode 100644
index 1bf24b0..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_007.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_007.png
deleted file mode 100644
index a450bd0..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_008.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_008.png
deleted file mode 100644
index 63ba593..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_009.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_009.png
deleted file mode 100644
index 6d05e5a..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_010.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_010.png
deleted file mode 100644
index 1c8cd8f..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_011.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_011.png
deleted file mode 100644
index b8bc564..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_012.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_012.png
deleted file mode 100644
index 3d80128..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_013.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_013.png
deleted file mode 100644
index c21dfba..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_014.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_014.png
deleted file mode 100644
index 2dfe90d..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_015.png b/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_015.png
deleted file mode 100644
index 5f40d73..0000000
--- a/core/res/res/drawable-xhdpi/btn_check_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_000.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_000.png
deleted file mode 100644
index b754381..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_001.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_001.png
deleted file mode 100644
index 517d7a7..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_002.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_002.png
deleted file mode 100644
index 2c1d5b6..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_003.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_003.png
deleted file mode 100644
index 0c6ff7e..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_004.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_004.png
deleted file mode 100644
index 0796601..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_005.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_005.png
deleted file mode 100644
index 9b4e0f8..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_006.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_006.png
deleted file mode 100644
index 25767eb..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_007.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_007.png
deleted file mode 100644
index cd0951f..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_008.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_008.png
deleted file mode 100644
index 9ae8165..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_009.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_009.png
deleted file mode 100644
index efd9bc6..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_010.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_010.png
deleted file mode 100644
index fccbc9d..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_011.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_011.png
deleted file mode 100644
index dddafca..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_012.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_012.png
deleted file mode 100644
index 7e37433..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_013.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_013.png
deleted file mode 100644
index 9bc22de..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_014.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_014.png
deleted file mode 100644
index 507ed10..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_015.png b/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_015.png
deleted file mode 100644
index 6a21c7f..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_off_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_000.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_000.png
deleted file mode 100644
index 0d544d9..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_001.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_001.png
deleted file mode 100644
index 39da0ac..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_002.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_002.png
deleted file mode 100644
index d5ada12..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_003.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_003.png
deleted file mode 100644
index d4e096c..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_004.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_004.png
deleted file mode 100644
index 468a9b4..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_005.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_005.png
deleted file mode 100644
index ea3cd2e..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_006.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_006.png
deleted file mode 100644
index 0652cb0..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_007.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_007.png
deleted file mode 100644
index 768d2b0..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_008.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_008.png
deleted file mode 100644
index 1d06a90..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_009.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_009.png
deleted file mode 100644
index 8a70a80..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_010.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_010.png
deleted file mode 100644
index bf9ec7f..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_011.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_011.png
deleted file mode 100644
index cff07b9..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_012.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_012.png
deleted file mode 100644
index 40f997e..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_013.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_013.png
deleted file mode 100644
index 6ba84ec..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_014.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_014.png
deleted file mode 100644
index 766610e..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_015.png b/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_015.png
deleted file mode 100644
index 810a029..0000000
--- a/core/res/res/drawable-xxhdpi/btn_check_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_000.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_000.png
deleted file mode 100644
index f0ff1a7..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_001.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_001.png
deleted file mode 100644
index b382df3..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_002.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_002.png
deleted file mode 100644
index 8cb4ce2..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_003.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_003.png
deleted file mode 100644
index 4db2b01..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_004.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_004.png
deleted file mode 100644
index 8c4709b..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_005.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_005.png
deleted file mode 100644
index 1ad960a..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_006.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_006.png
deleted file mode 100644
index e47cc20..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_007.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_007.png
deleted file mode 100644
index c4d0d51..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_008.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_008.png
deleted file mode 100644
index 915d56a..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_009.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_009.png
deleted file mode 100644
index 85795cb..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_010.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_010.png
deleted file mode 100644
index 157fd91..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_011.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_011.png
deleted file mode 100644
index 9d446de..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_012.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_012.png
deleted file mode 100644
index dfac1f0..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_013.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_013.png
deleted file mode 100644
index aed6c08..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_014.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_014.png
deleted file mode 100644
index 1b8bd6b..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_015.png b/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_015.png
deleted file mode 100644
index 5dd0e5b..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_off_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_000.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_000.png
deleted file mode 100644
index 5dd0e5b..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_000.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_001.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_001.png
deleted file mode 100644
index 1a31ad9..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_001.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_002.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_002.png
deleted file mode 100644
index 63c7f12..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_002.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_003.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_003.png
deleted file mode 100644
index 847dd08..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_003.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_004.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_004.png
deleted file mode 100644
index b93f3cc..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_004.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_005.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_005.png
deleted file mode 100644
index 1e3dea7..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_005.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_006.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_006.png
deleted file mode 100644
index 5a85238..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_006.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_007.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_007.png
deleted file mode 100644
index 35960ca..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_007.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_008.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_008.png
deleted file mode 100644
index 6db5555..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_008.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_009.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_009.png
deleted file mode 100644
index a9c5851..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_009.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_010.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_010.png
deleted file mode 100644
index 38465bd..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_010.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_011.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_011.png
deleted file mode 100644
index 15942dc..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_011.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_012.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_012.png
deleted file mode 100644
index 67d0d64..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_012.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_013.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_013.png
deleted file mode 100644
index 69b5c1b..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_013.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_014.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_014.png
deleted file mode 100644
index 0e5d331..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_014.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_015.png b/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_015.png
deleted file mode 100644
index f0ff1a7..0000000
--- a/core/res/res/drawable-xxxhdpi/btn_check_to_on_mtrl_015.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/btn_check_material_anim.xml b/core/res/res/drawable/btn_check_material_anim.xml
index 24df879..41caa4e 100644
--- a/core/res/res/drawable/btn_check_material_anim.xml
+++ b/core/res/res/drawable/btn_check_material_anim.xml
@@ -15,159 +15,15 @@
 -->
 
 <animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false" android:state_checked="true">
-        <bitmap android:src="@drawable/btn_check_to_on_mtrl_015"
-                android:tint="?attr/colorControlNormal"
-                android:alpha="?attr/disabledAlpha" />
-    </item>
-    <item android:state_enabled="false">
-        <bitmap android:src="@drawable/btn_check_to_on_mtrl_000"
-                android:tint="?attr/colorControlNormal"
-                android:alpha="?attr/disabledAlpha" />
-    </item>
-    <item android:state_checked="true" android:id="@+id/on">
-        <bitmap android:src="@drawable/btn_check_to_on_mtrl_015"
-                android:tint="?attr/colorControlActivated" />
-    </item>
-    <item android:id="@+id/off">
-        <bitmap android:src="@drawable/btn_check_to_on_mtrl_000"
-                android:tint="?attr/colorControlNormal" />
-    </item>
-    <transition android:fromId="@+id/off" android:toId="@+id/on">
-        <animation-list>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_000"
-                        android:tint="?attr/colorControlNormal" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_001"
-                        android:tint="?attr/colorControlNormal" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_002"
-                        android:tint="?attr/colorControlNormal" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_003"
-                        android:tint="?attr/colorControlNormal" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_004"
-                        android:tint="?attr/colorControlNormal" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_005"
-                        android:tint="?attr/colorControlNormal" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_006"
-                        android:tint="?attr/colorControlNormal" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_007"
-                        android:tint="?attr/colorControlNormal" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_008"
-                        android:tint="?attr/colorControlActivated" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_009"
-                        android:tint="?attr/colorControlActivated" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_010"
-                        android:tint="?attr/colorControlActivated" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_011"
-                        android:tint="?attr/colorControlActivated" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_012"
-                        android:tint="?attr/colorControlActivated" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_013"
-                        android:tint="?attr/colorControlActivated" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_014"
-                        android:tint="?attr/colorControlActivated" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_on_mtrl_015"
-                        android:tint="?attr/colorControlActivated" />
-            </item>
-        </animation-list>
-    </transition>
-    <transition android:fromId="@+id/on" android:toId="@+id/off">
-        <animation-list>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_000"
-                        android:tint="?attr/colorControlActivated" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_001"
-                        android:tint="?attr/colorControlActivated" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_002"
-                        android:tint="?attr/colorControlActivated" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_003"
-                        android:tint="?attr/colorControlActivated" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_004"
-                        android:tint="?attr/colorControlActivated" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_005"
-                        android:tint="?attr/colorControlActivated" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_006"
-                        android:tint="?attr/colorControlActivated" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_007"
-                        android:tint="?attr/colorControlActivated" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_008"
-                        android:tint="?attr/colorControlNormal" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_009"
-                        android:tint="?attr/colorControlNormal" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_010"
-                        android:tint="?attr/colorControlNormal" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_011"
-                        android:tint="?attr/colorControlNormal" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_012"
-                        android:tint="?attr/colorControlNormal" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_013"
-                        android:tint="?attr/colorControlNormal" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_014"
-                        android:tint="?attr/colorControlNormal" />
-            </item>
-            <item android:duration="15">
-                <bitmap android:src="@drawable/btn_check_to_off_mtrl_015"
-                        android:tint="?attr/colorControlNormal" />
-            </item>
-        </animation-list>
-    </transition>
+    <item android:state_checked="true" android:id="@+id/on"
+        android:drawable="@drawable/ic_checkbox_checked" />
+    <item android:id="@+id/off"
+        android:drawable="@drawable/ic_checkbox_unchecked" />
+
+    <transition android:fromId="@+id/off" android:toId="@+id/on"
+        android:drawable="@drawable/ic_checkbox_unchecked_animation" />
+
+    <transition android:fromId="@+id/on" android:toId="@+id/off"
+        android:drawable="@drawable/ic_checkbox_checked_animation" />
 </animated-selector>
 
diff --git a/core/res/res/drawable/ic_checkbox_checked.xml b/core/res/res/drawable/ic_checkbox_checked.xml
new file mode 100644
index 0000000..4764115
--- /dev/null
+++ b/core/res/res/drawable/ic_checkbox_checked.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:name="ic_checkbox_checked"
+    android:width="32dp"
+    android:viewportWidth="48"
+    android:height="32dp"
+    android:viewportHeight="48"
+    android:tint="?attr/colorControlNormal" >
+    <group
+        android:name="icon_null"
+        android:translateX="24"
+        android:translateY="24"
+        android:scaleX="0.2"
+        android:scaleY="0.2" >
+        <group
+            android:name="check"
+            android:scaleX="7.5"
+            android:scaleY="7.5" >
+            <path
+                android:name="check_path_merged"
+                android:pathData="M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M -2.0,5.00001525879 c 0.0,0.0 -5.0,-5.00001525879 -5.0,-5.00001525879 c 0.0,0.0 1.41409301758,-1.41409301758 1.41409301758,-1.41409301758 c 0.0,0.0 3.58590698242,3.58601379395 3.58590698242,3.58601379395 c 0.0,0.0 7.58590698242,-7.58601379395 7.58590698242,-7.58601379395 c 0.0,0.0 1.41409301758,1.41409301758 1.41409301758,1.41409301758 c 0.0,0.0 -9.0,9.00001525879 -9.0,9.00001525879 Z"
+                android:fillColor="#FF000000" />
+        </group>
+        <group
+            android:name="box_dilate"
+            android:scaleX="7.5"
+            android:scaleY="7.5" >
+            <path
+                android:name="box_inner_merged"
+                android:pathData="M 0.0,-1.0 l 0.0,0.0 c 0.5522847498,0.0 1.0,0.4477152502 1.0,1.0 l 0.0,0.0 c 0.0,0.5522847498 -0.4477152502,1.0 -1.0,1.0 l 0.0,0.0 c -0.5522847498,0.0 -1.0,-0.4477152502 -1.0,-1.0 l 0.0,0.0 c 0.0,-0.5522847498 0.4477152502,-1.0 1.0,-1.0 Z M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+                android:fillColor="#FF000000"
+                android:fillAlpha="0" />
+        </group>
+    </group>
+</vector>
diff --git a/core/res/res/drawable/ic_checkbox_checked_animation.xml b/core/res/res/drawable/ic_checkbox_checked_animation.xml
new file mode 100644
index 0000000..af5eeee
--- /dev/null
+++ b/core/res/res/drawable/ic_checkbox_checked_animation.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<animated-vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/ic_checkbox_checked" >
+    <target
+        android:name="icon_null"
+        android:animation="@anim/ic_checkbox_checked_icon_null_animation" />
+    <target
+        android:name="check_path_merged"
+        android:animation="@anim/ic_checkbox_checked_check_path_merged_animation" />
+    <target
+        android:name="box_inner_merged"
+        android:animation="@anim/ic_checkbox_checked_box_inner_merged_animation" />
+</animated-vector>
diff --git a/core/res/res/drawable/ic_checkbox_unchecked.xml b/core/res/res/drawable/ic_checkbox_unchecked.xml
new file mode 100644
index 0000000..410f0bc
--- /dev/null
+++ b/core/res/res/drawable/ic_checkbox_unchecked.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:name="ic_checkbox_unchecked"
+    android:width="32dp"
+    android:viewportWidth="48"
+    android:height="32dp"
+    android:viewportHeight="48"
+    android:tint="?attr/colorControlNormal" >
+    <group
+        android:name="icon_null"
+        android:translateX="24"
+        android:translateY="24"
+        android:scaleX="0.2"
+        android:scaleY="0.2" >
+        <group
+            android:name="check"
+            android:scaleX="7.5"
+            android:scaleY="7.5" >
+            <path
+                android:name="box_outer_merged"
+                android:pathData="M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M -2.0,5.00001525879 c 0.0,0.0 -1.4234161377,-1.40159606934 -1.4234161377,-1.40159606934 c 0.0,0.0 1.41409301758,-1.41409301758 1.41409301758,-1.41409301758 c 0.0,0.0 0.00932312011719,-0.0124053955078 0.00932312011719,-0.0124053955078 c 0.0,0.0 0.0234069824219,-0.0235137939453 0.0234069824219,-0.0235137939453 c 0.0,0.0 1.41409301758,1.41409301758 1.41409301758,1.41409301758 c 0.0,0.0 -1.4375,1.43751525879 -1.4375,1.43751525879 Z"
+                android:fillColor="#FF000000"
+                android:fillAlpha="0" />
+        </group>
+        <group
+            android:name="box_dilate"
+            android:scaleX="7.5"
+            android:scaleY="7.5" >
+            <path
+                android:name="box_inner_merged"
+                android:pathData="M -7.0,-7.0 l 14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,14.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -14.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-14.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z M 7.0,-9.0 c 0.0,0.0 -14.0,0.0 -14.0,0.0 c -1.1044921875,0.0 -2.0,0.8955078125 -2.0,2.0 c 0.0,0.0 0.0,14.0 0.0,14.0 c 0.0,1.1044921875 0.8955078125,2.0 2.0,2.0 c 0.0,0.0 14.0,0.0 14.0,0.0 c 1.1044921875,0.0 2.0,-0.8955078125 2.0,-2.0 c 0.0,0.0 0.0,-14.0 0.0,-14.0 c 0.0,-1.1044921875 -0.8955078125,-2.0 -2.0,-2.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z"
+                android:fillColor="#FF000000" />
+        </group>
+    </group>
+</vector>
diff --git a/core/res/res/drawable/ic_checkbox_unchecked_animation.xml b/core/res/res/drawable/ic_checkbox_unchecked_animation.xml
new file mode 100644
index 0000000..605fce1
--- /dev/null
+++ b/core/res/res/drawable/ic_checkbox_unchecked_animation.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<animated-vector
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/ic_checkbox_unchecked" >
+    <target
+        android:name="icon_null"
+        android:animation="@anim/ic_checkbox_unchecked_icon_null_animation" />
+    <target
+        android:name="box_outer_merged"
+        android:animation="@anim/ic_checkbox_unchecked_box_outer_merged_animation" />
+    <target
+        android:name="box_inner_merged"
+        android:animation="@anim/ic_checkbox_unchecked_box_inner_merged_animation" />
+</animated-vector>
diff --git a/core/res/res/drawable/ic_chevron_left.xml b/core/res/res/drawable/ic_chevron_left.xml
new file mode 100644
index 0000000..dc24706
--- /dev/null
+++ b/core/res/res/drawable/ic_chevron_left.xml
@@ -0,0 +1,25 @@
+<!--
+    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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.41 7.41L14 6l-6 6 6 6 1.41,-1.41L10.83 12z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_chevron_right.xml b/core/res/res/drawable/ic_chevron_right.xml
new file mode 100644
index 0000000..4e6d8e3
--- /dev/null
+++ b/core/res/res/drawable/ic_chevron_right.xml
@@ -0,0 +1,25 @@
+<!--
+    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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6,-6z"/>
+</vector>
diff --git a/core/res/res/drawable/pointer_arrow_icon.xml b/core/res/res/drawable/pointer_arrow_icon.xml
index 8f7d658..72af0c1 100644
--- a/core/res/res/drawable/pointer_arrow_icon.xml
+++ b/core/res/res/drawable/pointer_arrow_icon.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
     android:bitmap="@drawable/pointer_arrow"
-    android:hotSpotX="6dp"
-    android:hotSpotY="6dp" />
+    android:hotSpotX="5dp"
+    android:hotSpotY="5dp" />
diff --git a/core/res/res/drawable/time_picker_header_material.xml b/core/res/res/interpolator/ic_checkbox_checked_animation_interpolator_0.xml
similarity index 71%
copy from core/res/res/drawable/time_picker_header_material.xml
copy to core/res/res/interpolator/ic_checkbox_checked_animation_interpolator_0.xml
index ef2068a..ceac663 100644
--- a/core/res/res/drawable/time_picker_header_material.xml
+++ b/core/res/res/interpolator/ic_checkbox_checked_animation_interpolator_0.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ 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.
@@ -14,7 +15,6 @@
      limitations under the License.
 -->
 
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-        android:color="?attr/colorControlHighlight">
-    <item android:drawable="?attr/colorAccent" />
-</ripple>
+<pathInterpolator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 l 1.0,0.0 l 0.0,1.0" />
diff --git a/core/res/res/drawable/time_picker_header_material.xml b/core/res/res/interpolator/ic_checkbox_checked_animation_interpolator_1.xml
similarity index 71%
copy from core/res/res/drawable/time_picker_header_material.xml
copy to core/res/res/interpolator/ic_checkbox_checked_animation_interpolator_1.xml
index ef2068a..26bc8ad 100644
--- a/core/res/res/drawable/time_picker_header_material.xml
+++ b/core/res/res/interpolator/ic_checkbox_checked_animation_interpolator_1.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ 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.
@@ -14,7 +15,6 @@
      limitations under the License.
 -->
 
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-        android:color="?attr/colorControlHighlight">
-    <item android:drawable="?attr/colorAccent" />
-</ripple>
+<pathInterpolator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 c 0.33333333,0.0 0.0,1.0 1.0,1.0" />
diff --git a/core/res/res/drawable/time_picker_header_material.xml b/core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_0.xml
similarity index 71%
copy from core/res/res/drawable/time_picker_header_material.xml
copy to core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_0.xml
index ef2068a..ceac663 100644
--- a/core/res/res/drawable/time_picker_header_material.xml
+++ b/core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_0.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ 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.
@@ -14,7 +15,6 @@
      limitations under the License.
 -->
 
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-        android:color="?attr/colorControlHighlight">
-    <item android:drawable="?attr/colorAccent" />
-</ripple>
+<pathInterpolator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 l 1.0,0.0 l 0.0,1.0" />
diff --git a/core/res/res/drawable/time_picker_header_material.xml b/core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_1.xml
similarity index 71%
copy from core/res/res/drawable/time_picker_header_material.xml
copy to core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_1.xml
index ef2068a..26bc8ad 100644
--- a/core/res/res/drawable/time_picker_header_material.xml
+++ b/core/res/res/interpolator/ic_checkbox_unchecked_animation_interpolator_1.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ 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.
@@ -14,7 +15,6 @@
      limitations under the License.
 -->
 
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-        android:color="?attr/colorControlHighlight">
-    <item android:drawable="?attr/colorAccent" />
-</ripple>
+<pathInterpolator
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 c 0.33333333,0.0 0.0,1.0 1.0,1.0" />
diff --git a/core/res/res/layout-land/date_picker_holo.xml b/core/res/res/layout-land/date_picker_holo.xml
deleted file mode 100644
index 991888c..0000000
--- a/core/res/res/layout-land/date_picker_holo.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:layout_width="match_parent"
-              android:layout_height="@dimen/datepicker_view_animator_height"
-              android:gravity="center"
-              android:orientation="horizontal"
-              android:minWidth="@dimen/datepicker_dialog_width" >
-
-    <include
-        layout="@layout/date_picker_selected_date"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_weight="1" />
-
-    <include layout="@layout/date_picker_view_animator" />
-
-</LinearLayout>
diff --git a/core/res/res/layout-land/date_picker_material.xml b/core/res/res/layout-land/date_picker_material.xml
new file mode 100644
index 0000000..1e711c5
--- /dev/null
+++ b/core/res/res/layout-land/date_picker_material.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="horizontal">
+
+    <include
+        layout="@layout/date_picker_header_material"
+        android:layout_width="168dp"
+        android:layout_height="match_parent" />
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:orientation="vertical">
+
+        <include
+            layout="@layout/date_picker_view_animator_material"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1" />
+
+        <ViewStub
+            android:id="@id/buttonPanel"
+            android:layout="@layout/alert_dialog_button_bar_material"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/day_picker_button_margin_top" />
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/core/res/res/layout-land/time_picker_material.xml b/core/res/res/layout-land/time_picker_material.xml
index 1b85e8f..89c3749 100644
--- a/core/res/res/layout-land/time_picker_material.xml
+++ b/core/res/res/layout-land/time_picker_material.xml
@@ -53,6 +53,7 @@
                 android:id="@+id/hours"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
                 android:singleLine="true"
                 android:ellipsize="none"
                 android:gravity="right"
@@ -64,6 +65,7 @@
                 android:id="@+id/separator"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
                 android:importantForAccessibility="no"
                 tools:text=":"
                 tools:textSize="@dimen/timepicker_time_label_size"
@@ -75,6 +77,7 @@
                 android:id="@+id/minutes"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
                 android:singleLine="true"
                 android:ellipsize="none"
                 android:gravity="left"
@@ -97,6 +100,7 @@
                 android:id="@+id/am_label"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
                 android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
                 android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
                 android:paddingTop="@dimen/timepicker_am_top_padding"
@@ -111,6 +115,7 @@
                 android:id="@+id/pm_label"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
                 android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
                 android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
                 android:paddingTop="@dimen/timepicker_pm_top_padding"
diff --git a/core/res/res/layout-watch/progress_dialog_material.xml b/core/res/res/layout-watch/progress_dialog_material.xml
new file mode 100644
index 0000000..228f724
--- /dev/null
+++ b/core/res/res/layout-watch/progress_dialog_material.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+    <LinearLayout
+        android:id="@+id/body"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:baselineAligned="false"
+        android:paddingStart="?attr/dialogPreferredPadding"
+        android:paddingTop="@dimen/dialog_padding_top_material"
+        android:paddingEnd="?attr/dialogPreferredPadding"
+        android:paddingBottom="@dimen/dialog_padding_top_material">
+
+        <ProgressBar
+            android:id="@id/progress"
+            style="?android:attr/progressBarStyleSmall"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:max="10000"
+            android:layout_marginEnd="?attr/dialogPreferredPadding" />
+
+        <TextView
+            android:id="@+id/message"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical" />
+    </LinearLayout>
+</FrameLayout>
diff --git a/core/res/res/layout/alert_dialog_button_bar_material.xml b/core/res/res/layout/alert_dialog_button_bar_material.xml
index 891bcd5..1eea4e1 100644
--- a/core/res/res/layout/alert_dialog_button_bar_material.xml
+++ b/core/res/res/layout/alert_dialog_button_bar_material.xml
@@ -18,16 +18,16 @@
 <com.android.internal.widget.ButtonBarLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/buttonPanel"
-    style="?attr/buttonBarStyle"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layoutDirection="locale"
     android:orientation="horizontal"
     android:paddingStart="12dp"
     android:paddingEnd="12dp"
-    android:paddingTop="8dp"
-    android:paddingBottom="8dp"
-    android:gravity="bottom">
+    android:paddingTop="4dp"
+    android:paddingBottom="4dp"
+    android:gravity="bottom"
+    style="?attr/buttonBarStyle">
 
     <Button
         android:id="@+id/button3"
diff --git a/core/res/res/layout/date_picker_header_material.xml b/core/res/res/layout/date_picker_header_material.xml
new file mode 100644
index 0000000..bda7de9
--- /dev/null
+++ b/core/res/res/layout/date_picker_header_material.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:tools="http://schemas.android.com/tools"
+              android:id="@+id/date_picker_header"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:paddingBottom="18dp"
+              android:paddingStart="?attr/dialogPreferredPadding"
+              android:paddingEnd="?attr/dialogPreferredPadding"
+              android:orientation="vertical"
+              tools:background="@color/accent_material_light"
+              tools:paddingStart="24dp"
+              tools:paddingEnd="24dp">
+
+    <!-- Top padding should stay on this view so that
+         the touch target is a bit larger. -->
+    <TextView
+        android:id="@+id/date_picker_header_year"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="16dp"
+        android:textAppearance="@style/TextAppearance.Material.DatePicker.YearLabel"
+        tools:text="2015"
+        tools:textSize="@dimen/date_picker_year_label_size"
+        tools:textColor="@color/white" />
+
+    <TextView
+        android:id="@+id/date_picker_header_date"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.Material.DatePicker.DateLabel"
+        android:maxLines="2"
+        android:ellipsize="none"
+        tools:text="Thu, Sep 30"
+        tools:textSize="@dimen/date_picker_date_label_size"
+        tools:textColor="@color/white" />
+
+</LinearLayout>
diff --git a/core/res/res/layout/date_picker_holo.xml b/core/res/res/layout/date_picker_material.xml
similarity index 72%
rename from core/res/res/layout/date_picker_holo.xml
rename to core/res/res/layout/date_picker_material.xml
index 72030ea..a1c97ff 100644
--- a/core/res/res/layout/date_picker_holo.xml
+++ b/core/res/res/layout/date_picker_material.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2011 The Android Open Source Project
+     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.
@@ -14,17 +14,21 @@
      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="@dimen/datepicker_component_width"
+              android:layout_width="match_parent"
               android:layout_height="match_parent"
-              android:gravity="center"
               android:orientation="vertical">
 
     <include
-        layout="@layout/date_picker_selected_date"
+        layout="@layout/date_picker_header_material"
         android:layout_width="match_parent"
         android:layout_height="wrap_content" />
 
-    <include layout="@layout/date_picker_view_animator" />
+    <include
+        layout="@layout/date_picker_view_animator_material"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
 
 </LinearLayout>
diff --git a/core/res/res/layout/date_picker_month_item_material.xml b/core/res/res/layout/date_picker_month_item_material.xml
new file mode 100644
index 0000000..cb79cee
--- /dev/null
+++ b/core/res/res/layout/date_picker_month_item_material.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<android.widget.SimpleMonthView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/month_view"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="@dimen/day_picker_padding_horizontal"
+    android:paddingEnd="@dimen/day_picker_padding_horizontal"
+    android:paddingTop="@dimen/day_picker_padding_top" />
diff --git a/core/res/res/layout/date_picker_selected_date.xml b/core/res/res/layout/date_picker_selected_date.xml
deleted file mode 100644
index 9becb81..0000000
--- a/core/res/res/layout/date_picker_selected_date.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/day_picker_selector_layout"
-    android:layout_width="@dimen/datepicker_component_width"
-    android:layout_height="wrap_content"
-    android:gravity="center"
-    android:orientation="vertical">
-
-    <TextView
-        android:id="@+id/date_picker_header"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/datepicker_header_height"
-        android:gravity="center"
-        android:importantForAccessibility="no" />
-
-    <LinearLayout
-        android:id="@+id/date_picker_month_day_year_layout"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        android:paddingTop="4dp"
-        android:paddingBottom="4dp"
-        android:orientation="vertical"
-        android:gravity="center">
-
-        <LinearLayout
-            android:id="@+id/date_picker_month_and_day_layout"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:clickable="true"
-            android:orientation="vertical">
-
-            <TextView
-                android:id="@+id/date_picker_month"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:duplicateParentState="true"
-                android:gravity="center" />
-
-            <TextView
-                android:id="@+id/date_picker_day"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="-23dp"
-                android:layout_marginBottom="-20dp"
-                android:duplicateParentState="true"
-                android:gravity="center" />
-        </LinearLayout>
-
-        <TextView
-            android:id="@+id/date_picker_year"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:gravity="center" />
-    </LinearLayout>
-
-</LinearLayout>
diff --git a/core/res/res/layout/date_picker_view_animator.xml b/core/res/res/layout/date_picker_view_animator.xml
deleted file mode 100644
index 9085ed5..0000000
--- a/core/res/res/layout/date_picker_view_animator.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<com.android.internal.widget.AccessibleDateAnimator
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/animator"
-        android:layout_width="@dimen/datepicker_component_width"
-        android:layout_height="@dimen/datepicker_view_animator_height"
-        android:gravity="center" />
\ No newline at end of file
diff --git a/core/res/res/layout/date_picker_view_animator_material.xml b/core/res/res/layout/date_picker_view_animator_material.xml
new file mode 100644
index 0000000..620ddfa
--- /dev/null
+++ b/core/res/res/layout/date_picker_view_animator_material.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<ViewAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/animator"
+    android:layout_width="match_parent"
+    android:layout_height="0dp"
+    android:layout_weight="1"
+    android:gravity="center">
+
+    <android.widget.DayPickerView
+        android:id="@+id/date_picker_day_picker"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:inAnimation="@anim/fade_in"
+        android:outAnimation="@anim/fade_out" />
+
+    <android.widget.YearPickerView
+        android:id="@+id/date_picker_year_picker"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</ViewAnimator>
diff --git a/core/res/res/layout/floating_popup_container.xml b/core/res/res/layout/floating_popup_container.xml
new file mode 100644
index 0000000..f247919
--- /dev/null
+++ b/core/res/res/layout/floating_popup_container.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 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.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="wrap_content"
+    android:layout_height="@dimen/floating_toolbar_height"
+    android:elevation="2dp"
+    android:focusable="true"
+    android:focusableInTouchMode="true"
+    android:background="@android:color/background_light" />
diff --git a/core/res/res/layout/floating_popup_menu_button.xml b/core/res/res/layout/floating_popup_menu_button.xml
new file mode 100644
index 0000000..9fa13bd
--- /dev/null
+++ b/core/res/res/layout/floating_popup_menu_button.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 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.
+*/
+-->
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:minWidth="@dimen/floating_toolbar_menu_button_side_padding"
+    android:paddingLeft="@dimen/floating_toolbar_menu_button_side_padding"
+    android:paddingRight="@dimen/floating_toolbar_menu_button_side_padding"
+    android:paddingTop="0dp"
+    android:paddingBottom="0dp"
+    android:singleLine="true"
+    android:ellipsize="end"
+    android:fontFamily="sans-serif"
+    android:textSize="@dimen/floating_toolbar_text_size"
+    android:textAllCaps="true"
+    android:background="?attr/selectableItemBackground" />
\ No newline at end of file
diff --git a/core/res/res/layout/floating_popup_open_overflow_button.xml b/core/res/res/layout/floating_popup_open_overflow_button.xml
new file mode 100644
index 0000000..4c1176c
--- /dev/null
+++ b/core/res/res/layout/floating_popup_open_overflow_button.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 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.
+*/
+-->
+<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/floating_toolbar_menu_button_minimum_width"
+    android:layout_height="match_parent"
+    android:minWidth="@dimen/floating_toolbar_menu_button_minimum_width"
+    android:minHeight="@dimen/floating_toolbar_height"
+    android:src="@drawable/ic_menu_moreoverflow_material"
+    android:contentDescription="@string/action_menu_overflow_description"
+    android:background="?attr/selectableItemBackgroundBorderless" />
diff --git a/core/res/res/layout/time_picker_header_material.xml b/core/res/res/layout/time_picker_header_material.xml
index 0ef404d..be9e443 100644
--- a/core/res/res/layout/time_picker_header_material.xml
+++ b/core/res/res/layout/time_picker_header_material.xml
@@ -15,6 +15,8 @@
   ~ limitations under the License
   -->
 
+<!-- This layout is duplicated in land/time_picker_material.xml, so any
+     changes made here need to be manually copied over. -->
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                 xmlns:tools="http://schemas.android.com/tools"
                 android:id="@+id/time_header"
@@ -32,6 +34,7 @@
         android:layout_height="wrap_content"
         android:layout_toLeftOf="@+id/separator"
         android:layout_alignBaseline="@+id/separator"
+        android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
         android:singleLine="true"
         android:ellipsize="none"
         android:gravity="right"
@@ -46,6 +49,7 @@
         android:layout_marginLeft="@dimen/timepicker_separator_padding"
         android:layout_marginRight="@dimen/timepicker_separator_padding"
         android:layout_centerInParent="true"
+        android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
         android:importantForAccessibility="no"
         tools:text=":"
         tools:textSize="@dimen/timepicker_time_label_size"
@@ -59,6 +63,7 @@
         android:layout_height="wrap_content"
         android:layout_toRightOf="@+id/separator"
         android:layout_alignBaseline="@+id/separator"
+        android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
         android:singleLine="true"
         android:ellipsize="none"
         android:gravity="left"
@@ -83,6 +88,7 @@
             android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
             android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
             android:paddingTop="@dimen/timepicker_am_top_padding"
+            android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
             android:lines="1"
             android:ellipsize="none"
             tools:text="AM"
@@ -95,6 +101,7 @@
             android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
             android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
             android:paddingTop="@dimen/timepicker_pm_top_padding"
+            android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
             android:lines="1"
             android:ellipsize="none"
             tools:text="PM"
diff --git a/core/res/res/layout/year_label_text_view.xml b/core/res/res/layout/year_label_text_view.xml
index e5bd068..6240c4b 100644
--- a/core/res/res/layout/year_label_text_view.xml
+++ b/core/res/res/layout/year_label_text_view.xml
@@ -13,10 +13,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<android.widget.TextViewWithCircularIndicator
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/month_text_view"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/datepicker_year_label_height"
-        android:layout_gravity="center"
-        android:gravity="center" />
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+         android:id="@+id/month_text_view"
+         android:layout_width="match_parent"
+         android:layout_height="wrap_content"
+         android:minHeight="?attr/listPreferredItemHeightSmall"
+         android:paddingTop="12dp"
+         android:paddingBottom="12dp"
+         android:gravity="center" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 0c61591..2bc6849 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi-oproepe"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nie aangestuur nie"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> na <xliff:g id="TIME_DELAY">{2}</xliff:g> sekondes"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Laat die program toe om foto\'s en video\'s met die kamera te neem. Hierdie toestemming laat die program toe om die kamera te eniger tyd sonder jou bevestiging te gebruik."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"deaktiveer LED wat oordrag aandui wanneer kamera gebruik word"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Laat \'n pre-geïnstalleerde stelselprogram toe om die LED wat kamera-gebruik aandui, te deaktiveer."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"deaktiveer tablet permanent"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"deaktiveer TV permanent"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"deaktiveer foon permanent"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Laat die program toe om metodes te benut om vingerafdruksjablone vir gebruik by te voeg en uit te vee."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"gebruik vingerafdrukhardeware"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Laat die program toe om vingerafdrukhardeware vir stawing te gebruik"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Gedeeltelike vingerafdruk is bespeur. Probeer asseblief weer."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Kon nie vingerafdruk verwerk nie. Probeer asseblief weer."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Vingerafdruksensor is vuil. Maak dit skoon en probeer weer."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Vinger is te vinnig beweeg. Probeer asseblief weer."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Vinger is te stadig beweeg. Probeer asseblief weer."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Handelaarspesifieke aankoopfoutboodskap 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Kon nie verwerk nie. Probeer weer."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardeware is nie beskikbaar nie."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Vingerafdruk kan nie gestoor word nie. Verwyder asseblief \'n bestaande vingerafdruk."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Vingerafdrukuittelling is bereik. Probeer weer."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Vingerafdrukuittelling is bereik. Probeer weer."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Handelaarspesifieke foutboodskap."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"lees sinkroniseer-instellings"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Laat die program toe om die sinkroniseringinstellings van \'n rekening te lees. Byvoorbeeld, dit kan bepaal of die People-program met \'n rekening gesinkroniseer is."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"wissel tussen sinkronisasie aan en af"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Laat die program toe om te verifieer dat \'n pakket installeerbaar is."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"bind aan \'n pakkieverifieerder"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Laat die houer toe om versoeke aan pakketverifieerders te rig. Dit moet nooit vir normale programme nodig wees nie."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"verifieer voornemefilter"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Laat die program toe om te kyk of \'n voornemefilter geverifieer is of nie."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"bind aan \'n voornemefilterverifieerder"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Laat die houer toe om versoeke te rig aan voornemefilterverifieerders. Behoort nooit vir normale programme nodig te wees nie."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"kry toegang tot reekspoorte"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Laat die houer toe om toegang te verkry tot reekspoorte wat die SerialManager API gebruik."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"verkry toegang tot inhoud ekstern"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Verminder dag"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Vermeerder jaar"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Verminder jaar"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Vorige maand"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Volgende maand"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Kanselleer"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Vee uit"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Minute se sirkelglyer"</string>
     <string name="select_hours" msgid="6043079511766008245">"Kies ure"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Kies minute"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Maandrooster van dae"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Jaarlys"</string>
     <string name="select_day" msgid="7774759604701773332">"Kies maand en dag"</string>
     <string name="select_year" msgid="7952052866994196170">"Kies jaar"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> gekies"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> uitgevee"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Werk-<xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Om hierdie skerm te ontspeld, raak en hou tegelyk Terug en Oorsig."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index b639b4f..ba0223d 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"የWi-Fi ጥሪ ማድረጊያ"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>፡አልተላለፈም"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>፡<xliff:g id="DIALING_NUMBER">{1}</xliff:g> ከ<xliff:g id="TIME_DELAY">{2}</xliff:g> ሰከንዶች በኋላ"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"መተግበሪያው በካሜራው ፎቶዎችንና ቪዲዮዎችን እንዲያነሳ ይፈቅድለታል። ይህ ፈቃድ መተግበሪያው ካሜራውን በማንኛውም ጊዜ ያላንተ ማረጋገጫ እንዲጠቀም ይፈቅድለታል።"</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"ካሜራው ስራ ላይ ሲሆን የማስተላለፍ አመልካች ኤል ኢ ዲን ያሰናክሉ"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"ቀድሞ የተጫነ የስርዓት መተግበሪያ ካሜራውን አመላካች ኤል ኢ ዲ እንዳይጠቀም እንዲያሰናክል ያስችለዋል።"</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"በቋሚነት ጡባዊ አቦዝን"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"ቴሌቪዥን እስከመጨረሻ አሰናክል"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"በቋሚነት ስልኩን አቦዝን"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"መተግበሪያው ጥቅም ላይ እንዲውሉ የጣት አሻራ ቅንብር ደንቦችን ለማከል እና ለመሰረዝ የሚያስችሉ ስልቶችን እንዲያስጀምር ያስችለዋል።"</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"የጣት አሻራ ሃርድዌርን ተጠቀም"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"መተግበሪያው የጣት አሻራ ሃርድዌር ለማረጋገጥ ስራ እንዲጠቀም ያስችለዋል"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ከፊል የጣት አሻራ ተገኝቷል። እባክዎ እንደገና ይሞክሩ።"</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ጣት አሻራን መስራት አልተቻለም። እባክዎ እንደገና ይሞክሩ።"</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"የጣት አሻራ ዳሳሽ ቆሽሿል። እባክዎ ያጽዱት እና እንደገና ይሞክሩ።"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"ጣት በጣም በፍጥነት ተንቀሳቅሷል። እባክዎ እንደገና ይሞክሩ።"</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"ጣት በጣም በዝግታ ተንቀሳቅሷል። እባክዎ እንደገና ይሞክሩ።"</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"አቅራቢ-ተኮር ግዢ የስህተት መልዕክት 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"ሂደትን ማከናወን አልተቻለም። እንደገና ይሞክሩ።"</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"ሃርድዌር አይገኝም።"</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"የጣት አሻራ ሊከማች አይችልም። እባክዎ አሁን ያለውን የጣት አሻራ ያስወግዱ።"</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"የጣት አሻራ ማብቂያ ጊዜ ደርሷል። እንደገና ይሞክሩ።"</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"የጣት አሻራ ማብቂያ ጊዜ ደርሷል። እንደገና ይሞክሩ።"</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"አቅራቢ-ተኮር የስህተት መልዕክት"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"የሥምሪያ ቅንብሮች አንብብ"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"መተግበሪያው የአንድ መለያ የማመሳሰል ቅንብሮችን እንዲያነብ ይፈቅድለታል። ለምሳሌ ይህ የሰዎች መተግበሪያ ከመለያ ጋር መመሳሰሉን አለመመሳሰሉን ሊወስን ይችላል።"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ማመሳሰያ በማብራትና በማጥፋት መካከል ቀያይር"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"ፓኬጅ መጫን የሚችል መሆኑን ለማረጋገጥ ለመተግበሪያው ይፈቅዳሉ፡፡"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"በፓኬጅ አረጋጋጭ የተወሰነ"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"የፓኬጅ አረጋጋጮችን ጥየቃ ለማድረግ ያዡ ይፈቅዳሉ። ለመደበኛ መተግበሪያዎች በፍፁም አያስፈልግም።"</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"የፍላጎት ማጣሪያን አረጋግጥ"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"የፍላጎት ማጣሪያው የተረጋገጠ ወይም ያልተረጋገጠ መሆኑን ለመፈተሽ እንዲችል መተግበሪያውን ይፈቅድለታል።"</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"በፍላጎት ማጣሪያ አረጋገጭ ተገደብ"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"የፍላጎት ማጣሪያ አረጋጋጮችን ጥየቃ ለማድረግ ያዡ ይፈቅዳሉ። ለመደበኛ መተግበሪያዎች በፍፁም አያስፈልግም።"</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"ተከታታይ ወደቦችን ድረስ"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Allows the holder to access serial ports using the SerialManager API. የተከታታይ አደራጅ APIን በመጠቀም ያዡ የተከታታይ ወደቦችን እንዲደርስ ይፈቅዳል።"</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"ይዘት አቅራቢዎችን በውጭ በኩል ድረስባቸው"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"ቀን ቀንስ"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"ዓመት ጨምር"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"ዓመት ቀንስ"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"ያለፈው ወር"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"ቀጣይ ወር"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"ይቅር"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"ሰርዝ"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"የደቂቃዎች ክብ ተንሸራታች"</string>
     <string name="select_hours" msgid="6043079511766008245">"ሰዓታትን ይምረጡ"</string>
     <string name="select_minutes" msgid="3974345615920336087">"ደቂቃዎችን ይምረጡ"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"የቀናት የወር ፍርግርግ"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"የዓመት ዝርዝር"</string>
     <string name="select_day" msgid="7774759604701773332">"ወር እና ቀን ይምረጡ"</string>
     <string name="select_year" msgid="7952052866994196170">"ዓመት ይምረጡ"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> ተመርጧል"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> ተሰርዟል"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"ስራ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"ይህን ማያ ገጽ ለመንቀል ተመለስን እና አጠቃላይ እይታን በተመሳሳይ ይንኳቸውና ይያዟቸው።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 42bf57d..b8ac523 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -130,6 +130,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"‏الاتصال عبر Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: لم تتم إعادة التوجيه"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> بعد <xliff:g id="TIME_DELAY">{2}</xliff:g> ثانية"</string>
@@ -588,6 +589,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"للسماح للتطبيق بالتقاط صور ومقاطع فيديو من خلال الكاميرا. ويتيح هذا الإذن للتطبيق استخدام الكاميرا في أي وقت وبدون موافقة منك."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"‏تعطيل مؤشر LED للإرسال عندما تكون الكاميرا قيد الاستخدام"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"‏للسماح لتطبيق نظام مثبت مسبقًا لتعطيل مؤشر LED لاستخدام الكاميرا."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"تعطيل الجهاز اللوحي نهائيًا"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"تعطيل التلفزيون نهائيًا"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"تعطيل الهاتف على الدوام"</string>
@@ -750,6 +753,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"للسماح للتطبيق باستدعاء طرق لإضافة نماذج من بصمات الأصابع وحذفها."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"لاستخدام أجهزة بصمة الإصبع"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"للسماح للتطبيق باستخدام أجهزة بصمة الإصبع للمصادقة"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"تم اكتشاف بصمة الإصبع بشكل جزئي؛ يرجى إعادة المحاولة."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"تعذرت معالجة بصمة الإصبع. يُرجى إعادة المحاولة."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"جهاز استشعار بصمات الأصابع متسخ، يرجى تنظيفه وإعادة المحاولة."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"تحرك الإصبع بسرعة كبيرة جدًا؛ يرجى إعادة المحاولة."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"تحرك الإصبع ببطء شديد جدًا؛ يرجى إعادة المحاولة."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"رسالة الخطأ 0 التي حددها المورّد بشأن الاكتساب"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"تعذرت المعالجة؛ أعد المحاولة."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"الجهاز غير متاح."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"يتعذر تخزين بصمة الإصبع؛ يرجى إزالة إحدى البصمات المخزنة."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"تم بلوغ مهلة إدخال بصمة الإصبع. أعد المحاولة."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"تم بلوغ مهلة إدخال بصمة الإصبع. أعد المحاولة."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"رسالة الخطأ التي حددها المورّد."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"قراءة إعدادات المزامنة"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"للسماح للتطبيق بقراءة الإعدادات المتزامنة لحساب ما. على سبيل المثال، يمكن أن يؤدي هذا إلى تحديد ما إذا تمت مزامنة تطبيق \"الأشخاص\" مع حساب ما."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"التبديل بين تشغيل المزامنة وإيقافها"</string>
@@ -1123,6 +1142,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"السماح للتطبيق بالتحقق من إمكانية تثبيت حزمة."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"الالتزام بمحقق حزمة"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"السماح للمالك بإجراء طلبات محققي الحزمة. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"التحقق من فلتر الأهداف"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"للسماح للتطبيق بفحص ما إذا كان فلتر الأهداف قد تم التحقق منه أم لا."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"الالتزام بمحقق فلتر الأهداف"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"للسماح للمالك بإجراء طلبات محققي فلتر الأهداف. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"الدخول إلى المنافذ التسلسلية"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"يسمح لحامله بالدخول إلى المنافذ التسلسلية باستخدام واجهة برمجة التطبيقات."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"الدخول إلى مزودي المحتوى خارجيًا"</string>
@@ -1576,6 +1599,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"تقليل الأيام"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"زيادة الأعوام"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"تقليل الأعوام"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"الشهر السابق"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"الشهر التالي"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"إلغاء"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"حذف"</string>
@@ -1830,11 +1855,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"شريط التمرير الدائري للدقائق"</string>
     <string name="select_hours" msgid="6043079511766008245">"تحديد الساعات"</string>
     <string name="select_minutes" msgid="3974345615920336087">"تحديد الدقائق"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"شبكة الشهر مكونة من الأيام"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"قائمة الأعوام"</string>
     <string name="select_day" msgid="7774759604701773332">"تحديد الشهر واليوم"</string>
     <string name="select_year" msgid="7952052866994196170">"تحديد العام"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"تم تحديد <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"تم حذف <xliff:g id="KEY">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> المخصص للعمل"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"لإلغاء تثبيت هذه الشاشة، يمكنك لمس \"رجوع\" و\"نظرة عامة\" في آن واحد مع الاستمرار."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index ecfa7bd..4efbea5 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Обаждания през Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Не е пренасочено"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> след <xliff:g id="TIME_DELAY">{2}</xliff:g> секунди"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Разрешава на приложението да прави снимки и видеоклипове с камерата. Това разрешение му позволява да я използва по всяко време без потвърждение от ваша страна."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"деактивиране на светодиодния индикатор за предаване, когато камерата се използва"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Разрешава на предварително инсталирано системно приложение да деактивира светодиодния индикатор за използване на камерата."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"деактивиране на таблета за постоянно"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"деактивиране на телевизора за постоянно"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"деактивиране на телефона за постоянно"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Разрешава на приложението да извиква начини за добавяне и изтриване на шаблони за отпечатъци, които да се използват."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"използване на хардуера за отпечатъци"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Разрешава на приложението да използва хардуера за отпечатъци с цел удостоверяване"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Открит е частичен отпечатък. Моля, опитайте отново."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Отпечатъкът не можа да се обработи. Моля, опитайте отново."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Сензорът за отпечатъци е мръсен. Моля, почистете го и опитайте отново."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Преместихте пръста си твърде бързо. Моля, опитайте отново."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Преместихте пръста си твърде бавно. Моля, опитайте отново."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Съобщение за грешка 0 при придобиване от конкретен доставчик"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Не може да се обработи. Опитайте отново."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Няма достъп до хардуера."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Отпечатъкът не може да бъде съхранен. Моля, премахнете съществуващ."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Времето за изчакване за отпечатък изтече. Опитайте отново."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Времето за изчакване за отпечатък изтече. Опитайте отново."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Съобщение за грешка от конкретен доставчик."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"четене на настройките за синхронизиране"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Разрешава на приложението да чете настройките за синхронизиране на профил. Например това може да определи дали приложението Хора е синхронизирано с даден профил."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"включване и изключване на синхронизирането"</string>
@@ -1119,6 +1138,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Разрешава на приложението да провери дали пакетът може да се инсталира."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"обвързване с верификатор на пакета"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Разрешава на притежателя да прави заявки за верификатори на пакета. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"достъп до серийни портове"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Разрешава на притежателя достъп до серийни портове посредством приложния програмен интерфейс (API) SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"външен достъп до доставчиците на съдърж."</string>
@@ -1544,6 +1571,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Намаляване на дните"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Увеличаване на годините"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Намаляване на годините"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Отказ"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Изтриване"</string>
@@ -1794,11 +1825,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Кръгов плъзгач за минутите"</string>
     <string name="select_hours" msgid="6043079511766008245">"Избиране на часове"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Избиране на минути"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Месечна таблица на дните"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Списък с години"</string>
     <string name="select_day" msgid="7774759604701773332">"Избиране на месец и ден"</string>
     <string name="select_year" msgid="7952052866994196170">"Избиране на година"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"Избрахте <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"Изтрихте <xliff:g id="KEY">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> за работа"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"За да освободите екрана, докоснете и задръжте едновременно бутона за връщане назад и този за общ преглед."</string>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index f20e21f..dd383ae 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi কলিং"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ফরওয়ার্ড করা হয়নি"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> সেকেন্ড পরে"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"ক্যামেরার সাহায্যে ছবি তুলতে ও ভিডিও তৈরি করতে অ্যাপ্লিকেশানটিকে মঞ্জুর করে৷ এই অনুমতিটি অ্যাপ্লিকেশানটিকে আপনার নিশ্চয়তা ছাড়াই যেকোনো সময় ক্যামেরা ব্যবহার করতে মঞ্জুর করে৷"</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"যখন ক্যামেরা ব্যবহারে থাকে তখন ট্রান্সমিট সূচক LED অক্ষম করে"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"আগে থেকে ইনস্টল থাকা একটি সিস্টেম অ্যাপ্লিকেশানকে ক্যামেরা ব্যবহারের সূচক LEDটিকে অক্ষম করার অনুমতি দেয়৷"</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"ট্যাবলেটকে স্থায়ীভাবে অক্ষম করে"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"টিভি স্থায়ীভাবে অক্ষম করে"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"ফোনকে স্থায়ীভাবে অক্ষম করে"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"ব্যবহার করার জন্য আঙ্গুলের ছাপের টেম্প্লেটগুলি যোগ করা এবং মোছার পদ্ধতিগুলি গ্রহন করতে অ্যাপ্লিকেশানটিতে অমুমতি দেয়৷"</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"আঙ্গুলের ছাপ নেওয়ার হার্ডওয়্যার ব্যবহার করুন"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"অনুমোদনের জন্য আঙ্গুলের ছাপ নেওয়ার হার্ডওয়্যার ব্যবহার করতে অ্যাপ্লিকেশানটিতে অনুমতি দেয়"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"আঙ্গুলের ছাপ আংশিক সনাক্ত করা হয়েছে৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"আঙ্গুলের ছাপ প্রক্রিয়া করা যায়নি৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"আঙ্গুলের ছাপ নেওয়ার সেন্সরটি অপরিস্কার৷ অনুগ্রহ করে পরিষ্কার করে আবার চেষ্টা করুন৷"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"আঙ্গুল অতি দ্রুত সরানো হয়েছে৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"আঙ্গুল ধীরে সরানো হয়েছে৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"বিক্রেতা-নির্দিষ্ট অর্জনে ত্রুটি বার্তা ০"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"প্রক্রিয়া করতে অক্ষম হয়েছে৷ আবার চেষ্টা করুন৷"</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"হার্ডওয়্যার অনুপলব্ধ৷"</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"আঙ্গুলের ছাপ সংরক্ষণ করা যাবে না৷ অনুগ্রহ করে একটি বিদ্যমান আঙ্গুলের ছাপ সরান৷"</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"আঙ্গুলের ছাপ নেওয়ার সময়সীমা শেষ হযেছে৷ আবার চেষ্টা করুন৷"</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"আঙ্গুলের ছাপ নেওয়ার সময়সীমা শেষ হযেছে৷ আবার চেষ্টা করুন৷"</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"বিক্রেতা-নির্দিষ্ট ত্রুটি বার্তা৷"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"সিঙ্ক সেটিংস পড়ে"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"অ্যাপ্লিকেশানটিকে একটি অ্যাকাউন্টের জন্য সিঙ্ক সেটিংস পড়ার অনুমতি দেয়৷ উদাহরণস্বরূপ, \'পিপল\' অ্যাপ্লিকেশানটি কোনো অ্যাকাউন্টের সাথে সিঙ্ক করা আছে কিনা তা নির্ধারণ করতে পারে৷"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"সমন্বয় চালু এবং বন্ধ করা টগল করুন"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"অ্যাপ্লিকেশানকে ইনস্টলযোগ্য প্যাকেজ যাচাই করার অনুমতি দেয়৷"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"একটি প্যাকেজ যাচাইকারীতে সংলগ্ন করে"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"ধারককে, প্যাকেজ যাচাইকারীতে অনুরোধগুলি পাঠাতে দেয়৷ সাধারণ অ্যাপ্লিকেশানগুলির জন্য কখনোই প্রয়োজনীয় নয়৷"</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"অভিপ্রায় ফিল্টার যাচাই করুন"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"কোনো অভিপ্রায় ফিল্টার যাচাই করা হয়েছে কি না অ্যাপ্লিকেশানটিকে তা পরীক্ষা করার মঞ্জুরি দেয়৷"</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"একটি অভিপ্রায় ফিল্টার যাচাইকারী আবদ্ধ করুন"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"ধারককে,অভিপ্রায় ফিল্টার যাচাইকারীতে অনুরোধগুলি পাঠাতে দেয়৷ সাধারণ অ্যাপ্লিকেশানগুলির জন্য কখনোই প্রয়োজনীয় নয়৷"</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"সিরিয়াল পোর্টগুলি অ্যাক্সেস করে"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"সিরিয়াল ম্যানেজার API ব্যবহার করে ধারককে সিরিয়াল পোর্টগুলি অ্যাক্সেস করতে অনুমতি দেয়৷"</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"বাহ্যিকভাবে সামগ্রীর পরিষেবা প্রদানকারীদের অ্যাক্সেস করুন"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"দিন কম করুন"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"বছর বাড়ান"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"বছর কম করুন"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"পূর্ববর্তী মাস"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"পরের মাস"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"বাতিল করুন"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"মুছুন"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"বৃত্তাকার মিনিট নির্বাচকের স্লাইডার"</string>
     <string name="select_hours" msgid="6043079511766008245">"ঘন্টা নির্বাচন করুন"</string>
     <string name="select_minutes" msgid="3974345615920336087">"মিনিট নির্বাচন করুন"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"দিন দিয়ে সংগঠিত মাসের গ্রিড"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"বছরের তালিকা"</string>
     <string name="select_day" msgid="7774759604701773332">"মাস এবং দিন নির্বাচন করুন"</string>
     <string name="select_year" msgid="7952052866994196170">"বছর নির্বাচন করুন"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> নির্বাচন করা হয়েছে"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> মুছে ফেলা হয়েছে"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"কর্মক্ষেত্র <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"এই স্ক্রীনটিকে আনপিন করতে, \'ফিরুন\' এবং \'এক নজরে\' একসাথে স্পর্শ করুন এবং ধরে রাখুন৷"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index aebebf2..e6293d1 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Trucades per Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: no s\'ha desviat"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> després de <xliff:g id="TIME_DELAY">{2}</xliff:g> segons"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permet que l\'aplicació faci fotos i vídeos amb la càmera. Aquest permís permet que l\'aplicació utilitzi la càmera en qualsevol moment sense la teva confirmació."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"desactiva la transmissió del LED indicador en fer servir la càmera"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Permet que una aplicació dels sistema preinstal·lada desactivi el LED indicador d\'ús de la càmera."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"desactiva la tauleta de manera permanent"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"desactivar el televisor de manera permanent"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"desactivar definitivament el telèfon"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Permet que l\'aplicació invoqui mètodes per afegir i suprimir plantilles d\'empremtes digitals que es puguin fer servir."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"Utilitzar el maquinari d\'empremtes digitals"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Permet que l\'aplicació faci servir maquinari d\'empremtes digitals per a l\'autenticació"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"S\'ha detectat una empremta digital parcial. Torna-ho a provar."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No s\'ha pogut processar l\'empremta digital. Torna-ho a provar."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"El sensor d\'empremtes digitals està brut. Neteja\'l i torna-ho a provar."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"El dit s\'ha mogut massa ràpid. Torna-ho a provar."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"El dit s\'ha mogut massa lentament. Torna-ho a provar."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Missatge d\'error d\'adquisició 0 específic del proveïdor"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"No es pot processar. Torna-ho a provar."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"No hi ha maquinari disponible."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"L\'empremta digital no es pot desar. Suprimeix-ne una."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"S\'ha esgotat el temps d\'espera per a l\'empremta digital. Torna-ho a provar."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"S\'ha esgotat el temps d\'espera per a l\'empremta digital. Torna-ho a provar."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Missatge d\'error específic del proveïdor."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"llegir la configuració de sincronització"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permet que l\'aplicació llegeixi la configuració de sincronització d\'un compte. Per exemple, això pot determinar que l\'aplicació Persones estigui sincronitzada amb un compte."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activar o desactivar la sincronització"</string>
@@ -1119,6 +1138,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Permet que l\'aplicació verifiqui si un paquet es pot instal·lar."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"vincula a un verificador de paquets"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permet que el titular sol·liciti verificadors de paquets. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"accedeix a ports sèrie"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Permet que el titular accedeixi a ports sèrie amb l\'API SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"accedeix als proveïdors de contingut externament"</string>
@@ -1544,6 +1571,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Fes disminuir el dia"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Fes augmentar l\'any"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Fes disminuir l\'any"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancel·la"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Suprimeix"</string>
@@ -1794,11 +1825,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Control circular dels minuts"</string>
     <string name="select_hours" msgid="6043079511766008245">"Selecciona les hores"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Selecciona els minuts"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Graella mensual de dies"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Llista anual"</string>
     <string name="select_day" msgid="7774759604701773332">"Selecciona un mes i un dia"</string>
     <string name="select_year" msgid="7952052866994196170">"Selecciona un any"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> seleccionat"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> suprimit"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> de la feina"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Per anul·lar la fixació d\'aquesta pantalla, mantén premudes les opcions Enrere i Visió general alhora."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 988818b..95f1e90 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -128,6 +128,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Volání přes Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nepřesměrováno"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po <xliff:g id="TIME_DELAY">{2}</xliff:g> sek."</string>
@@ -586,6 +587,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Umožňuje aplikaci pořizovat fotografie a videa pomocí fotoaparátu. Toto oprávnění umožňuje aplikaci používat fotoaparát kdykoliv i bez vašeho svolení."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"vypnutí indikátoru LED přenosu při použití fotoaparátu"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Umožňuje předinstalované systémové aplikaci vypnout kontrolku LED fotoaparátu."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"trvalé vypnutí tabletu"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"trvalé vypnutí televize"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"trvalé vypnutí telefonu"</string>
@@ -748,6 +751,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Umožňuje aplikaci volat metody k přidání a smazání šablon otisků prstů, které budou použity."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"použití hardwaru na čtení otisků prstů"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Umožňuje aplikaci použít k ověření hardware na čtení otisků prstů"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Byla zjištěna jen část otisku prstu. Zkuste to znovu."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Zpracování otisku prstu se nezdařilo. Zkuste to znovu."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Senzor otisků prstů je znečištěn. Vyčistěte jej a zkuste to znovu."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Pohyb prstem byl příliš rychlý. Zkuste to znovu."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Pohyb prstem byl příliš pomalý. Zkuste to znovu."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Chybová zpráva 0 dodavatele ohledně načtení otisků"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Otisk prstu nelze zpracovat. Zkuste to znovu."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware není dostupný."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Otisk prstu nelze uložit. Odstraňte existující otisk prstu."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Časový limit sejmutí otisku prstu vypršel. Zkuste to znovu."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Časový limit sejmutí otisku prstu vypršel. Zkuste to znovu."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Chybová zpráva dodavatele"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"čtení nastavení synchronizace"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Umožňuje aplikaci číst nastavení synchronizace v účtu. Může například určit, zda je s účtem synchronizována aplikace Lidé."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"vypnutí nebo zapnutí synchronizace"</string>
@@ -1121,6 +1140,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Umožňuje aplikaci ověřit, zda balíček lze nainstalovat."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"navázat na ověřovatele balíčků"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Umožňuje držiteli podávat žádosti o ověření balíčků. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"ověření filtru objektů intent"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Umožňuje aplikaci zkontrolovat, zda je filtr objektů intent ověřený."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"vazba na ověření filtru objektů intent"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Umožňuje držiteli podávat žádosti o ověření filtrů objektů intent. Běžné aplikace by toto oprávnění neměly nikdy potřebovat."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"přístup k sériovým portům"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Umožňuje držiteli přístup k sériovým portům pomocí rozhraní SerialManager API."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"externí přístup k poskytovatelům obsahu"</string>
@@ -1560,6 +1583,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Ubrat den"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Přidat rok"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Ubrat rok"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Předchozí měsíc"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Příští měsíc"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Zrušit"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Smazat"</string>
@@ -1812,11 +1837,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Kruhový posuvník minut"</string>
     <string name="select_hours" msgid="6043079511766008245">"Zvolte hodiny"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Zvolte minuty"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Dny uspořádané po měsících"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Seznam roků"</string>
     <string name="select_day" msgid="7774759604701773332">"Vyberte měsíc a den"</string>
     <string name="select_year" msgid="7952052866994196170">"Vyberte rok"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"Vybrána položka <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"Číslice <xliff:g id="KEY">%1$s</xliff:g> byla smazána"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Pracovní <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Chcete-li tuto obrazovku uvolnit, klepněte současně na možnosti Zpět a Přehled a podržte je."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 20b7ad2..e93c5ef 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Opkald via Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ikke viderestillet"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> efter <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunder"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Tillader, at appen kan tage billeder og videoer med kameraet. Med denne tilladelse kan appen til enhver tid bruge kameraet uden din bekræftelse."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"deaktiver sendelysdioden, når kameraet er i brug"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Tillader, at en forudinstalleret systemapplikation deaktiverer lysdioden for brug af kameraet."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"deaktiver tabletcomputeren permanent"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"deaktivere tv\'et permanent"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"deaktiver telefonen permanent"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Tillader, at appen kan køre metoder til at tilføje og slette fingeraftryksskabeloner"</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"brug fingeraftrykhardware"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Tillader, at appen kan bruge fingeraftrykhardware til godkendelse"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Der blev registreret et delvist fingeraftryk. Prøv igen."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Fingeraftrykket kunne ikke behandles. Prøv igen."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sensoren til registrering af fingeraftryk er beskidt. Tør den af, og prøv igen."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Du bevægede fingeren for hurtigt. Prøv igen."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Du bevægede fingeren for langsomt. Prøv igen."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Leverandørspecifik fejlmeddelelse 0 i forbindelse med hentning"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Registreringen kan ikke gennemføres. Prøv igen."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardwaren er ikke tilgængelig."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingeraftrykket kan ikke gemmes. Fjern et eksisterende fingeraftryk."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Registrering af fingeraftryk fik timeout. Prøv igen."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Registrering af fingeraftryk fik timeout. Prøv igen."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Leverandørspecifik fejlmeddelelse."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"læse indstillinger for synkronisering"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Tillader, at appen kan læse synkroniseringsindstillingerne for en konto. Denne tilladelse kan f.eks. fastslå, om appen Personer er synkroniseret med en konto."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"slå synkronisering til og fra"</string>
@@ -908,7 +927,7 @@
     <string name="phoneTypeCar" msgid="8738360689616716982">"Bil"</string>
     <string name="phoneTypeCompanyMain" msgid="540434356461478916">"Virksomhed (hovednummer)"</string>
     <string name="phoneTypeIsdn" msgid="8022453193171370337">"ISDN"</string>
-    <string name="phoneTypeMain" msgid="6766137010628326916">"Hoved"</string>
+    <string name="phoneTypeMain" msgid="6766137010628326916">"Hovednr."</string>
     <string name="phoneTypeOtherFax" msgid="8587657145072446565">"Andre faxbeskeder"</string>
     <string name="phoneTypeRadio" msgid="4093738079908667513">"Radio"</string>
     <string name="phoneTypeTelex" msgid="3367879952476250512">"Telex"</string>
@@ -1119,6 +1138,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Tillader, at appen kan bekræfte, at en pakke kan installeres."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"bind til en bekræftelse af pakker"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Tillader, at indehaveren kan sende anmodninger om bekræftelser af pakker. Dette bør aldrig være nødvendigt for almindelige apps."</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"adgang til serielle porte"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Tillader, at indehaveren kan få adgang til serielle porte ved hjælp af SerialManager API."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"adgang til indholdsleverandører eksternt"</string>
@@ -1544,6 +1571,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Tidligere dag"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Senere år"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Tidligere år"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Annuller"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Slet"</string>
@@ -1794,11 +1825,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Cirkulær minutvælger"</string>
     <string name="select_hours" msgid="6043079511766008245">"Vælg timer"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Vælg minutter"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Månedsgitter med dage"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Liste over år"</string>
     <string name="select_day" msgid="7774759604701773332">"Vælg måned og dag"</string>
     <string name="select_year" msgid="7952052866994196170">"Vælg år"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> er valgt"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> er slettet"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> – arbejde"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Hvis du vil frigøre dette skærmbillede, skal du trykke på Tilbage og Oversigt på samme tid og holde fingeren nede."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 07153fe..b573c0c 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"WLAN-Telefonie"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nicht weitergeleitet"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g><xliff:g id="DIALING_NUMBER">{1}</xliff:g> nach <xliff:g id="TIME_DELAY">{2}</xliff:g> Sekunden."</string>
@@ -203,7 +204,7 @@
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"Flugmodus ist AN."</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"Flugmodus ist AUS."</string>
     <string name="global_action_settings" msgid="1756531602592545966">"Einstellungen"</string>
-    <string name="global_action_assist" msgid="3892832961594295030">"Geräteassistent"</string>
+    <string name="global_action_assist" msgid="3892832961594295030">"Assistent"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"Sprachassistent"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"Jetzt sperren"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Ermöglicht der App, Bilder und Videos mit der Kamera aufzunehmen. Die Berechtigung erlaubt der App, die Kamera jederzeit und ohne Ihre Bestätigung zu nutzen."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"LED-Anzeige für Übertragung bei Kameranutzung deaktivieren"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Vorinstallierte System-Apps können die LED-Anzeige für die Kameranutzung deaktivieren."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"Tablet dauerhaft deaktivieren"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"Fernseher dauerhaft deaktivieren"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"Telefon dauerhaft deaktivieren"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Erlaubt der App, Methoden zum Hinzufügen und Löschen zu verwendender Fingerabdruckvorlagen aufzurufen"</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"Fingerabdruckhardware verwenden"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Erlaubt der App, Fingerabdruckhardware zur Authentifizierung zu verwenden"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Fingerabdruck teilweise erkannt. Versuchen Sie es erneut."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Fingerabdruck konnte nicht verarbeitet werden. Versuchen Sie es erneut."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerabdrucksensor ist verschmutzt. Reinigen Sie ihn und versuchen Sie es erneut."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Finger zu schnell bewegt. Versuchen Sie es erneut."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Finger zu langsam bewegt. Versuchen Sie es erneut."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Anbieterspezifische Erfassungsfehlermeldung 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Unbrauchbar. Versuchen Sie es erneut."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware nicht verfügbar"</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingerabdruck kann nicht gespeichert werden. Entfernen Sie einen vorhandenen Fingerabdruck."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Zeitüberschreitung für Fingerabdruck. Versuchen Sie es erneut."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Zeitüberschreitung für Fingerabdruck. Versuchen Sie es erneut."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Anbieterspezifische Fehlermeldung"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"Synchronisierungseinstellungen lesen"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Ermöglicht der App, die Synchronisierungseinstellungen eines Kontos zu lesen. Beispielsweise kann damit festgestellt werden, ob Kontakte mit einem Konto synchronisiert werden."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"Synchronisierung aktivieren oder deaktivieren"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Ermöglicht der App die Überprüfung, ob ein Paket installiert werden kann"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"An Paketprüfung binden"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Ermöglicht dem Halter, Anfragen für die Paketprüfung zu senden. Sollte nie für normale Apps benötigt werden."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"Intent-Filter verifizieren"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Ermöglicht einer App, zu überprüfen, ob ein Intent-Filter verifiziert wurde"</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"An Intent-Filter-Verifizierung binden"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Ermöglicht dem Inhaber, Anfragen für die Verifizierung von Intent-Filtern zu senden. Sollte für normale Apps nie benötigt werden."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"Zugriff auf serielle Schnittstellen"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Ermöglicht dem Inhaber den Zugriff auf serielle Schnittstellen über das SerialManager-API"</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"Extern auf Content-Anbieter zugreifen"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Tag verringern"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Jahr verlängern"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Jahr verringern"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Vormonat"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Nächster Monat"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Abbrechen"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Löschen"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Kreisförmiger Schieberegler für Minuten"</string>
     <string name="select_hours" msgid="6043079511766008245">"Stunden auswählen"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Minuten auswählen"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Monatsraster mit einzelnen Tagen"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Jahresliste"</string>
     <string name="select_day" msgid="7774759604701773332">"Monat und Tag auswählen"</string>
     <string name="select_year" msgid="7952052866994196170">"Jahr auswählen"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> ausgewählt"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> gelöscht"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> (geschäftlich)"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Um die Fixierung dieses Bildschirms aufzuheben, berühren und halten Sie gleichzeitig \"Zurück\" und \"Übersicht\"."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index e0a6f88..bef52de 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Κλήση Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Δεν προωθήθηκε"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> μετά από <xliff:g id="TIME_DELAY">{2}</xliff:g> δευτερόλεπτα"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Επιτρέπει στην εφαρμογή τη λήψη φωτογραφιών και βίντεο με τη φωτογραφική μηχανή. Αυτή η άδεια δίνει τη δυνατότητα στην εφαρμογή να χρησιμοποιεί τη φωτογραφική μηχανή ανά πάσα στιγμή χωρίς την έγκρισή σας."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"απενεργοποίηση ένδειξης LED μετάδοσης όταν χρησιμοποιείται η φωτογραφική μηχανή"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Επιτρέπει σε μια προεγκατεστημένη εφαρμογή συστήματος να απενεργοποιήσει την ένδειξη LED χρήσης της φωτογραφικής μηχανής."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"μόνιμη απενεργοποίηση του tablet"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"μόνιμη απενεργοποίηση της τηλεόρασης"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"μόνιμη απενεργοποίηση τηλεφώνου"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Επιτρέπει στην εφαρμογή να επικαλείται μεθόδους για την προσθήκη και τη διαγραφή προτύπων μοναδικού χαρακτηριστικού για χρήση."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"χρήση εξοπλισμού μοναδικού χαρακτηριστικού"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Επιτρέπει στην εφαρμογή να χρησιμοποιεί εξοπλισμό μοναδικού χαρακτηριστικού για έλεγχο ταυτότητας"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Εντοπίστηκε μερικό μοναδικό χαρακτηριστικό. Δοκιμάστε ξανά."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Δεν ήταν δυνατή η επεξεργασία του μοναδικού χαρακτηριστικού. Δοκιμάστε ξανά."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Ο αισθητήρας μοναδικού χαρακτηριστικού δεν είναι καθαρός. Καθαρίστε τον και δοκιμάστε ξανά."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Πολύ γρήγορη κίνηση δαχτύλου. Δοκιμάστε ξανά."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Πολύ αργή κίνηση δαχτύλου. Δοκιμάστε ξανά."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Μήνυμα σφάλματος εξαγοράς για συγκεκριμένο προμηθευτή 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Δεν είναι δυνατή η επεξεργασία. Δοκιμάστε ξανά."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Ο εξοπλισμός δεν είναι διαθέσιμος."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Δεν είναι δυνατή η αποθήκευση μοναδικού χαρακτηριστικού. Καταργήστε το υπάρχον μοναδικό χαρακτηριστικό."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Λήξη χρονικού ορίου μοναδικού χαρακτηριστικού. Δοκιμάστε ξανά."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Λήξη χρονικού ορίου μοναδικού χαρακτηριστικού. Δοκιμάστε ξανά."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Μήνυμα σφάλματος για συγκεκριμένο προμηθευτή."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ανάγνωση ρυθμίσεων συγχρονισμού"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Επιτρέπει στην εφαρμογή την ανάγνωση των ρυθμίσεων συγχρονισμού για έναν λογαριασμό. Για παράδειγμα, αυτό μπορεί να καθορίσει εάν η εφαρμογή \"Άτομα\" είναι συγχρονισμένη με έναν λογαριασμό."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"εναλλαγή ενεργοποίησης και απενεργοποίησης συγχρονισμού"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Επιτρέπει στην εφαρμογή να επαληθεύσει τη δυνατότητα εγκατάστασης ενός πακέτου."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"δέσμευση με επαλήθευση πακέτου"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Επιτρέπει στον κάτοχο να υποβάλλει ερωτήματα σε προγράμματα επαλήθευσης πακέτου. Δεν απαιτείται για συνήθεις εφαρμογές."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"επαλήθευση φίλτρου πρόθεσης"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Επιτρέπει στην εφαρμογή να ελέγχει εάν ένα φίλτρο πρόθεσης είναι επαληθευμένο ή όχι."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"σύνδεση σε προγρ.επαλήθ.φίλτρου πρόθεσης"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Επιτρέπει στον κάτοχο να υποβάλλει ερωτήματα σε προγράμματα επαλήθευσης φίλτρων πρόθεσης. Δεν απαιτείται για συνήθεις εφαρμογές."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"πρόσβαση στις σειριακές θύρες"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Επιτρέπει στον κάτοχο την πρόσβαση στις σειριακές θύρες με τη χρήση του SerialManager API."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"εξωτερική πρόσβαση σε παρόχους περιεχ."</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Μείωση ημέρας"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Αύξηση έτους"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Μείωση έτους"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Προηγούμενος μήνας"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Επόμενος μήνας"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Ακύρωση"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Διαγραφή"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Κυκλικό ρυθμιστικό λεπτών"</string>
     <string name="select_hours" msgid="6043079511766008245">"Επιλογή ωρών"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Επιλογή λεπτών"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Πλέγμα ημερών του μήνα"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Λίστα ετών"</string>
     <string name="select_day" msgid="7774759604701773332">"Επιλογή μήνα και ημέρας"</string>
     <string name="select_year" msgid="7952052866994196170">"Επιλογή έτους"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"Επιλέχτηκε το στοιχείο <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> διαγράφηκε"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Εργασία <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Για να ξεκαρφιτσώσετε αυτήν την οθόνη, πατήστε παρατεταμένα \"Επιστροφή\" και \"Επισκόπηση\" ταυτόχρονα."</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index f52daf6..e1386a0 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi Calling"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Not forwarded"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> after <xliff:g id="TIME_DELAY">{2}</xliff:g> seconds"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Allows the app to take pictures and videos with the camera. This permission allows the app to use the camera at any time without your confirmation."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"disable transmit indicator LED when camera is in use"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Allows a pre-installed system application to disable the camera use indicator LED."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"permanently disable tablet"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"permanently disable TV"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"permanently disable phone"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Allows the app to invoke methods to add and delete fingerprint templates for use."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"use fingerprint hardware"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Allows the app to use fingerprint hardware for authentication"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerprint sensor is dirty. Please clean and try again."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Finger moved to fast. Please try again."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Finger moved to slow. Please try again."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Vendor-specific acquisition error message 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Unable to process. Try again."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware not available."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingerprint can\'t be stored. Please remove an existing fingerprint."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Fingerprint timeout reached. Try again."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Fingerprint timeout reached. Try again."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Vendor-specific error message."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"read sync settings"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Allows the app to read the sync settings for an account. For example, this can determine whether the People app is synced with an account."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"toggle sync on and off"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Allows the app to verify a package is installable."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"bind to a package verifier"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Allows the holder to make requests of package verifiers. Should never be needed for normal apps."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"verify intent filter"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Allows the app to check if an intent filter is verified or not."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"bind to an intent filter verifier"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Allows the holder to make requests of intent filter verifiers. Should never be needed for normal apps."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"access serial ports"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Allows the holder to access serial ports using the SerialManager API."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"access content providers externally"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Decrease day"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Increase year"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Decrease year"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Previous month"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Next month"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancel"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Minutes circular slider"</string>
     <string name="select_hours" msgid="6043079511766008245">"Select hours"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Select minutes"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Month grid of days"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Year list"</string>
     <string name="select_day" msgid="7774759604701773332">"Select month and day"</string>
     <string name="select_year" msgid="7952052866994196170">"Select year"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> selected"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> deleted"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"To unpin this screen, touch and hold Back and Overview at the same time."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index f52daf6..e1386a0 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi Calling"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Not forwarded"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> after <xliff:g id="TIME_DELAY">{2}</xliff:g> seconds"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Allows the app to take pictures and videos with the camera. This permission allows the app to use the camera at any time without your confirmation."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"disable transmit indicator LED when camera is in use"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Allows a pre-installed system application to disable the camera use indicator LED."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"permanently disable tablet"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"permanently disable TV"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"permanently disable phone"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Allows the app to invoke methods to add and delete fingerprint templates for use."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"use fingerprint hardware"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Allows the app to use fingerprint hardware for authentication"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerprint sensor is dirty. Please clean and try again."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Finger moved to fast. Please try again."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Finger moved to slow. Please try again."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Vendor-specific acquisition error message 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Unable to process. Try again."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware not available."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingerprint can\'t be stored. Please remove an existing fingerprint."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Fingerprint timeout reached. Try again."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Fingerprint timeout reached. Try again."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Vendor-specific error message."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"read sync settings"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Allows the app to read the sync settings for an account. For example, this can determine whether the People app is synced with an account."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"toggle sync on and off"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Allows the app to verify a package is installable."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"bind to a package verifier"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Allows the holder to make requests of package verifiers. Should never be needed for normal apps."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"verify intent filter"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Allows the app to check if an intent filter is verified or not."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"bind to an intent filter verifier"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Allows the holder to make requests of intent filter verifiers. Should never be needed for normal apps."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"access serial ports"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Allows the holder to access serial ports using the SerialManager API."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"access content providers externally"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Decrease day"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Increase year"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Decrease year"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Previous month"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Next month"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancel"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Minutes circular slider"</string>
     <string name="select_hours" msgid="6043079511766008245">"Select hours"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Select minutes"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Month grid of days"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Year list"</string>
     <string name="select_day" msgid="7774759604701773332">"Select month and day"</string>
     <string name="select_year" msgid="7952052866994196170">"Select year"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> selected"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> deleted"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"To unpin this screen, touch and hold Back and Overview at the same time."</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index f52daf6..e1386a0 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi Calling"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Not forwarded"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> after <xliff:g id="TIME_DELAY">{2}</xliff:g> seconds"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Allows the app to take pictures and videos with the camera. This permission allows the app to use the camera at any time without your confirmation."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"disable transmit indicator LED when camera is in use"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Allows a pre-installed system application to disable the camera use indicator LED."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"permanently disable tablet"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"permanently disable TV"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"permanently disable phone"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Allows the app to invoke methods to add and delete fingerprint templates for use."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"use fingerprint hardware"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Allows the app to use fingerprint hardware for authentication"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Partial fingerprint detected. Please try again."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Couldn\'t process fingerprint. Please try again."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingerprint sensor is dirty. Please clean and try again."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Finger moved to fast. Please try again."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Finger moved to slow. Please try again."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Vendor-specific acquisition error message 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Unable to process. Try again."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware not available."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingerprint can\'t be stored. Please remove an existing fingerprint."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Fingerprint timeout reached. Try again."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Fingerprint timeout reached. Try again."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Vendor-specific error message."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"read sync settings"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Allows the app to read the sync settings for an account. For example, this can determine whether the People app is synced with an account."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"toggle sync on and off"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Allows the app to verify a package is installable."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"bind to a package verifier"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Allows the holder to make requests of package verifiers. Should never be needed for normal apps."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"verify intent filter"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Allows the app to check if an intent filter is verified or not."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"bind to an intent filter verifier"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Allows the holder to make requests of intent filter verifiers. Should never be needed for normal apps."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"access serial ports"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Allows the holder to access serial ports using the SerialManager API."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"access content providers externally"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Decrease day"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Increase year"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Decrease year"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Previous month"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Next month"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancel"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Minutes circular slider"</string>
     <string name="select_hours" msgid="6043079511766008245">"Select hours"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Select minutes"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Month grid of days"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Year list"</string>
     <string name="select_day" msgid="7774759604701773332">"Select month and day"</string>
     <string name="select_year" msgid="7952052866994196170">"Select year"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> selected"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> deleted"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Work <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"To unpin this screen, touch and hold Back and Overview at the same time."</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index ce419dc..13fd0cf 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Llamada por Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: no se ha remitido"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> después de <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permite que la aplicación saque fotos o grabe videos con la cámara. Este permiso autoriza a la aplicación a utilizar la cámara en cualquier momento sin tu confirmación."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"Inhabilitar el indicador LED de transmisión mientras se utiliza la cámara"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Permite que una aplicación del sistema instalada previamente inhabilite el indicador LED de uso de la cámara."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"desactivar tablet de forma permanente"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"inhabilitar la TV permanentemente"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"desactivar dispositivo de manera permanente"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Permite que la aplicación emplee métodos para agregar y eliminar plantillas de huellas digitales para su uso."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"Utilizar hardware de huellas digitales"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Permite que la aplicación utilice el hardware de huellas digitales para realizar la autenticación."</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"La huella digital se detectó parcialmente. Vuelve a intentarlo."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No se pudo procesar la huella digital. Vuelve a intentarlo."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"El sensor de huellas digitales está sucio. Limpia el sensor y vuelve a intentarlo."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Moviste el dedo muy rápido. Vuelve a intentarlo."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Moviste el dedo muy despacio. Vuelve a intentarlo."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Mensaje de error de adquisición específico del proveedor 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"No se puede procesar. Vuelve a intentarlo."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"El hardware no está disponible."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"No se puede almacenar la huella digital. Elimina una de las existentes."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Finalizó el tiempo de espera para la huella digital. Vuelve a intentarlo."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Finalizó el tiempo de espera para la huella digital. Vuelve a intentarlo."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Mensaje de error específico del proveedor"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"leer la configuración de sincronización"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Este permiso permite que la aplicación consulte la configuración de sincronización de una cuenta. Esto permite, por ejemplo, determinar si la aplicación Personas está sincronizada con una cuenta."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activar y desactivar la sincronización"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Permite que la aplicación verifique si se puede instalar un paquete."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"Vincular a un verificador de paquetes"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permite que el titular solicite verificadores de paquetes. Las aplicaciones normales no deberían necesitar este permiso."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"verificar filtro de intento"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Permite que la aplicación compruebe si se verificó un filtro de intento."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"vincular a verificador filtro de intento"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Permite que el titular solicite verificadores de filtros de intentos. Las aplicaciones normales no deberían necesitar este permiso."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"Acceder a los puertos serie"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Permite acceder a puertos serie a través de la API SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"acceder a proveedores externamente"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Reducir día"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aumentar año"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reducir año"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Mes anterior"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Mes siguiente"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancelar"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Eliminar"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Control deslizante circular de minutos"</string>
     <string name="select_hours" msgid="6043079511766008245">"Seleccionar horas"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Seleccionar minutos"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Cuadrícula mensual de días"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Lista de años"</string>
     <string name="select_day" msgid="7774759604701773332">"Seleccionar mes y día"</string>
     <string name="select_year" msgid="7952052866994196170">"Seleccionar año"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> seleccionado"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> borrado"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> de trabajo"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Para dejar de fijar esta pantalla, mantén presionados los botones para volver y Recientes al mismo tiempo."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index e22ea21..89cf984 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Llamadas Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: No desviada"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> transcurridos <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permite que la aplicación haga fotos o grabe vídeos con la cámara. Este permiso autoriza a la aplicación a utilizar la cámara en cualquier momento sin tu confirmación."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"inhabilitar el indicador LED de transmisión mientras se utiliza la cámara"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Permite que una aplicación de sistema instalada previamente inhabilite el indicador LED que advierte del uso de la cámara."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"inhabilitar tablet de forma permanente"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"inhabilitar permanentemente la TV"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"inhabilitar el teléfono de forma permanente"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Permite que la aplicación invoque métodos para añadir y eliminar plantillas de huellas digitales y utilizarlas."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"utilizar hardware de huellas digitales"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Permite que la aplicación utilice el hardware de huellas digitales para realizar la autenticación"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Se ha detectado una huella digital parcial. Vuelve a intentarlo."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"No se ha podido procesar la huella digital. Vuelve a intentarlo."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"El sensor de huellas digitales está sucio. Límpialo y vuelve a intentarlo."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Has movido el dedo muy rápido. Vuelve a intentarlo."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Has movido el dedo muy despacio. Vuelve a intentarlo."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Mensaje de error de adquisición específico del proveedor: 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"No se puede procesar la huella digital. Vuelve a intentarlo."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware no disponible."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"No se puede almacenar la huella digital. Elimina una ya creada."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Se ha alcanzado el tiempo de espera de la huella digital. Vuelve a intentarlo."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Se ha alcanzado el tiempo de espera de la huella digital. Vuelve a intentarlo."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Mensaje de error específico del proveedor."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"leer la configuración de sincronización"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite que la aplicación consulte la configuración de sincronización de una cuenta. La aplicación puede utilizar este permiso, por ejemplo, para determinar si la aplicación Contactos está sincronizada con una cuenta."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activar y desactivar la sincronización"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Permite que la aplicación verifique si se puede instalar un paquete."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"enlazar con un detector de paquetes"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permite que se envíen solicitudes de detectores de paquetes. Las aplicaciones normales no deberían necesitar este permiso."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"verificar filtro de intento"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Permite que la aplicación compruebe si un filtro de intento se ha verificado."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"enlazar con detector filtro de intento"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Permite que se envíen solicitudes de detectores de filtros de intentos. Las aplicaciones normales no deberían necesitar este permiso."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"acceder a puertos serie"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Permite acceder a puertos serie a través de SerialManager API."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"acceder a proveedores de contenido externamente"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Reducir días"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aumentar año"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reducir año"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Mes anterior"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Próximo mes"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancelar"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Eliminar"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Control deslizante circular de minutos"</string>
     <string name="select_hours" msgid="6043079511766008245">"Seleccionar horas"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Seleccionar minutos"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Cuadrícula mensual de días"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Lista de años"</string>
     <string name="select_day" msgid="7774759604701773332">"Seleccionar mes y día"</string>
     <string name="select_year" msgid="7952052866994196170">"Seleccionar año"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> seleccionado"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> eliminado"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> de trabajo"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Para desactivar esta pantalla, mantén pulsados los botones de retroceso y Visión general al mismo tiempo."</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 85b5c3c..63ff865 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"WiFi-kõned"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: pole suunatud"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> sekundi pärast"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Võimaldab rakendusel teha kaameraga pilte ja videoid. See luba võimaldab rakendusel kasutada kaamerat mis tahes ajal teie kinnituseta."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"keela kaamera kasutamisel näidikutule kasutamine"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Lubab eelinstallitud süsteemirakendusel keelata kaamera näidikutule kasutamise."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"blokeeri tahvelarvuti jäädavalt"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"teleri alaline keelamine"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"blokeeri telefon jäädavalt"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Võimaldab rakendusel tühistada meetodid kasutatavate sõrmejäljemallide lisamiseks ja kustutamiseks."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"sõrmejälje riistvara kasutamine"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Võimaldab rakendusel autentimiseks kasutada sõrmejälje riistvara"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Tuvastati osaline sõrmejälg. Proovige uuesti."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Sõrmejälge ei õnnestunud töödelda. Proovige uuesti."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sõrmejäljeandur on must. Puhastage see ja proovige uuesti."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Sõrm liikus liiga kiiresti. Proovige uuesti."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Sõrm liikus liiga aeglaselt. Proovige uuesti."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Teenusepakkujapõhise värbamise veateade 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Töötlemine ei õnnestu. Proovige uuesti."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Riistvara pole saadaval."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Sõrmejälge ei saa salvestada. Eemaldage olemasolev sõrmejälg."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Sõrmejälje riistvara taimeri ajalõpp. Proovige uuesti."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Sõrmejälje riistvara taimeri ajalõpp. Proovige uuesti."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Teenusepakkujapõhine veateade."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"loe sünkroonimisseadeid"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Võimaldab rakendusel lugeda konto sünkroonimisseadeid. Näiteks võib see määrata, kas rakendus Inimesed on kontoga sünkroonitud."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"lülitage sünkroonimine sisse ja välja"</string>
@@ -1119,6 +1138,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Võimaldab rakendusel kinnitada, et paketti saab installida."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"sidumine paketi kinnitajaga"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Lubab omanikul teha taotlusi paketi kinnitajate kohta. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"juurdepääs jadaportidele"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Võimaldab omanikul SerialManageri API-liidese abil jadaportidele juurde pääseda."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"väline juurdepääs sisupakkujatele"</string>
@@ -1544,6 +1571,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Päeva vähendamine"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aasta suurendamine"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Aasta vähendamine"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Tühista"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Kustuta"</string>
@@ -1794,11 +1825,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Ringikujuline minutiliugur"</string>
     <string name="select_hours" msgid="6043079511766008245">"Tundide valimine"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Minutite valimine"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Päevad kuu ruudustikus"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Aastate loend"</string>
     <string name="select_day" msgid="7774759604701773332">"Kuu ja päeva valimine"</string>
     <string name="select_year" msgid="7952052866994196170">"Aasta valimine"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> on valitud"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> on kustutatud"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Töö <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Ekraanikuva vabastamiseks puudutage pikalt samal ajal nuppe Tagasi ja Ülevaade."</string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index 02d8dd3..386df35d 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi bidezko deiak"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ez da desbideratu"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> zenbakira <xliff:g id="TIME_DELAY">{2}</xliff:g> segundotan"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Kamerarekin argazkiak ateratzeko eta bideoak grabatzeko baimena ematen die aplikazioei. Baimen horrekin, aplikazioak kamera edonoiz erabil dezake zure baimenik gabe."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"Desgaitu LED argi adierazlea kamera abian denean"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Kamera erabiltzen ari dela adierazten duen LED argia desgaitzeko aukera ematen dio sisteman aurrez instalatutako aplikazio bati."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"desgaitu telefonoa behin betiko"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"Desgaitu telebista betiko"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"desgaitu telefonoa behin betiko"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Hatz-marka digitalen txantiloiak gehitzeko eta ezabatzeko metodoei dei egitea baimentzen die aplikazioei."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"Erabili hatz-marka digitalen hardwarea"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Autentifikatzeko hatz-marka digitalen hardwarea erabiltzea baimentzen die aplikazioei."</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Hatz-marka digitala ez da osorik hauteman. Saiatu berriro."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Ezin izan da hatza-marka prozesatu. Saiatu berriro."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Hatz-marka digitalen sentsorea zikina dago. Garbi ezazu, eta saiatu berriro."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Hatza bizkorregi mugitu duzu. Saiatu berriro."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Hatza mantsoegi mugitu duzu. Saiatu berriro."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Saltzailearen berariazko errore-mezua, erosketarekin erlazionatuta"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Ezin da prozesatu. Saiatu berriro."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardwarea ez dago erabilgarri."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Ezin da gorde hatz-marka digitala. Kendu lehendik gordeta duzunetako bat."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Hatz-marka digitalak prozesatzeko denbora-muga gainditu da. Saiatu berriro."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Hatz-marka digitalak prozesatzeko denbora-muga gainditu da. Saiatu berriro."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Saltzailearen berariazko errore-mezua."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"Irakurri sinkronizazio-ezarpenak"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Kontu baten sinkronizazio-ezarpenak irakurtzeko baimena ematen die aplikazioei. Adibidez, Jendea aplikazioa konturen batekin sinkronizatuta dagoen zehatz dezake."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"sinkronizazioa aktibatzea eta desaktibatzea"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Paketeak instala daitezkeen egiaztatzea baimentzen die aplikazioei."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"lotu pakete-egiaztatzaile batekin"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Pakete-egiaztatzaileei eskaerak egitea baimentzen die aplikazioei. Aplikazio normalek ez lukete beharko."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"Egiaztatu saiakera-iragazkia"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Saiakera-iragazki bat egiaztatuta dagoen ala ez egiaztatzea baimentzen die aplikazioei."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"Lotu saiakera-iragazkien egiaztatzaile bati"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Saiakera-iragazkien egiaztatzaileei eskaerak egitea baimentzen die aplikazioei. Aplikazio normalek ez lukete beharko."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"atzitu serie-atakak"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Jabeari serieko atakak atzitzeko aukera ematen dio SerialManager APIa erabilita."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"eduki-hornitzaileak kanpotik atzitzea"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Atzeratu egun bat"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aurreratu urtebete"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Atzeratu urtebete"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Aurreko hilabetea"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Hurrengo hilabetea"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Utzi"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Ezabatu"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Minutuak aukeratzeko ikuspegi zirkularra"</string>
     <string name="select_hours" msgid="6043079511766008245">"Hautatu orduak"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Hautatu minutuak"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Hilabete-ikuspegiko eguna aukeratzeko sareta"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Urteen zerrenda"</string>
     <string name="select_day" msgid="7774759604701773332">"Hautatu hilabetea eta eguna"</string>
     <string name="select_year" msgid="7952052866994196170">"Hautatu urtea"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> hautatu da"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> ezabatu da"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Laneko <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Aingura kentzeko, eduki ukituta Atzera eta Ikuspegi orokorra botoiak aldi berean."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 2073b5e..ea762f4 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"‏تماس از طریق Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: هدایت نشده"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> پس از <xliff:g id="TIME_DELAY">{2}</xliff:g> ثانیه"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"به برنامه اجازه می‌دهد با دوربین به عکسبرداری و فیلمبرداری بپردازد. این مجوز به برنامه اجازه می‌‌دهد از دوربین در هر زمانی بدون تأیید شما استفاده کند."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"‏LED نشانگر انتقال داده، هنگام استفاده از دوربین غیرفعال شود"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"‏به یک برنامه سیستم از قبل نصب شده اجازه می‌دهد LED نشانگر استفاده از دوربین را غیرفعال کند."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"غیر فعال کردن دائم رایانهٔ لوحی"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"غیرفعالسازی دائم تلویزیون"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"تلفن بطور دائمی غیرفعال شود"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"به برنامه امکان می‌دهد روش‌هایی را برای افزودن و حذف الگوهای اثر انگشت جهت استفاده، فعال کند."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"استفاده از سخت‌افزار اثر انگشت"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"به برنامه امکان می‌دهد از سخت‌افزار اثر انگشت برای احراز هویت استفاده کند"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"بخشی از اثر انگشت شناسایی شد. لطفاً دوباره امتحان کنید."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"اثرانگشت پردازش نشد. لطفاً دوباره امتحان کنید."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"حسگر اثر انگشت کثیف است. لطفاً آن را تمیز کنید و دوباره امتحان نمایید."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"انگشت خیلی سریع حرکت کرد. لطفاً دوباره امتحان کنید."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"انگشت خیلی آهسته حرکت کرد. لطفاً دوباره امتحان کنید."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"پیام خطای خرید خاص فروشنده ۰"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"پردازش ممکن نیست. دوباره امتحان کنید."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"سخت‌افزار در دسترس نیست."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"ذخیره اثر انگشت ممکن نیست. لطفاً یک اثر انگشت موجود را حذف کنید."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"مهلت زمانی ثبت اثر انگشت به پایان رسید. دوباره امتحان کنید."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"مهلت زمانی ثبت اثر انگشت به پایان رسید. دوباره امتحان کنید."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"پیام خطای خاص فروشنده."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"خواندن تنظیمات همگام‌سازی"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"به برنامه اجازه می‌دهد تنظیمات را برای یک حساب بخواند. به‌عنوان مثال، این ویژگی می‌تواند تعیین کند آیا حساب «افراد» شما با یک حساب همگام‌سازی شده است."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"تغییر وضعیت همگام‌سازی بین فعال و غیرفعال"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"به برنامه اجازه می‌دهد قابل نصب بودن بسته را تأیید کند."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"اتصال به یک تأیید کننده بسته"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"‏به دارنده اجازه می‎دهد تا تاییدکنندگان بسته را درخواست کند. برای برنامه‎های عادی نیاز نیست."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"تأیید فیلتر هدف"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"به برنامه اجازه می‌دهد بررسی کند که فیلتر هدف تأیید شده یا خیر."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"ارتباط با یک بررسی‌کننده فیلتر هدف"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"به دارنده اجازه می‌دهد بررسی‌کننده فیلتر هدف را درخواست کند. هرگز نباید برای برنامه‌های عادی مورد نیاز شود."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"دسترسی به درگاه‌های سریال"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"‏به دارنده اجازه می‌دهد با استفاده از SerialManager API به درگاه‌های سریال دسترسی داشته باشد."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"دسترسی خارجی به ارائه‌دهندگان محتوا"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"کاهش روز"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"افزایش سال"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"کاهش سال"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"ماه قبل"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"ماه بعد"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"لغو"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"لغزنده دایره‌ای دقیقه"</string>
     <string name="select_hours" msgid="6043079511766008245">"انتخاب ساعت"</string>
     <string name="select_minutes" msgid="3974345615920336087">"انتخاب دقیقه"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"جدول روزها براساس ماه"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"لیست سال‌ها"</string>
     <string name="select_day" msgid="7774759604701773332">"انتخاب ماه و روز"</string>
     <string name="select_year" msgid="7952052866994196170">"انتخاب سال"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> انتخاب شد"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> حذف شد"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> محل کار"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"برای برداشتن پین این صفحه، هم‌زمان «بازگشت» و «نمای کلی» را لمس کنید و نگه دارید."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index a2f1ccf..b66766c 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi-puhelut"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ei siirretty"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunnin päästä"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Antaa sovelluksen ottaa kuvia ja kuvata videoita kameralla. Sovellus voi käyttää kameraa milloin tahansa ilman lupaasi."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"poista lähetyksen merkkivalo käytöstä, kun kameraa käytetään"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Antaa valmiiksi asennetun järjestelmäsovelluksen poistaa käytöstä kameran käytössäolon merkkivalon."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"poista tabletti käytöstä lopullisesti"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"Poista televisio pysyvästi käytöstä"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"poista puhelin käytöstä pysyvästi"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Sallii sovelluksen käyttää menetelmiä, joilla voidaan lisätä tai poistaa sormenjälkimalleja."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"sormenjälkilaitteiston käyttö"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Sallii sovelluksen käyttää sormenjälkilaitteistoa todennukseen."</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Sormenjälki havaittiin vain osittain. Yritä uudelleen."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Sormenjäljen käsittely epäonnistui. Yritä uudelleen."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sormenjälkitunnistin on likainen. Puhdista tunnistin ja yritä uudelleen."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Liikutit sormea liian nopeasti. Yritä uudelleen."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Liikutit sormea liian hitaasti. Yritä uudelleen."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Toimittajakohtainen ​​hankintavirheilmoitus 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Käsittely ei onnistu. Yritä uudelleen."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Laitteisto ei ole käytettävissä."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Sormenjälkeä ei voida tallentaa. Poista aiemmin lisätty sormenjälki."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Sormenjälkitunnistimen toiminta aikakatkaistiin. Yritä uudelleen."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Sormenjälkitunnistimen toiminta aikakatkaistiin. Yritä uudelleen."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Toimittajakohtainen ​​virheilmoitus."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"lue synkronointiasetuksia"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Antaa sovelluksen lukea tilien synkronointiasetuksia. Sovellus voi esimerkiksi määrittää, onko Henkilöt-sovellus synkronoitu tilin kanssa."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ota synkronointi käyttöön tai poista se käytöstä"</string>
@@ -1119,6 +1138,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Antaa sovelluksen vahvistaa, että pakkaus on asennettavissa."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"sitoudu paketin vahvistajaan"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Antaa sovelluksen tehdä pakettien vahvistuspyyntöjä. Ei tavallisten sovellusten käyttöön."</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"käytä sarjaportteja"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Luvan haltija voi käyttää sarjaportteja SerialManager-sovellusliittymän avulla."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"käytä ulkoisia sisällöntarjoajia"</string>
@@ -1544,6 +1571,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Vähennä päivien määrää."</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Lisää vuosien määrää."</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Vähennä vuosien määrää."</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Peruuta"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Poista"</string>
@@ -1794,11 +1825,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Minuuttien ympyränmuotoinen liukusäädin"</string>
     <string name="select_hours" msgid="6043079511766008245">"Valitse tunnit"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Valitse minuutit"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Päiväruudukko kuukausittain"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Vuosiluettelo"</string>
     <string name="select_day" msgid="7774759604701773332">"Valitse kuukausi ja päivä"</string>
     <string name="select_year" msgid="7952052866994196170">"Valitse vuosi"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> on valittu"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> poistettiin"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> (työ)"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Poista näytön kiinnitys painamalla Edellinen- ja Viimeisimmät-kohtaa samanaikaisesti pitkään."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 837b58a..f89ed5a 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Appels Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : non transféré"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g> au bout de <xliff:g id="TIME_DELAY">{2}</xliff:g> secondes"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permet à l\'application de prendre des photos et de filmer des vidéos avec l\'appareil photo. Cette autorisation lui permet d\'utiliser l\'appareil photo à tout moment sans votre consentement."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"désactiver l\'indicateur d\'émission LED lorsque la caméra est en cours d\'utilisation"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Permet à une application système préinstallée de désactiver l\'indicateur LED d\'utilisation de la caméra."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"désactiver définitivement la tablette"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"désactiver définitivement le téléviseur"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"désactiver définitivement le téléphone"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Permet à l\'application de faire appel à des méthodes d\'ajout et de suppression de modèles d\'empreinte digitale que vous pouvez utiliser."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"utiliser le matériel d\'empreinte digitale"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Permet à l\'application d\'utiliser du matériel d\'empreinte digitale pour l\'authentification"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Empreinte digitale partielle détectée. Veuillez essayer de nouveau."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossible de traiter les empreintes digitales. Veuillez essayer de nouveau."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Le capteur d\'empreintes digitales est sale. Veuillez le nettoyer et essayer de nouveau."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Le doigt a bougé trop vite. Veuillez essayer de nouveau."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Le doigt a bougé trop lentement. Veuillez essayer de nouveau."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Message d\'erreur d\'acquisition propre au fournisseur 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Traitement impossible. Essayer de nouveau."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Matériel non disponible."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"L\'empreinte digitale ne peut pas être enregistrée. Veuillez supprimer une empreinte existante."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Le temps attribué pour lire l\'empreinte est écoulé. Veuillez essayer de nouveau."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Le temps attribué pour lire l\'empreinte est écoulé. Veuillez essayer de nouveau."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Message d\'erreur propre au fournisseur."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"lire les paramètres de synchronisation"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permet à l\'application d\'accéder aux paramètres de synchronisation d\'un compte. Par exemple, cette autorisation peut permettre de déterminer si l\'application Contacts est synchronisée avec un compte ou non."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activer ou désactiver la synchronisation"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Permet à l\'application de vérifier qu\'un paquet peut être installé."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"s\'associer à un vérificateur de paquet"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permet à l\'application autorisée d\'effectuer des requêtes de vérificateurs de paquet. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"vérifier le filtre d\'intention"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Permet à l\'application de vérifier si un filtre d\'intention est validé ou non."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"s\'associer à un vérificateur de filtre d\'intention"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Permet à l\'application autorisée d\'effectuer des requêtes de vérificateurs de filtres d\'intention. Les applications standards ne devraient jamais avoir recours à cette fonctionnalité."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"accéder aux ports série"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Permet à l\'application autorisée d\'accéder aux ports série avec l\'API SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"accès externe aux fournisseurs de contenu"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Jour précédent"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Année suivante"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Année précédente"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Le mois précédent"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Le mois prochain"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Annuler"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Supprimer"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Curseur circulaire des minutes"</string>
     <string name="select_hours" msgid="6043079511766008245">"Sélectionnez les heures"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Sélectionnez les minutes"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Calendrier mensuel sous forme de grille"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Liste des années"</string>
     <string name="select_day" msgid="7774759604701773332">"Sélectionnez un mois et un jour"</string>
     <string name="select_year" msgid="7952052866994196170">"Sélectionnez une année"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"« <xliff:g id="ITEM">%1$s</xliff:g> » a été sélectionné"</string>
     <string name="deleted_key" msgid="7659477886625566590">"« <xliff:g id="KEY">%1$s</xliff:g> » a été supprimé"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> (travail)"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Pour annuler l\'épinglage de cet écran, appuyez de manière prolongée sur Retour et Aperçu simultanément."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c6e1387..302c7c1 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Appels Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : non transféré"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : <xliff:g id="DIALING_NUMBER">{1}</xliff:g> au bout de <xliff:g id="TIME_DELAY">{2}</xliff:g> secondes"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permet à l\'application de prendre des photos et de filmer des vidéos avec l\'appareil photo. Cette autorisation lui permet d\'utiliser l\'appareil photo à tout moment sans votre consentement."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"désactiver l\'indicateur d\'émission LED lorsque la caméra est en cours d\'utilisation"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Permet à une application système préinstallée de désactiver l\'indicateur LED d\'utilisation de la caméra."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"désactiver définitivement la tablette"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"désactiver le téléviseur de manière définitive"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"désactiver définitivement le téléphone"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Autoriser l\'application à invoquer des méthodes pour ajouter et supprimer des modèles d\'empreintes digitales"</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"Utiliser le matériel d\'empreintes digitales"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Autoriser l\'application à utiliser le matériel d\'empreintes digitales pour l\'authentification"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Empreinte numérique partiellement détectée. Veuillez réessayer."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossible de reconnaître l\'empreinte numérique. Veuillez réessayer."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Le capteur d\'empreintes numériques est sale. Veuillez le nettoyer, puis réessayer."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Vous avez retiré votre doigt trop rapidement. Veuillez réessayer."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Vous avez déplacé votre doigt trop lentement. Veuillez réessayer."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Message d\'erreur d\'acquisition spécifique au fournisseur 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Impossible de reconnaître l\'empreinte numérique. Veuillez réessayer."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Matériel non disponible."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Impossible d\'enregistrer l\'empreinte numérique. Veuillez supprimer une empreinte."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Délai de détection de l\'empreinte numérique expiré. Veuillez réessayer."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Délai de détection de l\'empreinte numérique expiré. Veuillez réessayer."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Message d\'erreur spécifique au fournisseur."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"lire les paramètres de synchronisation"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permet à l\'application d\'accéder aux paramètres de synchronisation d\'un compte. Par exemple, cette autorisation peut permettre de déterminer si l\'application Contacts est synchronisée avec un compte ou non."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activer/désactiver la synchronisation"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Permet à l\'application de vérifier qu\'un package peut être installé."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"associer à un vérificateur de package"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permet à l\'application autorisée d\'effectuer des requêtes de vérificateurs de package. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"valider le filtre d\'intention"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Permet à l\'application de vérifier si un filtre d\'intention est validé ou non."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"s\'associer pour valider filtre d\'intention"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Permet à l\'application autorisée de demander la validation des filtres d\'intention. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"accéder aux ports série"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Permet à l\'application autorisée d\'accéder aux ports série avec l\'API SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"accès externe fournisseurs de contenu"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Jour précédent"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Année suivante"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Année précédente"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Mois précédent"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Mois suivant"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Annuler"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Supprimer"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Curseur circulaire des minutes"</string>
     <string name="select_hours" msgid="6043079511766008245">"Sélectionner une heure"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Sélectionner des minutes"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Calendrier mensuel sous forme de grille"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Liste des années"</string>
     <string name="select_day" msgid="7774759604701773332">"Sélectionner un mois et un jour"</string>
     <string name="select_year" msgid="7952052866994196170">"Sélectionner une année"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"\"<xliff:g id="ITEM">%1$s</xliff:g>\" sélectionné"</string>
     <string name="deleted_key" msgid="7659477886625566590">"\"<xliff:g id="KEY">%1$s</xliff:g>\" supprimé"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> (travail)"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Pour annuler l\'épinglage, appuyez de manière prolongée et simultanée sur \"Retour\" et \"Aperçu\"."</string>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index 5218ed5..a253939 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Chamadas por wifi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: non desviada"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> tras <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permite á aplicación tomar imaxes e vídeos coa cámara. Con este permiso a aplicación pode utilizar a cámara en calquera momento sen a túa confirmación."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"desactivar LED indicador de transmisión cando se está utilizando a cámara"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Permite a unha aplicación do sistema preinstalada desactivar o LED indicador de uso da cámara."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"desactivar o tablet permanentemente"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"desactivar a televisión permanentemente"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"desactivar o teléfono permanentemente"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Permite que a aplicación invoque métodos para engadir e eliminar modelos de uso de identificación dixital."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"usar hardware de identificación dixital"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Permite que a aplicación utilice hardware de identificación dixital para a autenticación"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Detectouse unha identificación dixital parcial. Téntao de novo."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Non se puido procesar a impresión dixital. Téntao de novo."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"O sensor de identificación dixital está sucio. Límpao e téntao de novo."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"O dedo moveuse demasiado rápido. Téntao de novo."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"O dedo moveuse demasiado lento. Téntao de novo."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Mensaxe de erro de adquisición específico do vendedor 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Non se pode procesar. Téntao de novo."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware non dispoñible."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Non se pode almacenar a identificación dixital. Elimina unha identificación dixital existente."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Esgotouse o tempo de espera da identificación dixital. Téntao de novo."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Esgotouse o tempo de espera da identificación dixital. Téntao de novo."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Mensaxe de erro específico do vendedor"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ler a configuración de sincronización"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite á aplicación ler a configuración de sincronización dunha conta. Por exemplo, esta acción pode determinar se a aplicación Contactos se sincroniza cunha conta."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activar e desactivar a sincronización"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Permite á aplicación verificar se un paquete é instalable."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"vincular a un verificador de paquetes"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permite ao propietario realizar solicitudes de verificadores de paquetes. As aplicacións normais non deberían necesitar este permiso."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"verificar filtro de intento"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Permite á aplicación comprobar se un filtro de intento está verificado ou non."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"ligar a verificador de filtro de intento"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Permite ao propietario realizar solicitudes de verificadores de filtros de intento. As aplicacións normais non deberían necesitar este permiso."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"acceder a portos serie"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Permite que o propietario poida acceder aos portos serie usando a API SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"acceder a provedores de contido externamente"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Reducir día"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aumentar ano"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reducir ano"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Mes anterior"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Mes seguinte"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancelar"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Eliminar"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Control de desprazamento circular dos minutos"</string>
     <string name="select_hours" msgid="6043079511766008245">"Seleccionar horas"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Seleccionar minutos"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Grade mensual de días"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Lista de anos"</string>
     <string name="select_day" msgid="7774759604701773332">"Seleccionar mes e día"</string>
     <string name="select_year" msgid="7952052866994196170">"Seleccionar ano"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> seleccionado"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> eliminado"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> do traballo"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Para soltar a pantalla, mantén premido Atrás e Visión xeral ao mesmo tempo."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 6463cc5..7407d23 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"वाई-फ़ाई कॉलिंग"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: अग्रेषित नहीं किया गया"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> सेकंड के बाद"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"ऐप्स  को कैमरे से चित्र और वीडियो लेने देता है. यह अनुमति ऐप्स  को किसी भी समय आपकी पुष्टि के बिना कैमरे का उपयोग करने देती है."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"कैमरा उपयोग में होने पर संचारण संकेतक LED अक्षम करें"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"पहले से इंस्टॉल किए गए सिस्टम ऐप्स  को कैमरे को संकेतक LED का उपयोग करने से अक्षम करती है."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"स्‍थायी रूप से टेबलेट अक्षम करें"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"टीवी को स्‍थायी रूप से अक्षम करना"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"फ़ोन को स्‍थायी रूप से अक्षम करें"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"अंगुली की छाप वाले टेम्पलेट का उपयोग करने के लिए जोड़ने और हटाने हेतु ऐप को विधियां प्रारंभ करने देती है."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"अंगुली की छाप के लिए हार्डवेयर का उपयोग करें"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"ऐप के प्रमाणीकरण के लिए अंगुली की छाप हार्डवेयर का उपयोग करने देती है"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक फ़िंगरप्रिंट की पहचान की गई. कृपया पुनः प्रयास करें."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"फ़िंगरप्रिंट संसाधित नहीं हो सका. कृपया पुन: प्रयास करें."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"फ़िंगरप्रिंट संवेदक गंदा है. कृपया साफ़ करें और पुनः प्रयास करें."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"अंगुली को तेज़ी से चलाया गया. कृपया पुनः प्रयास करें."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"अंगुली को धीरे चलाया गया. कृपया पुनः प्रयास करें."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"विक्रेता-विशिष्‍ट प्राप्‍ति त्रुटि संदेश 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"संसाधित करने में असमर्थ. पुनः प्रयास करें."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"हार्डवेयर उपलब्ध नहीं है."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"फ़िंगरप्रिंट को संग्रहीत नहीं किया जा सका. कृपया कोई मौजूदा फ़िंगरप्रिंट निकालें."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"फ़िंगरप्रिंट का समय समाप्त हो गया. पुनः प्रयास करें."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"फ़िंगरप्रिंट का समय समाप्त हो गया. पुनः प्रयास करें."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"विक्रेता-विशिष्‍ट त्रुटि संदेश."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"समन्वयन सेटिंग पढ़ें"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ऐप्स  को किसी खाते की समन्वयन सेटिंग पढ़ने देता है. उदाहरण के लिए, इससे यह निर्धारित किया जा सकता है कि लोग ऐप्स  किसी खाते के साथ समन्‍वयित है या नहीं."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"समन्‍वयन बंद या चालू टॉगल करें"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"एप्‍लि‍केशन को इंस्‍टॉल करने योग्‍य पैकेज सत्‍यापि‍त करने देता है."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"पैकेज प्रमाणक से आबद्ध करें"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"धारक को पैकेज प्रमाणक के अनुरोध की अनुमति‍ देता है. सामान्‍य ऐप्स के लिए कभी भी आवश्‍यक नहीं होना चाहिए."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"लक्ष्य फ़िल्टर सत्यापित करें"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"ऐप को जांच करने देती है कि कोई लक्ष्य फ़िल्टर सत्यापित है या नहीं."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"लक्ष्य फ़िल्टर प्रमाणक से आबद्ध करें"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"धारक को लक्ष्य फ़िल्टर प्रमाणक के अनुरोध की अनुमति देती है. सामान्य ऐप्स के लिए कभी भी आवश्यक नहीं होना चाहिए."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"सीरियल पोर्ट पर पहुंचें"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"SerialManager API का उपयोग करके धारक को सीरियल पोर्ट पर पहुंच प्रदान करता है."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"बाह्य रूप से सामग्री प्रदाताओं पर पहुंच"</string>
@@ -1211,9 +1234,9 @@
     <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> चल रहा है"</string>
     <string name="app_running_notification_text" msgid="4653586947747330058">"अधिक जानकारी के लिए या ऐप्स  रोकने के लिए स्पर्श करें."</string>
     <string name="ok" msgid="5970060430562524910">"ठीक है"</string>
-    <string name="cancel" msgid="6442560571259935130">"रहने दें"</string>
+    <string name="cancel" msgid="6442560571259935130">"अभी नहीं"</string>
     <string name="yes" msgid="5362982303337969312">"ठीक है"</string>
-    <string name="no" msgid="5141531044935541497">"रहने दें"</string>
+    <string name="no" msgid="5141531044935541497">"अभी नहीं"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"ध्यान दें"</string>
     <string name="loading" msgid="7933681260296021180">"लोड हो रहे हैं..."</string>
     <string name="capital_on" msgid="1544682755514494298">"चालू"</string>
@@ -1335,7 +1358,7 @@
     <string name="sms_short_code_details" msgid="5873295990846059400">"इससे आपके मोबाइल खाते पर "<b>"शुल्क लग सकता है"</b>"."</string>
     <string name="sms_premium_short_code_details" msgid="7869234868023975"><b>"इससे आपके मोबाइल खाते पर शुल्क लगेगा."</b></string>
     <string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"भेजें"</string>
-    <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"रहने दें"</string>
+    <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"अभी नहीं"</string>
     <string name="sms_short_code_remember_choice" msgid="5289538592272218136">"मेरी पसंद को याद रखें"</string>
     <string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"आप इसे बाद में सेटिंग &gt; ऐप्स  में बदल सकते हैं"</string>
     <string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"हमेशा अनुमति दें"</string>
@@ -1544,8 +1567,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"दिन कम करें"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"वर्ष बढ़ाएं"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"वर्ष कम करें"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"पिछला महीना"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"अगला महीना"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
-    <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"रहने दें"</string>
+    <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"अभी नहीं"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"हटाएं"</string>
     <string name="keyboardview_keycode_done" msgid="1992571118466679775">"पूर्ण"</string>
     <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"Mode change"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"मिनटों का चक्राकार स्लाइडर"</string>
     <string name="select_hours" msgid="6043079511766008245">"घंटे चुनें"</string>
     <string name="select_minutes" msgid="3974345615920336087">"मिनट चुनें"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"दिनों की माह ग्रिड"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"वर्ष की सूची"</string>
     <string name="select_day" msgid="7774759604701773332">"माह और दिन चुनें"</string>
     <string name="select_year" msgid="7952052866994196170">"वर्ष चुनें"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> चयनित"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> को हटा दिया गया"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"कार्यस्थल का <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"इस स्क्रीन को अनपिन करने के लिए, एक ही समय में वापस जाएं और अवलोकन को स्पर्श करके रखें."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 0f39d7c..a935a5a 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -127,6 +127,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi pozivi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nije proslijeđeno"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> nakon <xliff:g id="TIME_DELAY">{2}</xliff:g> s"</string>
@@ -585,6 +586,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Aplikaciji omogućuje snimanje slika i videozapisa fotoaparatom. Ta dozvola aplikaciji omogućuje upotrebu fotoaparata u bilo kojem trenutku bez vašeg odobrenja."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"onemogućavanje lampice pokazivača prijenosa kada je fotoaparat u upotrebi"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Omogućuje unaprijed instaliranim aplikacijama sustava onemogućavanje lampice pokazivača upotrebe fotoaparata."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"trajno onemogući tabletni uređaj"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"trajno onemogućivanje televizora"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"trajno onemogućavanje telefona"</string>
@@ -747,6 +750,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Aplikaciji omogućuje pozivanje načina za dodavanje i brisanje predložaka otisaka prstiju koji će se upotrijebiti."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"upotreba hardvera za čitanje otisaka prstiju"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Aplikaciji omogućuje upotrebu hardvera za čitanje otisaka prstiju radi autentifikacije."</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Otkriven je djelomični otisak prsta. Pokušajte ponovo."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Obrada otiska prsta nije uspjela. Pokušajte ponovo."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Senzor otiska prsta nije čist. Očistite ga i pokušajte ponovo."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Prebrzo pomicanje prsta. Pokušajte ponovo."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Presporo pomicanje prsta. Pokušajte ponovo."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Poruka pogreške prilikom dohvaćanja vezana uz dobavljača 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Nije obrađeno. Pokušajte ponovo."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardver nije dostupan."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Otisak prsta nije pohranjen. Uklonite postojeći otisak prsta."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Isteklo je vrijeme čekanja za otisak prsta. Pokušajte ponovo."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Isteklo je vrijeme čekanja za otisak prsta. Pokušajte ponovo."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Poruka pogreške vezana uz dobavljača."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"čitanje postavki sinkronizacije"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Aplikaciji omogućuje čitanje postavki sinkronizacije za račun. Time se, primjerice, može utvrditi je li aplikacija Osobe sinkronizirana s računom."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"uključivanje/isključivanje sinkronizacije"</string>
@@ -1120,6 +1139,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Aplikaciji omogućuje da provjeri je li paket moguće instalirati."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"vezano uz paketnu provjeru"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Nositelju omogućuje da traži paketnu provjeru. Ne bi smjelo biti potrebno za normalne aplikacije."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"potvrditi filtar namjere"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Omogućuje aplikaciji da provjeri je li filtar namjere potvrđen."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"vezati se uz potvrdu filtara namjere"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Omogućuje nositelju zahtijevanje potvrde filtara namjere. Ne bi smjelo biti potrebno za uobičajene aplikacije."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"pristup serijskim priključcima"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Rukovatelju omogućuje pristup serijskim priključcima pomoću značajke SerialManager API."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"pristup pružateljima sadržaja izvana"</string>
@@ -1552,6 +1575,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Smanjenje dana"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Povećanje godine"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Smanjenje godine"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Prethodni mjesec"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Sljedeći mjesec"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Odustani"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Izbriši"</string>
@@ -1803,11 +1828,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Kružni klizač minuta"</string>
     <string name="select_hours" msgid="6043079511766008245">"Odaberite sate"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Odaberite minute"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Mreža dana u mjesecu"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Popis godina"</string>
     <string name="select_day" msgid="7774759604701773332">"Odaberite mjesec i dan"</string>
     <string name="select_year" msgid="7952052866994196170">"Odaberite godinu"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"Odabrana je stavka <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"Izbrisan je broj <xliff:g id="KEY">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> za posao"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Da biste otkvačili ovaj zaslon, istovremeno dodirnite i zadržite Natrag i Pregled."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index adada44..9d5f0c9 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi-hívás"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: nincs átirányítva"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> másodperc után"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Lehetővé teszi az alkalmazás számára, hogy a fényképezőgéppel fotókat és videókat készítsen. Az engedéllyel rendelkező alkalmazás bármikor, az Ön jóváhagyása nélkül használhatja a fényképezőgépet."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"átviteljelző LED letiltása, ha a kamera használatban van"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Lehetővé teszi egy előre telepített rendszeralkalmazás számára, hogy letiltsa a kamerahasználatot jelző LED-et."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"a táblagép végleges deaktiválása"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"a tévé teljes letiltása"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"telefon végleges letiltása"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Lehetővé teszi az alkalmazás számára a használni kívánt ujjlenyomatsablonok hozzáadására és törlésére szolgáló metódusok indítását."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"ujjlenyomat-olvasó hardver használata"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Lehetővé teszi az alkalmazás számára az ujjlenyomat-olvasó hardver hitelesítésre való használatát"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"A rendszer az ujjlenyomatnak csak egy részletét érzékelte. Próbálkozzon újra."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nem sikerült feldolgozni az ujjlenyomatot. Próbálkozzon újra."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Az ujjlenyomat-olvasó koszos. Tisztítsa meg, majd próbálkozzon újra."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Túl hamar vette el az ujját. Próbálkozzon újra."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Túl lassan vette el az ujját. Próbálkozzon újra."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Szolgáltatóspecifikus akvizíciós hibakód 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"A feldolgozás sikertelen. Próbálkozzon újra."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"A hardver nem érhető el."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Az ujjlenyomat nem tárolható. Távolítson el egy meglévő ujjlenyomatot."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Az ujjlenyomat-beolvasási műveletkor időtúllépés történt. Próbálkozzon újra."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Az ujjlenyomat-beolvasási műveletkor időtúllépés történt. Próbálkozzon újra."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Szolgáltatóspecifikus hibaüzenet"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"szinkronizálási beállítások olvasása"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Lehetővé teszi az alkalmazás számára egy fiók szinkronizálási beállításainak beolvasását. Például ellenőrizheti, hogy a Személyek alkalmazás szinkronizálva van-e egy fiókkal."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"szinkronizálás be-és kikapcsolása"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Lehetővé teszi az alkalmazás számára, hogy ellenőrizze, egy csomag telepíthető-e."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"egy csomaghitelesítőhöz kötődnek"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Lehetővé teszi, hogy a tulajdonos kérelmeket nyújtson be a csomag hitelesítőivel kapcsolatban. A normál alkalmazásoknak erre soha nincs szüksége."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"„intent filter” hitelesítése"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Lehetővé teszi annak ellenőrzését egy alkalmazás számára, hogy egy „intent filter” hitelesítve van-e."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"rögzítés egy „intent filter” hitelesítőhöz"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Lehetővé teszi, hogy a tulajdonos kéréseket kezdeményezzen az „intent filter” hitelesítőkkel kapcsolatban. A normál alkalmazásoknak erre soha nincs szüksége."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"soros portok elérése"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Lehetővé teszi a tulajdonos számára a soros portok elérését a SerialManager API segítségével."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"tartalomszolgáltatók külső elérése"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Dátum értékének csökkentése"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Év értékének növelése"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Év értékének csökkentése"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Előző hónap"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Következő hónap"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Mégse"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Perc kör alakú csúszkája"</string>
     <string name="select_hours" msgid="6043079511766008245">"Óra kiválasztása"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Perc kiválasztása"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Napok havi leosztásban"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Évek listája"</string>
     <string name="select_day" msgid="7774759604701773332">"Válassza ki a hónapot és a napot"</string>
     <string name="select_year" msgid="7952052866994196170">"Válassza ki az évet"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> kiválasztva"</string>
     <string name="deleted_key" msgid="7659477886625566590">"A(z) <xliff:g id="KEY">%1$s</xliff:g> érték törölve"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Munkahelyi <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"A képernyő rögzítésének feloldásához tartsa lenyomva a Vissza és az Áttekintés lehetőséget egyszerre."</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 65bacf7..c1ce22f 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Զանգեր Wi-Fi-ի միջոցով"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>. Չի վերահասցեավորվել"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>. <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>. <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> վայրկյանից"</string>
@@ -236,7 +237,7 @@
     <string name="permgrouplab_writeDictionary" msgid="8090237702432576788">"Գրել օգտվողի բառարանում"</string>
     <string name="permgroupdesc_writeDictionary" msgid="2711561994497361646">"Ավելացնել բառեր օգտվողի բառարանում:"</string>
     <string name="permgrouplab_bookmarks" msgid="1949519673103968229">"Էջանիշեր և պատմություն"</string>
-    <string name="permgroupdesc_bookmarks" msgid="4169771606257963028">"Ուղղակի մուտք դեպի էջանիշեր և դիտարկչի պատմություն:"</string>
+    <string name="permgroupdesc_bookmarks" msgid="4169771606257963028">"Ուղղակի մուտք դեպի էջանիշեր և դիտարկիչի պատմություն:"</string>
     <string name="permgrouplab_deviceAlarms" msgid="6117704629728824101">"Ազդանշան"</string>
     <string name="permgroupdesc_deviceAlarms" msgid="4769356362251641175">"Կարգավորել զարթուցիչի ժամացույցը:"</string>
     <string name="permgrouplab_voicemail" msgid="4162237145027592133">"Ձայնային փոստ"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Թույլ է տալիս հավելվածին ֆոտոխցիկով լուսանկարել և տեսանկարել: Այս թույլտվությունը հնարավորություն է տալիս հավելվածին օգտագործել ֆոտոխցիկը ցանկացած ժամանակ` առանց ձեր հաստատման:"</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"անջատել փոխանցող LED ցուցիչը, երբ ֆոտոխցիկը օգտագործվում է"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Թույլ է տալիս նախապես տեղադրված համակարգային ծրագրին անջատել ֆոտոխցիկի օգտագործման LED ցուցիչը:"</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"մշտապես անջատել գրասալիկը"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"մշտապես անջատել հեռուստացույցը"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"ընդմիշտ կասեցնել հեռախոսը"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Հավելվածին թույլ է տալիս կատարել այնպիսի գործառույթներ, որոնց միջոցով կարելի է օգտագործման համար ավելացնել և հեռացնել մատնահետքերի նմուշներ:"</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"օգտագործել մատնահետքերի գրանցման սարքը"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Հավելվածին թույլ է տալիս նույնականացման համար օգտագործել մատնահետքերի գրանցման սարքը"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Մատնահետքը հայտնաբերվել է մասամբ: Փորձեք նորից:"</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Չհաջողվեց մշակել մատնահետքը: Նորից փորձեք:"</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Մատնահետքերի սենսորն աղտոտված է: Մաքրեք այն և փորձեք նորից:"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Մատը շարժեցիք շատ արագ: Փորձեք նորից:"</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Մատը շարժեցիք շատ դանդաղ: Փորձեք նորից:"</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Վաճառողի կողմից սահմանվող ձեռքբերման սխալի հաղորդագրություն 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Հնարավոր չէ շարունակել: Փորձեք նորից:"</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Սարքն անհասանելի է:"</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Հնարավոր չէ պահել մատնահետքը: Հեռացրեք առկա մատնահետքը:"</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Մատնահետքի գրանցման ժամանակը սպառվել է: Փորձեք նորից:"</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Մատնահետքի գրանցման ժամանակը սպառվել է: Փորձեք նորից:"</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Վաճառողի կողմից սահմանվող սխալի հաղորդագրություն:"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"կարդալ համաժամեցման կարգավորումները"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Թույլ է տալիս հավելվածին կարդալ համաժամեցման կարգավորումները հաշվի համար: Օրինակ` այն կարող է որոշել, արդյոք Մարդիկ հավելվածը համաժամեցված է հաշվի հետ:"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"համաժամեցումը փոխարկել միացվածի և անջատվածի"</string>
@@ -1100,11 +1119,11 @@
     <string name="autofill_area" msgid="3547409050889952423">"Տարածք"</string>
     <string name="autofill_emirate" msgid="2893880978835698818">"Էմիրություն"</string>
     <string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"կարդալ ձեր վեբ էջանիշերը և պատմությունը"</string>
-    <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Թույլ է տալիս հավելվածին կարդալ դիտարկչի այցելած բոլոր URL-ների պատմությունը և դիտարկչի բոլոր էջանիշերը: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
+    <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Թույլ է տալիս հավելվածին կարդալ դիտարկիչի այցելած բոլոր URL-ների պատմությունը և դիտարկիչի բոլոր էջանիշերը: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
     <string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"գրել վեբ էջանիշերը և պատմությունը"</string>
-    <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Թույլ է տալիս հավելվածին փոփոխել դիտարկչի պատմությունը կամ ձեր գրասալիկում պահված էջանիշերը: Այն կարող է թույլ տալ հավելվածին ջնջել կամ փոփոխել դիտարկչի տվյալները: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
+    <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Թույլ է տալիս հավելվածին փոփոխել դիտարկիչի պատմությունը կամ ձեր գրասալիկում պահված էջանիշերը: Այն կարող է թույլ տալ հավելվածին ջնջել կամ փոփոխել դիտարկիչի տվյալները: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
     <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Թույլ է տալիս հավելվածին փոփոխել դիտարկիչի պատմությունը կամ հեռուստացույցում պահված էջանիշները: Սա կարող է թույլ տալ հավելվածին ջնջել կամ փոփոխել դիտարկիչի տվյալները: Ուշադրություն. այս թույլտվությունը չի կարող հարկադրվել երրորդ կողմի դիտարկիչների կամ այլ հավելվածների կողմից, որոնք նույնպես կարողանում են վեբ էջեր բացել:"</string>
-    <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Թույլ է տալիս հավելվածին փոփոխել դիտարկչի պատմությունը կամ ձեր հեռախոսում պահված էջանիշերը: Այն կարող է թույլ տալ հավելվածին ջնջել կամ փոփոխել դիտարկչի տվյալները: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
+    <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Թույլ է տալիս հավելվածին փոփոխել դիտարկիչի պատմությունը կամ ձեր հեռախոսում պահված էջանիշերը: Այն կարող է թույլ տալ հավելվածին ջնջել կամ փոփոխել դիտարկիչի տվյալները: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
     <string name="permlab_setAlarm" msgid="1379294556362091814">"դնել ազդանշան"</string>
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Թույլ է տալիս հավելվածին սահմանել զարթուցիչի ծրագրում տեղադրված ազդանշանը: Զարթուցիչի որոշ հավելվածներ չեն կարող կիրառել այս հատկությունը:"</string>
     <string name="permlab_writeVoicemail" msgid="7309899891683938100">"գրել ձայնային փոստ"</string>
@@ -1113,12 +1132,20 @@
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Թույլ է տալիս հավելվածին ավելացնել հաղորդագրություններ ձեր ձայնային փոստի արկղում:"</string>
     <string name="permlab_readVoicemail" msgid="8415201752589140137">"կարդալ ձայնային փոստը"</string>
     <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Ծրագրին թույլ է տալիս կարդալ ձեր ձայնային փոստը"</string>
-    <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"փոփոխել դիտարկչի աշխարհագրական տեղանքի թույլտվությունները"</string>
-    <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Թույլ է տալիս հավելվածին փոփոխել դիտարկչի աշխարհագրական դիրքի թույլտվությունները: Վնասարար հավելվածները կարող են օգտագործել սա` թույլատրելու ուղարկել տեղադրության վերաբերյալ տեղեկությունները կամայական վեբ կայքերին:"</string>
+    <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"փոփոխել դիտարկիչի աշխարհագրական տեղանքի թույլտվությունները"</string>
+    <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Թույլ է տալիս հավելվածին փոփոխել դիտարկիչի աշխարհագրական դիրքի թույլտվությունները: Վնասարար հավելվածները կարող են օգտագործել սա` թույլատրելու ուղարկել տեղադրության վերաբերյալ տեղեկությունները կամայական վեբ կայքերին:"</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"հաստատել փաթեթները"</string>
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Թույլ է տալիս հավելվածին հաստատել, որ փաթեթը տեղադրելի է:"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"միանալ փաթեթի ստուգիչին"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Թույլ է տալիս սեփականատիրոջը փաթեթի ստուգիչների հարցում կատարել: Սովորական հավելվածների համար երբևէ չպետք է անհրաժեշտ լինի:"</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"մուտք գործել հաջորդական միացքներ"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Թույլ է տալիս սեփականատիրոջը մուտք գործել հաջորդական միացքներ` օգտագործելով SerialManager API-ը:"</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"դրսից մատչել բովանդակություն տրամադրողներին"</string>
@@ -1544,6 +1571,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Նվազեցնել օրը"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Աճեցնել տարին"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Նվազեցնել տարին"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Չեղարկել"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Ջնջել"</string>
@@ -1794,11 +1825,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Րոպեների ընտրություն թվատախտակից"</string>
     <string name="select_hours" msgid="6043079511766008245">"Ընտրեք ժամը"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Ընտրեք րոպեն"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Ամսաթվի ընտրության պատուհան"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Տարիների ցանկ"</string>
     <string name="select_day" msgid="7774759604701773332">"Ընտրեք ամիսն ու օրը"</string>
     <string name="select_year" msgid="7952052866994196170">"Ընտրեք տարին"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"Ընտրված է <xliff:g id="ITEM">%1$s</xliff:g> տարրը"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> թիվը ջնջված է"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Աշխատանքային <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Այս էկրան ապամրացնելու համար միաժամանակ հպեք և պահեք Հետ և Համատեսք կոճակները:"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 2f7cb25..86b642f 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Panggilan Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Tidak diteruskan"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> setelah <xliff:g id="TIME_DELAY">{2}</xliff:g> detik"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Memungkinkan aplikasi mengambil gambar dan video dengan kamera. Izin ini memungkinkan aplikasi menggunakan kamera kapan saja tanpa konfirmasi Anda."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"nonaktifkan LED indikator transmisi saat kamera digunakan"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Izinkan aplikasi sistem yang sudah dipasang sebelumnya menonaktifkan LED indikator penggunaan kamera."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"noaktifkan tablet secara permanen"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"nonaktifkan TV secara permanen"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"nonaktifkan ponsel secara permanen"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Mengizinkan aplikasi memanggil metode untuk menambahkan dan menghapus template sidik jari untuk digunakan."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"gunakan perangkat keras sidik jari"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Mengizinkan aplikasi untuk menggunakan perangkat keras sidik jari untuk otentikasi"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Sebagian sidik jari terdeteksi. Coba lagi."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Tidak dapat memproses sidik jari. Coba lagi."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Sensor sidik jari kotor. Bersihkan dan coba lagi."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Jari digerakkan terlalu cepat. Coba lagi."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Jari digerakkan terlalu lambat. Coba lagi."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Pesan kesalahan akuisisi khusus vendor 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Tidak dapat memproses. Coba lagi."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Perangkat keras tidak tersedia."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Sidik jari tidak dapat disimpan. Hapus sidik jari yang ada."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Waktu sidik jari habis. Coba lagi."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Waktu sidik jari habis. Coba lagi."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Pesan kesalahan khusus vendor"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"baca setelan sinkron"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Memungkinkan aplikasi membaca setelan sinkronisasi untuk sebuah akun. Misalnya, izin ini dapat menentukan apakah aplikasi Orang disinkronkan dengan sebuah akun."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"nyalakan dan matikan sinkronisasi"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Mengizinkan apl memverifikasi bahwa suatu paket dapat dipasang."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"mengikat ke pemverifikasi paket"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Mengizinkan pemegang mengajukan permintaan pemverifikasian paket. Tidak pernah dibutuhkan oleh apl normal."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"verifikasi maksud filter"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Mengizinkan aplikasi memeriksa apakah maksud filter diverifikasi atau tidak."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"ikat ke pemverifikasi maksud filter"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Mengizinkan pemegangnya mengajukan permintaan pemverifikasi maksud filter. Tidak diperlukan untuk aplikasi normal."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"akses port serial"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Memungkinkan pemegangnya mengakses port serial menggunakan API SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"mengakses penyedia konten dari luar"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Kurangi hari"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Tambah tahun"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Kurangi tahun"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Bulan sebelumnya"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Bulan berikutnya"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Batal"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Hapus"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Penggeser putar menit"</string>
     <string name="select_hours" msgid="6043079511766008245">"Pilih jam"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Pilih menit"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Kisi hari pada bulan"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Daftar tahun"</string>
     <string name="select_day" msgid="7774759604701773332">"Pilih bulan dan hari"</string>
     <string name="select_year" msgid="7952052866994196170">"Pilih tahun"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> dipilih"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> dihapus"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Kantor <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Untuk melepas pin layar ini, sentuh lama tombol Kembali dan Ringkasan secara bersamaan."</string>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index a926d3d..8f17e90 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi símtöl"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ekki áframsent"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> eftir <xliff:g id="TIME_DELAY">{2}</xliff:g> sekúndur"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Leyfir forriti að taka myndir og myndskeið með myndavélinni. Þessi heimild leyfir forritinu að nota myndavélina hvenær sem er án þinnar heimildar."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"slökkva á gaumljósi flutnings þegar myndavél er í notkun"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Leyfir foruppsettu kerfisforriti að gera gaumljós myndavélarinnar óvirkt."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"gera spjaldtölvuna varanlega óvirka"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"gera sjónvarpið varanlega óvirkt"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"gera símann varanlega óvirkan"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Gerir forritinu kleift að beita aðferðum til að bæta við og eyða fingrafarasniðmátum til notkunar."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"nota fingrafarabúnað"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Leyfir forritinu að nota fingrafarabúnað til auðkenningar"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Hluti fingrafars greindist. Reyndu aftur."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Ekki var hægt að vinna úr fingrafarinu. Reyndu aftur."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingrafaraskynjarinn er óhreinn. Hreinsaðu hann og reyndu aftur."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Fingurinn hreyfðist of hratt. Reyndu aftur."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Fingurinn hreyfðist of hægt. Reyndu aftur."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Skráningarvilluboð 0 frá framleiðanda"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Ekki var hægt að vinna úr þessu. Reyndu aftur."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Vélbúnaður er ekki tiltækur."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Ekki er hægt að vista fingrafarið. Fjarlægðu eitthvert af fingraförunum sem fyrir eru."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Tímamörk runnu út fyrir fingrafar. Reyndu aftur."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Tímamörk runnu út fyrir fingrafar. Reyndu aftur."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Villuboð frá framleiðanda."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"lesa samstillingar"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Leyfir forriti að lesa kosti samstillingar fyrir reikning. Þetta er til dæmis hægt að nota til að komast að því hvort forritið Fólk er samstillt við reikning."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"kveikja og slökkva á samstillingu"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Leyfir forriti að staðfesta að hægt sé að setja upp pakka."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"bindast pakkasannvottun"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Leyfir handhafa að búa til beiðnir fyrir pakkastaðfestingu. Ætti aldrei að vera nauðsynlegt fyrir venjuleg forrit."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"staðfesta ásetningssíu"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Leyfir forriti að athuga hvort ásetningssía er staðfest eða ekki."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"bindast staðfestingu ásetningssíu"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Leyfir handhafa að búa til beiðnir fyrir staðfestingu ásetningssíu. Ætti aldrei að vera nauðsynlegt fyrir venjuleg forrit."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"aðgangur að raðtengjum"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Leyfir forriti að fá aðgang að raðtengjum með forritaskilum SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"ytri aðgangur að efnisveitum"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Niður um dag"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Upp um ár"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Niður um ár"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Fyrri mánuður"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Næsti mánuður"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Hætta við"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Eyða"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Valskífa fyrir mínútur"</string>
     <string name="select_hours" msgid="6043079511766008245">"Veldu klukkustundir"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Veldu mínútur"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Mánaðartafla með dögum"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Áralisti"</string>
     <string name="select_day" msgid="7774759604701773332">"Veldu mánuð og dag"</string>
     <string name="select_year" msgid="7952052866994196170">"Veldu ár"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> valið"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> eytt"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> í vinnu"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Til að taka lásinn af þessari skjámynd skaltu halda inni Til baka og Yfirliti samtímis."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 74ad5f7..adabdfb 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Chiamate Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: inoltro non effettuato"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g><xliff:g id="DIALING_NUMBER">{1}</xliff:g> dopo <xliff:g id="TIME_DELAY">{2}</xliff:g> secondi"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Consente all\'applicazione di scattare foto e riprendere video con la fotocamera. Questa autorizzazione consente all\'applicazione di utilizzare la fotocamera in qualsiasi momento senza la tua conferma."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"disabilitazione del LED di indicazione della trasmissione quando la fotocamera è in uso"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Consente a un\'applicazione di sistema preinstallata di disabilitare il LED che indica l\'utilizzo della fotocamera."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"disattivazione definitiva tablet"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"disattivazione definitiva della TV"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"disattivazione telefono"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Consente all\'app di richiamare metodi per aggiungere e rimuovere modelli di impronte digitali da utilizzare."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"utilizza hardware per il riconoscimento delle impronte digitali"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Consente all\'app di utilizzare l\'hardware per il riconoscimento delle impronte digitali per eseguire l\'autenticazione"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Rilevata impronta digitale parziale. Riprova."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Impossibile elaborare l\'impronta digitale. Riprova."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Il sensore di impronte digitali è sporco. Puliscilo e riprova."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Movimento del dito troppo rapido. Riprova."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Movimento del dito troppo lento. Riprova."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Messaggio di errore di acquisizione specifico del fornitore: 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Impossibile elaborare l\'impronta. Riprova."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware non disponibile."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Impossibile memorizzare l\'impronta digitale. Rimuovi un\'impronta esistente."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Timeout impronta digitale. Riprova."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Timeout impronta digitale. Riprova."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Messaggio di errore specifico del fornitore."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"lettura impostazioni di sincronizz."</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Consente all\'applicazione di leggere le impostazioni di sincronizzazione per un account. Ad esempio, questa autorizzazione può determinare se l\'applicazione Persone è sincronizzata con un account."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"attivazione e disattivazione della sincronizzazione"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Consente all\'applicazione di verificare se un pacchetto è installabile."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"associazione a verifica pacchetto"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Consente al proprietario di effettuare richieste relative alle verifiche dei pacchetti. Non dovrebbe mai essere necessario per le normali applicazioni."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"verifica filtro di intent"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Consente all\'app di verificare se un filtro di intent è stato verificato o meno."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"collegamento a uno strumento di verifica dei filtri di intent"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Consente al proprietario di effettuare richieste relative agli strumenti di verifica dei filtri di intent. Non dovrebbe mai essere necessario per applicazioni normali."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"accesso alle porte seriali"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Permette al proprietario di accedere alle porte seriali utilizzando l\'API SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"accesso a fornitori di contenuti esterni"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Riduci giorno"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aumenta anno"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Riduci anno"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Mese precedente"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Mese successivo"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Annulla"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Canc"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Dispositivo di scorrimento circolare per i minuti"</string>
     <string name="select_hours" msgid="6043079511766008245">"Seleziona le ore"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Seleziona i minuti"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Griglia di giorni per mese"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Elenco degli anni"</string>
     <string name="select_day" msgid="7774759604701773332">"Seleziona mese e giorno"</string>
     <string name="select_year" msgid="7952052866994196170">"Seleziona anno"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"Elemento selezionato: <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> eliminato"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> lavoro"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Per sbloccare questa schermata, tocca e tieni premute contemporaneamente le opzioni Indietro e Panoramica."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 1e8d680..007245b 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -128,6 +128,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"‏שיחות ב-Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ללא העברה"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> כעבור <xliff:g id="TIME_DELAY">{2}</xliff:g> שניות"</string>
@@ -586,6 +587,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"מאפשר לאפליקציה לצלם תמונות וסרטונים באמצעות המצלמה. אישור זה מאפשר לאפליקציה להשתמש במצלמה בכל עת ללא אישורך."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"השבת את נורית מצב השידור כשהמצלמה בשימוש"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"מתיר לאפליקציית מערכת המותקן מראש להשבית את השימוש של המצלמה בנורית המצב."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"השבת טאבלט לצמיתות"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"השבתה של הטלוויזיה לצמיתות"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"השבת טלפון לצמיתות"</string>
@@ -748,6 +751,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"מאפשר לאפליקציה להפעיל שיטות להוספה ומחיקה של תבניות טביעות אצבעות שבהן ייעשה שימוש."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"שימוש בחומרה של טביעות אצבעות"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"מאפשר לאפליקציה להשתמש בחומרה של טביעות אצבעות לצורך אימות"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"זוהתה טביעת אצבע חלקית. נסה שוב."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"לא ניתן היה לעבד את טביעת האצבע. נסה שוב."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"החיישן של טביעות האצבעות מלוכלך. נקה אותו ונסה שוב."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"האצבע זזה מהר מדי, נסה שוב."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"האצבע זזה לאט מדי, נסה שוב."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"הודעת שגיאה 0 של רכישה ספציפית לספק"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"אין אפשרות לעבד. נסה שוב."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"החומרה לא זמינה."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"לא ניתן לאחסן טביעת אצבע. הסר טביעת אצבע קיימת."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"חלף הזמן הקצוב לטביעת אצבע. נסה שוב."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"חלף הזמן הקצוב לטביעת אצבע. נסה שוב."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"הודעת שגיאה ספציפית לספק."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"קרא את הגדרות הסינכרון"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"מאפשר לאפליקציה לקרוא את הגדרות הסנכרון של חשבון. לדוגמה, ניתן לגלות כך האם האפליקציה \'אנשים\' מסונכרן עם חשבון כלשהו."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"הפעלת וכיבוי סנכרון"</string>
@@ -1121,6 +1140,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"מאפשר לאפליקציה לאמת שחבילה ניתנת להתקנה."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"הכפפה למאמת חבילה"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"מאפשר למשתמש להגיש בקשות של מאמתי חבילות. הרשאה זו לעולם אינה נחוצה לאפליקציות רגילים."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"אימות של מסנן כוונה"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"מאפשר לאפליקציה לבדוק אם מסנן כוונה אומת או לא."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"הכפפה אל מאמת של מסנן כוונה"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"מאפשר למשתמש להגיש בקשות של מאמתי סינון כוונה. לעולם לא אמור להיות נחוץ לאפליקציות רגילות."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"גישה ליציאות טוריות"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"‏מאפשר לבעלים לגשת ליציאות טוריות באמצעות ממשק ה- API של SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"גישה לספקי תוכן באופן חיצוני"</string>
@@ -1560,6 +1583,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"הפחת יום"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"הוסף שנה"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"הפחת שנה"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"החודש הקודם"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"החודש הבא"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"ביטול"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"מחק"</string>
@@ -1812,11 +1837,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"מחוון דקות מעגלי"</string>
     <string name="select_hours" msgid="6043079511766008245">"בחר שעות"</string>
     <string name="select_minutes" msgid="3974345615920336087">"בחר דקות"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"בחירת ימים בחודש בתצוגת רשת"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"רשימת שנים"</string>
     <string name="select_day" msgid="7774759604701773332">"בחר חודש ויום"</string>
     <string name="select_year" msgid="7952052866994196170">"בחר שנה"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> נבחר"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> נמחק"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"עבודה <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"כדי לבטל את הקפאת המסך הזה, גע בו-זמנית נגיעה ממושכת ב\'הקודם\' ו\'סקירה\'."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 5598018..51055d8 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi通話"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:転送できません"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g> (<xliff:g id="TIME_DELAY">{2}</xliff:g>秒後)"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"カメラでの写真と動画の撮影をアプリに許可します。これにより、アプリが確認なしでいつでもカメラを使用できるようになります。"</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"カメラの使用中に通信インジケータLEDを無効にする"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"カメラ使用インジケータLEDを無効にすることをプレインストールされているシステムアプリに許可します。"</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"タブレットを完全に無効化"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"テレビを完全に無効化"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"端末を永続的に無効にする"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"使用する指紋テンプレートの追加や削除を行う方法の呼び出しをアプリに許可します。"</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"指紋ハードウェアの使用"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"指紋ハードウェアを認証に使用することをアプリに許可します"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"指紋を一部しか検出できませんでした。もう一度お試しください。"</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"指紋を処理できませんでした。もう一度お試しください。"</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指紋センサーに汚れがあります。汚れを落としてもう一度お試しください。"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"指の動きが速すぎました。もう一度お試しください。"</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"指の動きが遅すぎました。もう一度お試しください。"</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"ベンダー固有の取得エラーメッセージ0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"処理できませんでした。もう一度お試しください。"</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"ハードウェアを利用できません。"</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"指紋を保存できません。既存の指紋を削除してください。"</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"指紋の読み取りがタイムアウトになりました。もう一度お試しください。"</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"指紋の読み取りがタイムアウトになりました。もう一度お試しください。"</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"ベンダー固有のエラーメッセージです。"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"同期設定の読み取り"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"アカウントの同期設定の読み取りをアプリに許可します。たとえば、連絡帳アプリがアカウントと同期しているかどうかをアプリから特定できるようになります。"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"同期のON/OFFの切り替え"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"パッケージがインストール可能かどうか確認することをアプリに許可します。"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"パッケージベリファイアにバインド"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"パッケージベリファイアのリクエストを所有者に許可します。通常のアプリでは不要です。"</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"インテントフィルタの検証"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"インテントフィルタが検証されているかどうかを確認することをアプリに許可します。"</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"インテントフィルタベリファイアにバインド"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"インテントフィルタベリファイアのリクエストを所有者に許可します。通常のアプリでは不要です。"</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"シリアルポートへのアクセス"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"SerialManager APIを使用してシリアルポートにアクセスすることを所有者に許可します。"</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"コンテンツプロバイダへの外部アクセス"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"1日戻します"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"1年進めます"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"1年戻します"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"前月"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"翌月"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"キャンセル"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"削除"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"円形スライダー(分)"</string>
     <string name="select_hours" msgid="6043079511766008245">"時間を選択"</string>
     <string name="select_minutes" msgid="3974345615920336087">"分を選択"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"日グリッド(月別)"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"年リスト"</string>
     <string name="select_day" msgid="7774759604701773332">"月と日を選択"</string>
     <string name="select_year" msgid="7952052866994196170">"年を選択"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g>を選択しました"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g>を削除しました"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"仕事の<xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"この画面の固定を解除するには[戻る]と[最近]を同時に押し続けます。"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index d179c28..7a0483b 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"დარეკვა Wi-Fi-ს მეშვეობით"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: არ არის გადამისამართებული"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> წამის შემდეგ"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"აპს შეეძლება კამერით სურათისა და ვიდეოს გადაღება. ეს ნებართვა აპს უფლებას აძლევს, ნებისმიერ დროს გამოიყენოს კამერა თქვენი დადასტურების გარეშე."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"კამერის გამოყენებისას გადამცემი ინდიკატორის LED გათიშვა"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"ნებას რთავს წინასწარ დაყენებული სისტემის აპლიკაციას, გამორთოს კამერის გამოყენების ინდიკატორი LED."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"მუდმივად გამორთული ტაბლეტი"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"ტელევიზორის მუდმივად გამორთვა"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"ტელეფონის სამუდამოდ დეაქტივაცია"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"საშუალებას აძლევს აპლიკაციას დაამატოს ან ამოშალოს გამოსაყენებელი თითის ანაბეჭდის ნიმუშები,"</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"თითის ანაბეჭდის აპარატის გამოყენება"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"საშუალებას აძლევს აპლიკაციას გამოიყენოს ავტენთიფიკაციისათვის თითის ანაბეჭდის აპარატი"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"აღმოჩენილია თითის ნაწილობრივი ანაბეჭდი. გთხოვთ, სცადოთ ხელახლა."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"თითის ანაბეჭდი ვერ მუშავდება. გთხოვთ, სცადოთ ხელახლა."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"თითის ანაბეჭდის სენსორი დაბინძურებულია. გთხოვთ, გაასუფთაოთ და სცადოთ ხელახლა."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"თითის აღება მეტისმეტად სწრაფად მოხდა. გთხოვთ, სცადოთ ხელახლა."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"თითის აღება მეტისმეტად ნელა მოხდა. გთხოვთ, სცადოთ ხელახლა."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"მომწოდებლის მიხედვით სპეციფიკური მოპოვების შეცდომის შეტყობინება 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"დამუშავება შეუძლებელია. სცადეთ ხელახლა."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"აპარატურა არ არის ხელმისაწვდომი."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"თითის ანაბეჭდის შენახვა ვერ ხერხდება. გთხოვთ, ამოშალოთ არსებული თითის ანაბეჭდი."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"თითის ანაბეჭდის ლოდინის დრო ამოიწურა. სცადეთ ხელახლა."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"თითის ანაბეჭდის ლოდინის დრო ამოიწურა. სცადეთ ხელახლა."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"მომწოდებლის მიხედვით სპეციფიკური შეცდომის შეტყობინება."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"სინქრონიზაციის პარამეტრების წაკითხვა"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"აპს შეეძლება, წაიკითხოს ანგარიშის სინქრონიზაციის პარამეტრები. მაგალითად, მას შეეძლება განსაზღვროს, არის თუ არა People აპი სინქრონიზებული ანგარიშთან."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"სინქრონიზაციის ჩართვა და გამორთვა"</string>
@@ -1119,6 +1138,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"აპს შეუძლია დაადასტუროს პაკეტის დაყანების შესაძლებლობა."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"პაკეტების ვერიფიკატორებთან დაკავშირება"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"მფლობელს შეეძლება პაკეტის ვერიფიკატორების მოთხოვნა. არასდროს გამოიყენება ჩვეულებრივ აპებში."</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"სერიულ პორტებზე წვდომა"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"მფლობელს შეეძლება სერიულ პორტებზე წვდომა სერიული მენეჯერის  API-ის გამოყენებით."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"კონტენტის მომწოდებლებთან გარედან წვდომა"</string>
@@ -1544,6 +1571,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"დღის მოკლება"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"წლის მომატება"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"წლის მოკლება"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"გაუქმება"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"წაშლა"</string>
@@ -1794,11 +1825,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"წუთების წრიული სლაიდერი"</string>
     <string name="select_hours" msgid="6043079511766008245">"აირჩიეთ საათები"</string>
     <string name="select_minutes" msgid="3974345615920336087">"აირჩიეთ წუთები"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"დღეების ბადე თვეზე"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"წლის სია"</string>
     <string name="select_day" msgid="7774759604701773332">"აირჩიეთ თვე და რიცხვი"</string>
     <string name="select_year" msgid="7952052866994196170">"აირჩიეთ წელი"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"არჩეულია <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> წაიშალა"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"სამსახური <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"მიმაგრების გასაუქმებლად ერთდროულად შეეხეთ და არ აუშვათ ღილაკებს „უკან“ და „მიმოხილვა“."</string>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index caa12d2..f843dbe 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi қоңыраулары"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Басқа нөмірге бағытталмады"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>  <xliff:g id="TIME_DELAY">{2}</xliff:g> секундтан кейін"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Қолданбаға камераны қолданып, фотосурет немесе бейне жазу мүмкіндігін береді. Бұл рұқсат камераны кез келген уақытта сіздің құптауыңызды қажет етпей қолдану мүмкіндігін береді."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"камера қолданыста болғанда жарық диодты шамы бар көрсеткішті өшіру"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Алдын ала орнатылған жүйе қолданбасына камераның жарық диодты көрсеткішті қолдануын өшіру мүмкіндігін береді."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"планшетті мүлдем өшіру"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"ТД біржола өшіру"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"телефонды мүлдем өшіру"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Қолданбаға пайдаланатын саусақ ізі үлгілерін қосу және жою әдістерін шақыруға мүмкіндік береді."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"саусақ ізі жабдығын пайдалану"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Қолданбаға аутентификацияалу үшін саусақ ізі жабдығын пайдалануға мүмкіндік береді"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Саусақ ізі ішінара анықталды. Әрекетті қайталаңыз."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Саусақ ізін өңдеу мүмкін емес. Әрекетті қайталаңыз."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Саусақ ізі сенсоры лас. Тазалап, әрекетті қайталаңыз."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Саусақ тым тез қозғалды. Әрекетті қайталаңыз."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Саусақ тым баяу қозғалды. Әрекетті қайталаңыз."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Жеткізушіге тән алу қатесі туралы хабар 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Өңдеу мүмкін емес. Әрекетті қайталаңыз."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Жабдық қол жетімді емес."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Саусақ ізін сақтау мүмкін емес. Бар саусақ ізін жойыңыз."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Саусақ ізін күту уақыты бітті. Әрекетті қайталаңыз."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Саусақ ізін күту уақыты бітті. Әрекетті қайталаңыз."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Жеткізушіге тән қате туралы хабар."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"синх параметрлерін оқу"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Қолданбаға есептік жазба синхрондау параметрлерін оқу мүмкіндігін береді. Мысалы, бұл арқылы People қолданбасының есептік жазбамен сихрондалғаны анықталуы мүмкін."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"синх қосу және өшіру арасында ауысу"</string>
@@ -1119,6 +1138,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Қолданбаға буманы орнатуға болатынын тексеруге рұқсат береді."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"жинақ растау қызметіне байланыстыру"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Пайдаланушыға бума верификаторларының сұрауларын жасауға рұқсат береді. Қалыпты қолданбалар үшін ешқашан қажет болмайды."</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"сериялық портқа кіру"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Пайдаланушыға Сериялық Менеджер қолданба бағдарламалау интерфейсін қолданып, сериялық порттарға кіру мүмкіндігін ұсынады."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"мазмұн жабдықтаушыларға сырттан қатынасу"</string>
@@ -1544,6 +1571,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Күн азайту"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Жыл арттыру"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Жыл азайту"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Өшіру"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Жою"</string>
@@ -1794,11 +1825,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Минут айналымын қозғалтқыш"</string>
     <string name="select_hours" msgid="6043079511766008245">"Сағат таңдау"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Минут таңдау"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Күндердің айлық торлары"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Жыл тізімі"</string>
     <string name="select_day" msgid="7774759604701773332">"Ай мен күнді таңдау"</string>
     <string name="select_year" msgid="7952052866994196170">"Жыл таңдау"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> таңдалды"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> жойылды"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Жұмыс <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Осы экранды босату үшін «Кері» және «Шолу» пәрмендерін бір уақытта түртіп, ұстап тұрыңыз."</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 3326462..dffdf5e 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"ការហៅតាម Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> ៖ មិន​បាន​បញ្ជូន​បន្ត"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> បន្ទាប់​ពី <xliff:g id="TIME_DELAY">{2}</xliff:g> វិនាទី"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"ឲ្យ​កម្មវិធី​ថត​រូប និង​វីដេអូ​ដោយ​ប្រើ​ម៉ាស៊ីន​ថត។ វា​ឲ្យ​កម្មវិធី​​ប្រើ​ម៉ាស៊ីន​ថត​នៅ​ពេល​​ណាមួយ​ដោយ​គ្មាន​ការ​បញ្ជាក់​របស់​អ្នក។"</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"បិទ​​ពន្លឺ​បង្ហាញ​ការ​បញ្ជូន​​ពេល​ម៉ាស៊ីន​ថត​កំពុង​ប្រើ"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"ឲ្យ​កម្មវិធី​ប្រព័ន្ធ​ដែល​បាន​ដំឡើង​រួច​បិទ​​ LED បង្ហាញ​ការ​ប្រើ​ម៉ាស៊ីន​ថត។"</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"បិទ​កុំព្យូទ័រ​បន្ទះ​ជា​អចិន្ត្រៃយ៍"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"បិទដំណើរការទូរទស្សន៍ជាអចិន្ត្រៃយ៍"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"បិទ​ទូរស័ព្ទ​ជា​អចិន្ត្រៃយ៍"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"អនុញ្ញាតឲ្យកម្មវិធីប្រើវិធីសាស្ត្របន្ថែម និងលុបពុម្ពម្រាមដៃសម្រាប់ប្រើប្រាស់។"</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"ប្រើផ្នែករឹងស្នាមម្រាមដៃ"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"អនុញ្ញាតឲ្យកម្មវិធីប្រើផ្នែករឹងស្នាមម្រាមដៃសម្រាប់ការផ្ទៀងផ្ទាត់"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"បានផ្តិតយកស្នាមម្រាមដៃមិនពេញលក្ខណៈ។ សូមព្យាយាមម្តងទៀត។"</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"មិនអាចដំណើរការស្នាមម្រាមដៃបានទេ។ សូមព្យាយាមម្តងទៀត។"</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ឧបករណ៍ផ្តិតម្រាមដៃប្រលាក់ហើយ។ សូមសម្អាត ហើយព្យាយាមម្តងទៀត។"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"ម្រាមដៃមានចលនារហ័សពេក។ សូមព្យាយាមម្តងទៀត។"</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"ម្រាមដៃមានចលនាយឺតពេក។ សូមព្យាយាមម្តងទៀត។"</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"សារកំហុសនៃការទិញពីអ្នកលក់ជាក់លាក់ 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"មិនអាចដំណើរការបានទេ។ សូមព្យាយាមម្តងទៀត។"</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"មិនមានផ្នែករឹងទេ។"</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"មិនអាចផ្ទុកស្នាមម្រាមដៃទេ។ សូមយកស្នាមម្រាមដៃដែលមានស្រាប់ចេញ។"</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"ការផ្តិតម្រាមដៃបានអស់ពេល។ សូមព្យាយាមម្តងទៀត។"</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"ការផ្តិតម្រាមដៃបានអស់ពេល។ សូមព្យាយាមម្តងទៀត។"</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"សារកំហុសពីអ្នកលក់ជាក់លាក់។"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"អាន​ការ​កំណត់​ធ្វើ​សម​កាល​កម្ម"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ឲ្យ​កម្មវិធី​អាន​ការ​កំណត់​ធ្វើ​សម​កាល​កម្ម​សម្រាប់​គណនី។ ឧទាហរណ៍ វា​អាច​កំណត់​ថា​តើ​​​កម្មវិធី​ត្រូវ​បាន​បើក​ជា​មួយ​គណនី​ដែរ​ឬទេ។"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"បិទ/បើក​ការ​ធ្វើ​សម​កាល​កម្ម"</string>
@@ -1119,6 +1138,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"ឲ្យ​កម្មវិធី​ផ្ទៀងផ្ទាត់​កញ្ចប់​ដែល​អាច​ដំឡើង​បាន។"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"ចង​ទៅ​កម្មវិធី​ផ្ទៀងផ្ទាត់​កញ្ចប់"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"ឲ្យ​ម្ចាស់​ស្នើ​កម្មវិធី​ផ្ទៀងផ្ទាត់​កញ្ចប់។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"ចូល​ដំណើរការ​ច្រក​ស៊េរី"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"អនុញ្ញាត​ឱ្យ​ចូល​ដំណើរ​ការ​ទៅ​កាន់​ច្រក​សៀរៀល​ដោយ​ប្រើ SerialManager API ។"</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"ចូល​ដំណើរការ​ក្រុមហ៊ុន​ផ្ដល់​មាតិកា​ខាង​ក្រៅ"</string>
@@ -1546,6 +1573,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"បន្ថយ​ថ្ងៃ"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"បង្កើន​​ឆ្នាំ"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"បន្ថយ​ឆ្នាំ"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់​"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string>
@@ -1796,11 +1827,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"គ្រាប់​រំកិល​រង្វង់​នាទី"</string>
     <string name="select_hours" msgid="6043079511766008245">"ជ្រើស​ម៉ោង"</string>
     <string name="select_minutes" msgid="3974345615920336087">"ជ្រើស​នាទី"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"ក្រឡា​​​ខែ​នៃ​ថ្ងៃ"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"បញ្ជី​ឆ្នាំ"</string>
     <string name="select_day" msgid="7774759604701773332">"ជ្រើស​ខែ និង​ថ្ងៃ"</string>
     <string name="select_year" msgid="7952052866994196170">"ជ្រើស​ឆ្នាំ"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"បាន​ជ្រើស <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"បាន​លុប <xliff:g id="KEY">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"កន្លែង​ធ្វើការ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"ដើម្បី​មិន​ភ្ជាប់​អេក្រង់​នេះ ប៉ះ ហើយ​សង្កត់​ថយក្រោយ និង​ទិដ្ឋភាព​នៅ​ពេល​តែ​មួយ។"</string>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index 2bd45d4..cde560f 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"ವೈ-ಫೈ ಕರೆ ಮಾಡುವಿಕೆ"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ಫಾರ್ವರ್ಡ್ ಮಾಡಲಾಗಿಲ್ಲ"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> ಸೆಕೆಂಡುಗಳ ನಂತರ <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"ಕ್ಯಾಮರಾ ಮೂಲಕ ಚಿತ್ರಗಳು ಮತ್ತು ವೀಡಿಯೊಗಳನ್ನು ಸೆರೆಹಿಡಿಯಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಈ ಅನುಮತಿಯು ನಿಮ್ಮ ಖಾತರಿ ಇಲ್ಲದೆಯೇ ಯಾವುದೇ ಸಮಯದಲ್ಲಿ ಕ್ಯಾಮರಾವನ್ನು ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"ಕ್ಯಾಮರಾ ಬಳಕೆಯಲ್ಲಿರುವಾಗ ಪ್ರಸಾರ ಸೂಚಕ LED ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"ಕ್ಯಾಮರಾ ಬಳಕೆ ಸೂಚಕ LED ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಪೂರ್ವ-ಸ್ಥಾಪಿತ ಸಿಸ್ಟಂ ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"ಶಾಶ್ವತವಾಗಿ ಟ್ಯಾಬ್ಲೆಟ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"ಟಿವಿಯನ್ನು ಶಾಶ್ವತವಾಗಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"ಶಾಶ್ವತವಾಗಿ ಫೋನ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"ಬಳಕೆಗೆ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಟೆಂಪ್ಲೇಟ್‌ಗಳನ್ನು ಸೇರಿಸಲು ಮತ್ತು ಅಳಿಸಲು ವಿಧಾನಗಳನ್ನು ಮನವಿ ಮಾಡಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಹಾರ್ಡ್‌ವೇರ್ ಬಳಸಿ"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"ಪ್ರಮಾಣೀಕರಣಕ್ಕಾಗಿ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಹಾರ್ಡ್‌ವೇರ್ ಬಳಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ಭಾಗಶಃ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಪತ್ತೆಯಾಗಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸಾರ್ ಕೊಳೆಯಾಗಿದೆ. ದಯವಿಟ್ಟು ಅದನ್ನು ಸ್ವಚ್ಛಗೊಳಿಸಿ ಹಾಗೂ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"ಬೆರಳನ್ನು ವೇಗವಾಗಿ ಸರಿಸಲಾಗಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"ಬೆರಳನ್ನು ನಿಧಾನವಾಗಿ ಸರಿಸಲಾಗಿದೆ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"ಮಾರಾಟಗಾರ-ನಿರ್ದಿಷ್ಟ ಸ್ವಾಧೀನ ದೋಷ ಸಂದೇಶ 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"ಹಾರ್ಡ್‌ವೇರ್ ಲಭ್ಯವಿಲ್ಲ."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸಂಗ್ರಹಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ತೆಗೆದುಹಾಕಿ."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅವಧಿ ಮೀರಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅವಧಿ ಮೀರಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"ಮಾರಾಟಗಾರ-ನಿರ್ದಿಷ್ಟ ದೋಷ ಸಂದೇಶ."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ಸಿಂಕ್ ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ರೀಡ್‌ ಮಾಡು"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ಒಂದು ಖಾತೆಯ ಸಿಂಕ್ ಸೆಟ್ಟಿಂಗ್‍‍ಗಳನ್ನು ಓದಲು ಅಪ್ಲಿಕೇಶನ್‍‍ಗೆ ಅವಕಾಶ ನೀಡುತ್ತದೆ. ಉದಾಹರಣೆಗೆ, ಖಾತೆಯೊಂದಿಗೆ ಜನರ ಅಪ್ಲಿಕೇಶನ್ ಸಿಂಕ್ ಮಾಡಲಾಗಿದೆಯೇ ಎಂಬುದನ್ನು ಇದು ನಿರ್ಧರಿಸಬಹುದು."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ಸಿಂಕ್ ಆನ್ ಮತ್ತು ಸಿಂಕ್ ಆಫ್ ಟಾಗಲ್ ಮಾಡಿ"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"ಪ್ಯಾಕೇಜ್‌‌‌ ಅನ್ನು ಸ್ಥಾಪಿಸಬಹುದಾದ ಪರಿಶೀಲನೆಯನ್ನು ಅಪ್ಲಿಕೇಶನ್‌‌ ಅನುಮತಿಸುತ್ತದೆ."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"ಪ್ಯಾಕೇಜ್ ಪರಿಶೀಲಕಕ್ಕೆ ಪ್ರತಿಬಂಧಿಸಿ"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"ಪ್ಯಾಕೇಜ್‌ ಪರಿಶೀಲನಾಗಾರರ ವಿನಂತಿಗಳನ್ನು ಮಾಡಲು ಹೊಂದಿರುವವರಿಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಸಾಮಾನ್ಯ ಅಪ್ಲಿಕೇಶನ್‌ಗಳಿಗೆ ಎಂದಿಗೂ ಅಗತ್ಯವಿರುವುದಿಲ್ಲ."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"ಉದ್ದೇಶಿತ ಫಿಲ್ಟರ್ ಪರಿಶೀಲಿಸಿ"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"ಇಂಟರ್ನೆಟ್ ಫಿಲ್ಟರ್ ಅನ್ನು ಪರಿಶೀಲಿಸಲಾಗಿದೆಯೇ ಅಥವಾ ಇಲ್ಲವೆ ಎಂಬುದನ್ನು ಪರೀಕ್ಷಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"ಉದ್ದೇಶಿತ ಫಿಲ್ಟರ್ ಪರಿಶೀಲಕವನ್ನು ಪ್ರತಿಬಂಧಿಸಿ"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"ಉದ್ದೇಶಿತ ಫಿಲ್ಟರ್ ಪರಿಶೀಲನಾಗಾರರ ವಿನಂತಿಗಳನ್ನು ಮಾಡಲು ಹೊಂದಿರುವವರಿಗೆ ಅವಕಾಶ ಮಾಡಿಕೊಡುತ್ತದೆ. ಸಾಮಾನ್ಯ ಅಪ್ಲಿಕೇಶನ್‌ಗಳಿಗೆ ಎಂದಿಗೂ ಅಗತ್ಯವಿರುವುದಿಲ್ಲ."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"ಸರಣಿ ಪೋರ್ಟ್‌ಗಳನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"SerialManager API ಬಳಸಿಕೊಂಡು ಸರಣಿ ಪೋರ್ಟ್‌ಗಳಿಗೆ ಪ್ರವೇಶ ಪಡೆಯಲು ಹೊಂದಿರುವವರಿಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"ವಿಷಯ ಪೂರೈಕೆದಾರರನ್ನು ಬಾಹ್ಯ ರೀತಿಯಲ್ಲಿ ಪ್ರವೇಶಿಸಿ"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"ದಿನವನ್ನು ಕಡಿಮೆಮಾಡಿ"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"ವರ್ಷವನ್ನು ಹೆಚ್ಚಿಸಿ"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"ವರ್ಷವನ್ನು ಕಡಿಮೆಮಾಡಿ"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"ಹಿಂದಿನ ತಿಂಗಳು"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"ಮುಂದಿನ ತಿಂಗಳು"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"ರದ್ದುಮಾಡು"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"ಅಳಿಸು"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"ನಿಮಿಷಗಳ ವೃತ್ತಾಕಾರ ಸ್ಲೈಡರ್"</string>
     <string name="select_hours" msgid="6043079511766008245">"ಗಂಟೆಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="select_minutes" msgid="3974345615920336087">"ನಿಮಿಷಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"ದಿನಗಳ ತಿಂಗಳಿನ ಗ್ರಿಡ್"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"ವರ್ಷದ ಪಟ್ಟಿ"</string>
     <string name="select_day" msgid="7774759604701773332">"ತಿಂಗಳು ಮತ್ತು ದಿನವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="select_year" msgid="7952052866994196170">"ವರ್ಷವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> ಅಳಿಸಲಾಗಿದೆ"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"ಕೆಲಸ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"ಈ ಪರದೆಯನ್ನು ಅನ್‌ಪಿನ್ ಮಾಡಲು, ‘ಹಿಂದೆ’ ಮತ್ತು ‘ಸಮಗ್ರ ನೋಟ’ವನ್ನು ಏಕಕಾಲದಲ್ಲಿ ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಒತ್ತಿ ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 463f84d5..cf3a98c 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi 통화"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: 착신전환 안됨"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g><xliff:g id="TIME_DELAY">{2}</xliff:g>초 후"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"앱이 카메라로 사진과 동영상을 찍을 수 있도록 허용합니다. 이 권한을 사용하면 앱이 언제든지 사용자의 확인 없이 카메라를 사용할 수 있습니다."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"카메라를 사용할 때 전송 표시 LED 사용 중지"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"사전 설치된 시스템 애플리케이션에서 카메라 사용 표시 LED를 사용 중지하도록 허용합니다."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"영구적으로 태블릿 사용 안함"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"TV를 영구적으로 사용 중지"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"휴대전화를 영구적으로 사용 중지"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"사용할 지문 템플릿의 추가 및 삭제 메소드를 앱에서 실행하도록 허용합니다."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"지문 하드웨어 사용"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"앱에서 지문 하드웨어를 인증에 사용하도록 허용합니다."</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"지문이 일부만 인식되었습니다. 다시 시도해 주세요."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"지문을 인식할 수 없습니다. 다시 시도해 주세요."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"지문 센서를 깨끗이 닦고 다시 시도하세요."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"손가락을 너무 빨리 움직였습니다. 다시 시도해 주세요."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"손가락을 너무 느리게 움직였습니다. 다시 시도해 주세요."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"공급업체별 획득 오류 메시지 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"인식할 수 없습니다. 다시 시도하세요."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"하드웨어를 사용할 수 없습니다."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"지문을 저장할 수 없습니다. 기존 지문을 삭제하세요."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"지문 인식 시간이 초과되었습니다. 다시 시도하세요."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"지문 인식 시간이 초과되었습니다. 다시 시도하세요."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"공급업체별 오류 메시지"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"동기화 설정 읽기"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"앱이 계정의 동기화 설정을 읽을 수 있도록 허용합니다. 예를 들어, 계정에서 주소록 앱을 동기화할지 여부를 확인할 수 있습니다."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"동기화 사용 및 사용 중지 전환"</string>
@@ -1119,6 +1138,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"앱이 패키지가 설치 가능한지 확인할 수 있도록 허용합니다."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"패키지 인증 연결"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"권한을 가진 프로그램이 패키지 인증을 요청할 수 있도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"직렬 포트에 액세스"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"SerialManager API를 사용하여 권한을 가진 프로그램이 직렬 포트에 액세스할 수 있도록 합니다."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"외부에서 콘텐츠 제공자에 액세스"</string>
@@ -1544,6 +1571,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"\'일\'을 줄입니다."</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"\'연도\'를 늘립니다."</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"\'연도\'를 줄입니다."</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt 키"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"취소"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete 키"</string>
@@ -1794,11 +1825,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"분 원형 슬라이더"</string>
     <string name="select_hours" msgid="6043079511766008245">"시간 선택"</string>
     <string name="select_minutes" msgid="3974345615920336087">"분 선택"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"월별 바둑판식 날짜 표시"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"년"</string>
     <string name="select_day" msgid="7774759604701773332">"월/일 선택"</string>
     <string name="select_year" msgid="7952052866994196170">"연도 선택"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g>이(가) 선택됨"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> 삭제됨"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"업무용 <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"화면 고정을 해제하려면 \'뒤로\'와 \'개요\'를 동시에 길게 터치합니다."</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index fdfc442..a2e671e 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -194,6 +194,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi Чалуу"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <!-- no translation found for cfTemplateNotForwarded (1683685883841272560) -->
     <skip />
     <!-- no translation found for cfTemplateForwarded (1302922117498590521) -->
@@ -749,6 +750,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Колдонмого камера аркылуу видео жана сүрөт тартуу уруксатын берет. Бул уруксат, камераны каалаган убакта, сиздин ырастооңузсуз колдонуу уруксатын берет."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"өткөрүүчүнүн LED индикаторун камера иштеп жатканда өчүрүү"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Камтылган системалык колдонмолорго камеранын LED\'ди колдонуусун өчүрүү мүмкүнчүлүгүн берет."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <!-- no translation found for permlab_brick (2961292205764488304) -->
     <skip />
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"сыналгыны биротоло өчүрүү"</string>
@@ -948,6 +951,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Колдонмого пайдалануу үчүн манжа изинин үлгүлөрүн кошуу жана жок кылуу мүмкүндүгүн берет."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"манжа изинин аппараттык камсыздоосун колдонуу"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Колдонмого аныктыгын текшерүү үчүн манжа изинин аппараттык камсыздоосун пайдалануу мүмкүндүгүн берет"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Манжа изи жарым-жартылай аныкталды. Кайра аракет кылыңыз."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Манжа изи иштелбей койду. Кайра аракет кылыңыз."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Манжа изинин сенсору кирдеп калган. Тазалап, кайра аракет кылыңыз."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Манжа өтө тез жылдырылды. Кайра аракет кылыңыз."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Манжа өтө жай жылдырылды. Кайра аракет кылыңыз."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Жеткирүүчүгө тиешелүү ээ болуу катасы жөнүндө билдирүү 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Иштетилбей жатат. Кайра аракет кылыңыз."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Аппараттык камсыздоо жеткиликтүү эмес."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Манжа изин сактоо мүмкүн эмес. Учурдагы манжа изин алып салыңыз."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Манжа изин күтүү мөөнөтү бүттү. Кайра аракет кылыңыз."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Манжа изин күтүү мөөнөтү бүттү. Кайра аракет кылыңыз."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Жеткирүүчүгө тиешелүү ката жөнүндө билдирүү."</item>
+  </string-array>
     <!-- no translation found for permlab_readSyncSettings (6201810008230503052) -->
     <skip />
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Колдонмого эсеп менен синхрондошуу тууралоолорун окуганга уруксат берет. Мисалы, Кишилер колдонмосу эсеп менен синхрондошкондугун аныктай алат."</string>
@@ -1446,6 +1465,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Колдонмого топтомдун орнотула тургандыгын текшерүү мүмкүнчүлүгүн берет."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"таңгак текшерүүчүгө байлаштыруу"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Кармоочуга топтом текшергичтеринин атынан өтүнүү мүмкүнчүлүгүн берет. Кадимки колдонмолорго эч качан талап кылынбайт."</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"сериялык портторго жетүү"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Ээсине SerialManager API\'ни колдонуп, серия портуна жеткенге уруксат берүү."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"тышкы контент провайдерлерге жетки алуу"</string>
@@ -2040,6 +2067,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Күндү азайтуу"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Жылды жогорулатуу"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Жылды азайтуу"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Айнуу"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Жок кылуу"</string>
@@ -2309,11 +2340,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Мүнөт жебеси"</string>
     <string name="select_hours" msgid="6043079511766008245">"Саатты тандаңыз"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Мүнөттөрдү тандаңыз"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Айдын күндөрү"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Жыл тизмеги"</string>
     <string name="select_day" msgid="7774759604701773332">"Ай жана күндү тандаңыз"</string>
     <string name="select_year" msgid="7952052866994196170">"Жылды тандаңыз"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> тандалды"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> өчүрүлдү"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Жумуш <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Бул экранды бошотуу үчүн Артка жана Көз жүгүртүүнү чогуу басып, кармап туруңуз."</string>
diff --git a/core/res/res/values-land/dimens_material.xml b/core/res/res/values-land/dimens_material.xml
index 379ccf6..202f4a4 100644
--- a/core/res/res/values-land/dimens_material.xml
+++ b/core/res/res/values-land/dimens_material.xml
@@ -48,4 +48,14 @@
     <dimen name="timepicker_text_inset_inner">46dp</dimen>
     <dimen name="timepicker_text_size_normal">14sp</dimen>
     <dimen name="timepicker_text_size_inner">12sp</dimen>
+
+    <!-- Used by Material-style SimpleMonthView -->
+    <dimen name="date_picker_month_height">40dp</dimen>
+    <dimen name="date_picker_day_of_week_height">14dp</dimen>
+    <dimen name="date_picker_day_height">32dp</dimen>
+    <dimen name="date_picker_day_width">46dp</dimen>
+    <dimen name="date_picker_day_selector_radius">16dp</dimen>
+    <dimen name="day_picker_padding_horizontal">18dp</dimen>
+    <dimen name="day_picker_padding_top">0dp</dimen>
+    <dimen name="day_picker_button_margin_top">-8dp</dimen>
 </resources>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index e565c00..01097a0 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"ການ​ໂທ Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ບໍ່ຖືກສົ່ງຕໍ່"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> ຫຼັງຈາກ <xliff:g id="TIME_DELAY">{2}</xliff:g> ວິນາທີ"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"ອະນຸຍາດໃຫ້ແອັບຯຖ່າຍຮູບ ແລະວິດີໂອດ້ວຍກ້ອງຖ່າຍຮູບ. ການອະນຸຍາດນີ້ຈະອານຸຍາດໃຫ້ແອັບຯ ສາມາດໃຊ້ກ້ອງຖ່າຍຮູບໄດ້ຕະຫລອດເວລາ ໂດຍບໍ່ຕ້ອງຖ້າການຢືນຢັນຈາກທ່ານ."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"ປິດໄຟສັນຍານ LED ເມື່ອນຳໃຊ້ກ້ອງ"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"ອະນຸຍາດໃຫ້ແອັບພລິເຄຊັນທີ່ມາກັບໂຕເຄື່ອງ ປິດການນຳໃຊ້ໄຟ LED ໃນກ້ອງຖ່າຍຮູບ."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"ປິດການນຳໃຊ້ແທັບເລັດຖາວອນ"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"ປິດ​ໃຊ້​ງານໂທລະພາບຖາ​ວອນ"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"ປິດການເຮັດວຽກຂອງໂທລະສັບຖາວອນ"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"ອະ​ນຸ​ຍາດ​ໃຫ້​ແອັບ​ເຮັດ​ໃຫ້​ວິ​ທີ​ການ​ຕ່າງໆ​ເພີ່ມ ແລະ​ລຶບ​ແມ່​ແບບ​ລາຍ​ນີ້ວ​ມື​ສຳ​ລັບ​ການ​ໃຊ້."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"ໃຊ້​ຮາດ​ແວ​ລາຍ​ນີ້ວ​ມື"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"ອະ​ນຸ​ຍາດ​ໃຫ້​ແອັບ​ນຳ​ໃຊ້​ຮາດ​ແວ​ລາຍ​ນີ້ວ​ມື​ສຳ​ລັບ​ການ​ຮັບ​ຮອງ"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ກວດ​ພົບ​ລາຍ​ນີ້ວ​ມື​ບາງ​ສ່ວນ​ແລ້ວ. ກະ​ລຸ​ນາ​ລອງ​ໃໝ່​ອີກ."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ບໍ່​ສາ​ມາດ​ດຳ​ເນີນ​ການ​ລາຍ​ນີ້ວ​ມື​ໄດ້. ກະ​ລຸ​ນາ​ລອງ​ໃໝ່​ອີກ."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ເຊັນ​ເຊີ​ລາຍ​ນີ້ວ​ມື​ເປື້ອນ. ກະ​ລຸ​ນາ​ທຳ​ຄວາມ​ສະ​ອາດ ແລະ​ລອງ​ໃໝ່​ອີກ."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"ຍ້າຍ​ນີ້ວ​ມື​ໄປ​ໄວ​ເກີນ​ໄປ. ກະ​ລຸ​ນາ​ລອງ​ໃໝ່​ອີກ."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"ຍ້າຍ​ນີ້ວ​ມື​ໄປ​ຊ້ເກີນ​ໄປ. ກະ​ລຸ​ນາ​ລອງ​ໃໝ່​ອີກ."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"ຂໍ້​ຄວາມ​ການ​ຜິດ​ພາດ​ການ​ໄດ້​ມາ​ສະ​ເພາະ​ຜູ້​ຂາຍ 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"ບໍ່​ສາ​ມາດ​ປະ​ມວນ​ຜົນ​ໄດ້. ລອງ​ໃໝ່​ອີກ."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"ບໍ່​ມີ​ຮາດ​ແວ​ໃຫ້​ຢູ່."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"ບໍ່​ສາ​ມາດ​ເກັບ​ຮັກ​ສາ​ລາຍ​ນີ້ວ​ມື​ໄວ້​ໄດ້. ກະ​ລຸ​ນາ​ເອົາ​ລາຍ​ນີ້ວ​ມື​ທີ່​ມີ​ຢູ່​ອອກ​ໄປ."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"ເວ​ລາ​ລາຍ​ນີ້ວ​ມື​ບໍ່​ເຂົ້າ​ເຖິງ​ໄດ້. ລອງ​ໃໝ່​ອີກ."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"ເວ​ລາ​ລາຍ​ນີ້ວ​ມື​ບໍ່​ເຂົ້າ​ເຖິງ​ໄດ້. ລອງ​ໃໝ່​ອີກ."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"ຂໍ້​ຄວາມ​ການ​ຂັດ​ຂ້ອງ​ສະ​ເພາະ​ຜູ້​ຂາຍ"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ອ່ານການຕັ້ງຄ່າຊິ້ງຂໍ້ມູນ"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ອະນຸຍາດໃຫ້ແອັບຯ ອ່ານການຕັ້ງຄ່າການຊິ້ງຂໍ້ມູນຂອງບັນຊີໄດ້. ຕົວຢ່າງເຊັ່ນ: ມັນຈະສາມາດກວດສອບໄດ້ແອັບຯ People ຖືກຊິ້ງຂໍ້ມູນກັບບັນຊີໃດນຶ່ງແລ້ວຫຼືຍັງ."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ສະລັບການເປີດ ແລະປິດການຊິ້ງຂໍ້ມູນ"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"ອະນຸຍາດໃຫ້ແອັບຯຢືນຢັນວ່າແພັກເກດໃດນຶ່ງ ສາມາດຕິດຕັ້ງໄດ້."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"ເຊື່ອມໂຍງກັບໂຕຢືນຢັນແພັກເກດ"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"ອະນຸຍາດໃຫ້ເຈົ້າຂອງເຮັດການຮ້ອງຂໍໂຕຢືນຢັນແພັກເກັດ. ບໍ່ຈຳເປັນສຳລັບແອັບຯທົ່ວໄປ."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"ຢືນ​ຢັນ​ການ​ກັ່ນ​ຕອງ​ຕາມ​ຕັ້ງ"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"ອະ​ນຸ​ຍາດ​ໃຫ້​ແອັບ​ກວດ​ເບິ່ງວ່າ ການ​ກັ່ນ​ຕອງ​ຕາມ​ຕັ້ງ​ໃຈ​ໄວ້​ໄດ້​ຮັບ​ການ​ຢືນ​ຢັນ ຫຼື​ບໍ່."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"ຜູກ​ມັດ​ກັບ​ໂຕ​ຢືນ​ຢັນ​ການ​ກັ່ນ​ຕອງ​ຕາມ​ຕັ້ງ​ໃຈ"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"ອະນຸຍາດໃຫ້ເຈົ້າຂອງເຮັດການຮ້ອງຂໍໂຕຢືນຢັນການ​ກັ່ນ​ຕອງ​ຕາມ​ຕັ້ງ​ໃຈ. ບໍ່ຈຳເປັນສຳລັບແອັບຯທົ່ວໄປ."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"ເຂົ້າເຖິງພອດຊີຣຽວ"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"ອະນຸຍາດໃຫ້ເຈົ້າຂອງສາມາດເຂົ້າເບິ່ງ serial ports ໂດຍການນຳໃຊ້ SerialManager API."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"ເຂົ້າເຖິງຜູ່ສະໜອງເນື້ອຫາພາຍນອກ"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"ຫຼຸດຈຳນວນມື້"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"ເພີ່ມປີຂຶ້ນ"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"ຫຼຸດຈຳນວນປີ"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"​ເດືອນ​ແລ້ວ"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"ເດືອນ​ໜ້າ"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"ຍົກເລີກ"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"ລຶບ"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"ໂຕໝຸນປັບນາທີ"</string>
     <string name="select_hours" msgid="6043079511766008245">"ເລືອກ​ຊົ່ວ​ໂມງ"</string>
     <string name="select_minutes" msgid="3974345615920336087">"ເລືອກນາ​ທີ"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"ຕາຕາລາງວັນທີເດືອນປີ"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"ລາຍການປີ"</string>
     <string name="select_day" msgid="7774759604701773332">"ເລືອກເດືອນ ແລະ ວັນ"</string>
     <string name="select_year" msgid="7952052866994196170">"ເລືອກ​ປີ"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> ຖືກເລືອກແລ້ວ"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> ຖືກລຶບແລ້ວ"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"​ບ່ອນ​ເຮັດ​ວຽກ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"ເພື່ອ​ຖອດ​ການ​ປັກ​ໝຸດ​ໜ້າ​ຈໍ​ນີ້, ສຳ​ຜັດປຸ່ມ ​ກັບ​ຄືນ ແລະ ພາບ​ຮວມ ຄ້າງ​ໄວ້​ພ້ອມ​ກັນ."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 099593c..d387c910 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -128,6 +128,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"„Wi-Fi“ skambinimas"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neperadresuota"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po <xliff:g id="TIME_DELAY">{2}</xliff:g> sek."</string>
@@ -586,6 +587,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Leidžiama programai fotografuoti ir filmuoti kamera. Šis leidimas suteikia teisę programai naudoti kamerą bet kada be jūsų patvirtinimo."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"neleisti perduoti LED indikatoriaus, kai naudojamas fotoaparatas"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Leidžiama iš anksto įdiegtai sistemos programai išjungti fotoaparato naudojimo indikatoriaus LED."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"visam laikui neleisti planšetinio kompiuterio"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"visam laikui išjungti TV"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"visam laikui išjungti telefoną"</string>
@@ -748,6 +751,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Leidžiama programai aktyvinti metodus, norint pridėti ir ištrinti naudojamus kontrolinių kodų šablonus."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"naudoti kontrolinio kodo aparatinę įrangą"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Leidžiama programai naudoti kontrolinio kodo aparatinę įrangą tapatybei nustatyti"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Aptiktas dalinis kontrolinis kodas. Bandykite dar kartą."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nepavyko apdoroti kontrolinio kodo. Bandykite dar kartą."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Kontrolinio kodo jutiklis purvinas. Nuvalykite ir bandykite dar kartą."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Per greitai judinate pirštą. Bandykite dar kartą."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Per lėtai judinate pirštą. Bandykite dar kartą."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Konkretaus paslaugų teikėjo įgijimo klaidos pranešimas (0)"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Nepavyko apdoroti. Bandykite dar kartą."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Aparatinė įranga negalima."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Negalima išsaugoti kontrolinio kodo. Pašalinkite esamą kontrolinį kodą."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Baigėsi kontrolinio kodo nustatymo skirtasis laikas. Bandykite dar kartą."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Baigėsi kontrolinio kodo nustatymo skirtasis laikas. Bandykite dar kartą."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Konkretaus paslaugų teikėjo klaidos pranešimas"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"skaityti sinchronizavimo nustatymus"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Leidžiama programai skaityti ir sinchronizuoti paskyros nustatymus. Pvz., taip gali būti nustatoma, ar su paskyra sinchronizuota Žmonių programa."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"įjungti arba išjungti sinchronizavimą"</string>
@@ -1121,6 +1140,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Leidžiama programai patikrinti, ar paketą galima įdiegti."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"susaistyti su paketo tikrinimo programa"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Savininkui leidžiama teikti užklausas patikrinti paketą. Įprastoms programoms to neturėtų prireikti."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"patvirtinti tikslinį filtrą"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Leidžiama programai patikrinti, ar tikslinis filtras patvirtintas."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"priskirti tikslinio filtro tvirtin. pr."</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Savininkui leidžiama teikti užklausas patvirtinti tikslinį filtrą. Įprastoms programoms to neturėtų prireikti."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"pasiekti nuosekliuosius prievadus"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Leidžiama savininkui pasiekti nuosekliuosius prievadus naudojant „SerialManager“ API."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"pasiekti turinio teikėjus iš išorės"</string>
@@ -1560,6 +1583,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Sumažinti dienų skaičių"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Padidinti metų skaičių"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Sumažinti metų skaičių"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Ankstesnis mėnuo"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Kitas mėnuo"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Atšaukti"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Ištrinti"</string>
@@ -1812,11 +1837,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Apskritas minučių slankiklis"</string>
     <string name="select_hours" msgid="6043079511766008245">"Pasirinkite valandas"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Pasirinkite minutes"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Mėnesio dienų tinklelis"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Metų sąrašas"</string>
     <string name="select_day" msgid="7774759604701773332">"Pasirinkite mėnesį ir dieną"</string>
     <string name="select_year" msgid="7952052866994196170">"Pasirinkite metus"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"Pasirinkta: <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"Ištrinta: <xliff:g id="KEY">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Darbo <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Jei norite atsegti šį ekraną, vienu metu palieskite ir palaikykite „Atgal“ ir „Apžvalga“."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 9ab2772..c64ea9e 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -127,6 +127,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi zvani"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: nav pāradresēts"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> pēc <xliff:g id="TIME_DELAY">{2}</xliff:g> sekundes(-ēm)"</string>
@@ -585,6 +586,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Ļauj lietotnei uzņemt attēlus un videoklipus ar kameru. Ar šo atļauju lietotne var jebkurā brīdī izmantot kameru bez jūsu apstiprinājuma."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"Atspējot pārraidīšanas LED indikatoru, kad kamera tiek izmantota"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Ļauj iepriekš instalētai sistēmas lietojumprogrammai atspējot LED indikatoru, izmantojot kameru."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"neatgriezeniski atspējot planšetdatoru"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"neatgriezeniski atspējot televizora darbību"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"neatgriezeniski atspējot tālruni"</string>
@@ -747,6 +750,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Atļauj lietotnei izsaukt metodes izmantojamo pirkstu nospiedumu veidņu pievienošanai un dzēšanai."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"lietot pirkstu nospiedumu aparatūru"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Atļauj lietotnei izmantot pirkstu nospiedumu aparatūru autentificēšanai."</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Noteikts daļējs pirksta nospiedums. Lūdzu, mēģiniet vēlreiz."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nevarēja apstrādāt pirksta nospiedumu. Lūdzu, mēģiniet vēlreiz."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Pirkstu nospiedumu sensors ir netīrs. Lūdzu, notīriet to un mēģiniet vēlreiz."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Pārāk ātra pirksta kustība. Lūdzu, mēģiniet vēlreiz."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Pārāk lēna pirksta kustība. Lūdzu, mēģiniet vēlreiz."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Piegādātāja noteikts iegūšanas kļūdas ziņojums 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Nevar apstrādāt pirksta nospiedumu. Mēģiniet vēlreiz."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Aparatūra nav pieejama."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Pirkstu nospiedumu nevar saglabāt. Lūdzu, noņemiet esošu pirksta nospiedumu."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Pirkstu nospiedumu nolasīšanas aparatūras noildze. Mēģiniet vēlreiz."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Pirkstu nospiedumu nolasīšanas aparatūras noildze. Mēģiniet vēlreiz."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Piegādātāja noteikts kļūdas ziņojums"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"lasīt sinhronizācijas iestatījumus"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Ļauj lietotnei lasīt konta sinhronizācijas iestatījumus. Piemēram, šādi var noteikt, vai lietotne Personas ir sinhronizēta ar kontu."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ieslēgt un izslēgt sinhronizāciju"</string>
@@ -1120,6 +1139,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Ļauj lietotnei verificēt, vai pakotne ir instalējama."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"saistīšana ar pakotnes verificētāju"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Ļauj īpašniekam sūtīt pakotņu verificētāju pieprasījumus. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"piekļuve seriālajiem portiem"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Ļauj īpašniekam piekļūt seriālajiem portiem, izmantojot SerialManager API."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"ārēji piekļūt satura nodrošinātājiem"</string>
@@ -1552,6 +1579,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Norādīt agrāku dienu"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Norādīt vēlāku gadu"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Norādīt agrāku gadu"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alternēšanas taustiņš"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Atcelt"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Dzēšanas taustiņš"</string>
@@ -1803,11 +1834,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Minūšu apļveida slīdnis"</string>
     <string name="select_hours" msgid="6043079511766008245">"Atlasiet stundas."</string>
     <string name="select_minutes" msgid="3974345615920336087">"Atlasiet minūtes."</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Režģis ar mēneša dienām"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Gadu saraksts"</string>
     <string name="select_day" msgid="7774759604701773332">"Atlasiet mēnesi un dienu."</string>
     <string name="select_year" msgid="7952052866994196170">"Atlasiet gadu."</string>
-    <string name="item_is_selected" msgid="949687401682476608">"Atlasīts: <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> tika dzēsts."</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Darbā: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Lai atspraustu šo ekrānu, vienlaicīgi pieskarieties pogām “Atpakaļ” un “Pārskats” un turiet tās."</string>
diff --git a/core/res/res/values-mcc310-mnc260-af/strings.xml b/core/res/res/values-mcc310-mnc260-af/strings.xml
index e33c27b..2d4b749 100644
--- a/core/res/res/values-mcc310-mnc260-af/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-af/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi-oproepe is nie beskikbaar nie. Kontak jou diensverskaffer om Wi-Fi-oproepe te aktiveer."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi-oproep"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-am/strings.xml b/core/res/res/values-mcc310-mnc260-am/strings.xml
index 24a88097..126631a 100644
--- a/core/res/res/values-mcc310-mnc260-am/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-am/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi ጥሪ ማድረጊያ አይገኝም። የ Wi-Fi ጥሪ ማድረጊያን ለማንቃት የእርስዎን አገልግሎት አቅራቢ ያነጋግሩ።"</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"የ%s Wi-Fi ጥሪ"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-ar/strings.xml b/core/res/res/values-mcc310-mnc260-ar/strings.xml
index 8c2e6f6..1175eec 100644
--- a/core/res/res/values-mcc310-mnc260-ar/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ar/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"‏الاتصال عبر Wi-Fi ليس متوفرًا. اتصل بمشغل شبكة الجوّال لتمكين الاتصال عبر Wi-Fi."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"‏%s جارٍ الاتصال عبر Wi-Fi"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-bg/strings.xml b/core/res/res/values-mcc310-mnc260-bg/strings.xml
index 86ad9ab..be1d0d3 100644
--- a/core/res/res/values-mcc310-mnc260-bg/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-bg/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Обажданията през Wi-Fi не са налице. Свържете се с оператора си, за да ги активирате."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s – обаждания през Wi-Fi"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-bn-rBD/strings.xml b/core/res/res/values-mcc310-mnc260-bn-rBD/strings.xml
index 97df2db..0135108 100644
--- a/core/res/res/values-mcc310-mnc260-bn-rBD/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-bn-rBD/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi কলিং উপলব্ধ নেই৷ Wi-Fi কলিং সক্ষম করতে আপনার পরিষেবা প্রদানকারীর সাথে যোগাযোগ করুন৷"</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi কলিং"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-ca/strings.xml b/core/res/res/values-mcc310-mnc260-ca/strings.xml
index e7d2158..5a10585 100644
--- a/core/res/res/values-mcc310-mnc260-ca/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ca/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Les trucades per Wi-Fi no estan disponibles. Contacta amb l\'operador de telefonia mòbil per activar-les."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Trucada de Wi-Fi de: %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-cs/strings.xml b/core/res/res/values-mcc310-mnc260-cs/strings.xml
index 987284d..8f1d5dc 100644
--- a/core/res/res/values-mcc310-mnc260-cs/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-cs/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Volání přes Wi-Fi není k dispozici. Kontaktujte operátora, aby volání přes Wi-Fi aktivoval."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Volání přes Wi-Fi: %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-da/strings.xml b/core/res/res/values-mcc310-mnc260-da/strings.xml
index 20532b2..4e6c2d0 100644
--- a/core/res/res/values-mcc310-mnc260-da/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-da/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Opkald via Wi-Fi er ikke muligt. Kontakt dit mobilselskab for at aktivere Opkald via Wi-Fi."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi-opkald"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-de/strings.xml b/core/res/res/values-mcc310-mnc260-de/strings.xml
index e69d062..f4838eb 100644
--- a/core/res/res/values-mcc310-mnc260-de/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-de/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"WLAN-Anrufe sind nicht möglich. Bitte kontaktieren Sie Ihren Mobilfunkanbieter bezüglich der Aktivierung der WLAN-Telefonie."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s WLAN-Anrufe"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-el/strings.xml b/core/res/res/values-mcc310-mnc260-el/strings.xml
index cd5d615..b3d3aed 100644
--- a/core/res/res/values-mcc310-mnc260-el/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-el/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Η κλήση Wi-Fi δεν είναι διαθέσιμη. Επικοινωνήστε με την εταιρεία κινητής τηλεφωνίας σας για να ενεργοποιήσετε την κλήση Wi-Fi."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Κλήση Wi-Fi"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-en-rAU/strings.xml b/core/res/res/values-mcc310-mnc260-en-rAU/strings.xml
index 3f764c0..8a9da8f 100644
--- a/core/res/res/values-mcc310-mnc260-en-rAU/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-en-rAU/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi Calling isn\'t available. Contact your operator to enable Wi-Fi Calling."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi Calling"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-en-rGB/strings.xml b/core/res/res/values-mcc310-mnc260-en-rGB/strings.xml
index 3f764c0..8a9da8f 100644
--- a/core/res/res/values-mcc310-mnc260-en-rGB/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-en-rGB/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi Calling isn\'t available. Contact your operator to enable Wi-Fi Calling."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi Calling"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-en-rIN/strings.xml b/core/res/res/values-mcc310-mnc260-en-rIN/strings.xml
index 3f764c0..8a9da8f 100644
--- a/core/res/res/values-mcc310-mnc260-en-rIN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-en-rIN/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi Calling isn\'t available. Contact your operator to enable Wi-Fi Calling."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi Calling"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-es-rUS/strings.xml b/core/res/res/values-mcc310-mnc260-es-rUS/strings.xml
index 3e13ebb..3f37a6d 100644
--- a/core/res/res/values-mcc310-mnc260-es-rUS/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-es-rUS/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Las llamadas con Wi-Fi no están disponibles. Comunícate con el proveedor para habilitar la función."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Llamada por Wi-Fi de %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-es/strings.xml b/core/res/res/values-mcc310-mnc260-es/strings.xml
index 9dea2f1..24017c2 100644
--- a/core/res/res/values-mcc310-mnc260-es/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-es/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Las llamadas Wi-Fi no están disponibles. Ponte en contacto con tu operador para habilitarlas."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Llamada Wi-Fi de %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-et-rEE/strings.xml b/core/res/res/values-mcc310-mnc260-et-rEE/strings.xml
index 465507a..5d3d277 100644
--- a/core/res/res/values-mcc310-mnc260-et-rEE/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-et-rEE/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"WiFi-kõned pole saadaval. WiFi-kõnede lubamiseks võtke ühendust operaatoriga."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s WiFi kaudu helistamine"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-eu-rES/strings.xml b/core/res/res/values-mcc310-mnc260-eu-rES/strings.xml
index 23194ff..3bcedf7 100644
--- a/core/res/res/values-mcc310-mnc260-eu-rES/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-eu-rES/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi bidezko deiak ez daude erabilgarri. Gaitzeko, jarri operadorearekin harremanetan."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi bidezko deiak"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-fa/strings.xml b/core/res/res/values-mcc310-mnc260-fa/strings.xml
index 083a613..198d9e2 100644
--- a/core/res/res/values-mcc310-mnc260-fa/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-fa/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"‏تماس از طریق Wi-Fi امکان‌پذیر نیست. برای فعال کردن تماس Wi-Fi، با شرکت مخابراتی‌تان تماس بگیرید."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"‏تماس ‪%s Wi-Fi"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-fi/strings.xml b/core/res/res/values-mcc310-mnc260-fi/strings.xml
index 43efe46..a6c0986f 100644
--- a/core/res/res/values-mcc310-mnc260-fi/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-fi/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi-puhelut eivät ole käytettävissä. Pyydä operaattoriasi ottamaan Wi-Fi-puhelut käyttöön."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Wi-Fi-puhelut: %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-fr-rCA/strings.xml b/core/res/res/values-mcc310-mnc260-fr-rCA/strings.xml
index 2d63c89..92f5e79 100644
--- a/core/res/res/values-mcc310-mnc260-fr-rCA/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-fr-rCA/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"La fonction d\'appel par Wi-Fi n\'est pas disponible. Communiquez avec votre fournisseur de services pour activer les appels par Wi-Fi."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Appels Wi-Fi %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-fr/strings.xml b/core/res/res/values-mcc310-mnc260-fr/strings.xml
index 358bec8..b4fe869 100644
--- a/core/res/res/values-mcc310-mnc260-fr/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-fr/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Les appels Wi-Fi ne sont pas disponibles. Contactez votre opérateur pour les activer."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Appels Wi-Fi %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-gl-rES/strings.xml b/core/res/res/values-mcc310-mnc260-gl-rES/strings.xml
index 32ef3d3..11080f3 100644
--- a/core/res/res/values-mcc310-mnc260-gl-rES/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-gl-rES/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"As chamadas por wifi non están dispoñible. Contacta co teu operador para activar as chamadas por wifi."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Chamadas wifi de %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-hi/strings.xml b/core/res/res/values-mcc310-mnc260-hi/strings.xml
index 93193fd..fb65db3 100644
--- a/core/res/res/values-mcc310-mnc260-hi/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-hi/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi कॉलिंग उपलब्‍ध नहीं है. वाई-फ़ाई कॉलिंग सक्षम करने के लिए अपने वाहक से संपर्क करें."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s वाई-फ़ाई कॉलिंग"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-hr/strings.xml b/core/res/res/values-mcc310-mnc260-hr/strings.xml
index 30e22f1..7442481 100644
--- a/core/res/res/values-mcc310-mnc260-hr/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-hr/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi pozivi nisu dostupni. Obratite se mobilnom operateru radi omogućivanja Wi-Fi poziva."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi pozivanje"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-hu/strings.xml b/core/res/res/values-mcc310-mnc260-hu/strings.xml
index 44b0a9e..c8241e9 100644
--- a/core/res/res/values-mcc310-mnc260-hu/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-hu/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"A Wi-Fi-hívás nem érhető el. Engedélyezéséhez forduljon szolgáltatójához."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi-hívás"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-hy-rAM/strings.xml b/core/res/res/values-mcc310-mnc260-hy-rAM/strings.xml
index aa63f70..9296ab4 100644
--- a/core/res/res/values-mcc310-mnc260-hy-rAM/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-hy-rAM/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi-ի միջոցով զանգերն անհասանելի են: Դրանք միացնելու համար դիմեք ձեր օպերատորին:"</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi զանգեր"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-in/strings.xml b/core/res/res/values-mcc310-mnc260-in/strings.xml
index 669d384..d697236 100644
--- a/core/res/res/values-mcc310-mnc260-in/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-in/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Panggilan Wi-Fi tidak tersedia. Hubungi operator untuk mengaktifkan Panggilan Wi-Fi."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Panggilan Wi-Fi"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-is-rIS/strings.xml b/core/res/res/values-mcc310-mnc260-is-rIS/strings.xml
index 2549756..1ba95b3 100644
--- a/core/res/res/values-mcc310-mnc260-is-rIS/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-is-rIS/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi símtöl eru ekki í boði. Hafðu samband við símafyrirtækið þitt til að virkja Wi-Fi símtöl."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi símtöl"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-it/strings.xml b/core/res/res/values-mcc310-mnc260-it/strings.xml
index 7f58424..f84e8f7 100644
--- a/core/res/res/values-mcc310-mnc260-it/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-it/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Le chiamate Wi-Fi non sono disponibili. Contatta il tuo operatore per attivarle."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Chiamata Wi-Fi %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-iw/strings.xml b/core/res/res/values-mcc310-mnc260-iw/strings.xml
index 161c868..b882387 100644
--- a/core/res/res/values-mcc310-mnc260-iw/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-iw/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"‏שיחות ב-Wi-Fi אינן זמינות. צור קשר עם הספק שלך כדי להפעיל שיחות ב-Wi-Fi."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"‏שיחות Wi-Fi של %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-ja/strings.xml b/core/res/res/values-mcc310-mnc260-ja/strings.xml
index 237b606..9933a50 100644
--- a/core/res/res/values-mcc310-mnc260-ja/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ja/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi通話が利用できません。携帯通信会社に連絡してWi-Fi通話を有効にしてください。"</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Wi-Fi通話(%s)"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-ka-rGE/strings.xml b/core/res/res/values-mcc310-mnc260-ka-rGE/strings.xml
index 53488dd..d1eadac 100644
--- a/core/res/res/values-mcc310-mnc260-ka-rGE/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ka-rGE/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"დარეკვა Wi-Fi-ს მეშვეობით მიუწვდომელია. დაუკავშირდით თქვენს ოპერატორს Wi-Fi-ს მეშვეობით დარეკვის ჩასართავად."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s დარეკვა Wi-Fi-ს მეშვეობით"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-kk-rKZ/strings.xml b/core/res/res/values-mcc310-mnc260-kk-rKZ/strings.xml
index 10ce253..b135e1d 100644
--- a/core/res/res/values-mcc310-mnc260-kk-rKZ/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-kk-rKZ/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi қоңырауы қол жетімді емес. Wi-Fi қоңырауы қосу үшін жабдықтаушыға хабарласыңыз."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi арқылы қоңырау шалу"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-km-rKH/strings.xml b/core/res/res/values-mcc310-mnc260-km-rKH/strings.xml
index 7f62ff6..b3e86df8 100644
--- a/core/res/res/values-mcc310-mnc260-km-rKH/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-km-rKH/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"ការហៅតាម Wi-Fi មិនមាននោះទេ។ សូមទាក់ទងទៅអ្នកផ្តល់សេវាកម្មទូរស័ព្ទរបស់អ្នកដើម្បីបើកដំណើរការហៅតាម Wi-Fi។"</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"ការហៅតាមរយៈ Wi-Fi %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-kn-rIN/strings.xml b/core/res/res/values-mcc310-mnc260-kn-rIN/strings.xml
index 4977708..6a6378c 100644
--- a/core/res/res/values-mcc310-mnc260-kn-rIN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-kn-rIN/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"ವೈ-ಫೈ ಕರೆ ಮಾಡುವಿಕೆ ಲಭ್ಯವಿಲ್ಲ. ವೈ-ಫೈ ಕರೆ ಮಾಡುವಿಕೆಯನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲು ನಿಮ್ಮ ವಾಹಕವನ್ನು ಸಂಪರ್ಕಿಸಿ."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s ವೈ-ಫೈ ಕರೆ ಮಾಡುವಿಕೆ"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-ko/strings.xml b/core/res/res/values-mcc310-mnc260-ko/strings.xml
index 0dcc45a..9a4d89c 100644
--- a/core/res/res/values-mcc310-mnc260-ko/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ko/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi 통화를 사용할 수 없습니다. Wi-Fi 통화를 사용하려면 이동통신사에 문의하세요."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi 통화"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-ky-rKG/strings.xml b/core/res/res/values-mcc310-mnc260-ky-rKG/strings.xml
index b8f1e5a..290ce32 100644
--- a/core/res/res/values-mcc310-mnc260-ky-rKG/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ky-rKG/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi Чалуу жеткиликтүү эмес. Wi-Fi Чалууну иштетүү үчүн операторуңузга кайрылыңыз."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi Чалуу"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-lo-rLA/strings.xml b/core/res/res/values-mcc310-mnc260-lo-rLA/strings.xml
index 8f826e4..3b96835 100644
--- a/core/res/res/values-mcc310-mnc260-lo-rLA/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-lo-rLA/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"ການໂທ Wi-Fi ບໍ່ພ້ອມໃຊ້ງານ. ໃຫ້ຕິດຕໍ່ຫາຜູ້ໃຫ້ບໍລິການຂອງທ່ານເພື່ອເປີດໃຊ້ການໂທ Wi-Fi."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"ການ​ໂທ %s Wi-Fi"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-lt/strings.xml b/core/res/res/values-mcc310-mnc260-lt/strings.xml
index b4a0dbb..09d151b 100644
--- a/core/res/res/values-mcc310-mnc260-lt/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-lt/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"„Wi-Fi“ skambinimo funkcija nepasiekiama. Susisiekite su operatoriumi, kad įgalintumėte „Wi-Fi“ skambinimo funkciją."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"„%s“ „Wi-Fi“ skambinimas"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-lv/strings.xml b/core/res/res/values-mcc310-mnc260-lv/strings.xml
index 19fafeb..2da9a9d 100644
--- a/core/res/res/values-mcc310-mnc260-lv/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-lv/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi zvani nav pieejami. Lai iespējotu Wi-Fi zvanus, sazinieties ar savu mobilo sakaru operatoru."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi zvani"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-mk-rMK/strings.xml b/core/res/res/values-mcc310-mnc260-mk-rMK/strings.xml
index a4125b4..ddf0af6 100644
--- a/core/res/res/values-mcc310-mnc260-mk-rMK/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-mk-rMK/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Повикувањето преку Wi-Fi не е достапно. Контактирајте го операторот за да овозможите Повикување преку Wi-Fi."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Повикување преку Wi-Fi"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-ml-rIN/strings.xml b/core/res/res/values-mcc310-mnc260-ml-rIN/strings.xml
index 732e18b..2565306 100644
--- a/core/res/res/values-mcc310-mnc260-ml-rIN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ml-rIN/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi കോളിംഗ് ലഭ്യമല്ല. Wi-Fi കോളിംഗ് പ്രവർത്തനക്ഷമമാക്കാൻ നിങ്ങളുടെ കാരിയറെ ബന്ധപ്പെടുക."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi കോളിംഗ്"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-mn-rMN/strings.xml b/core/res/res/values-mcc310-mnc260-mn-rMN/strings.xml
index 8b311ee..c52dd31 100644
--- a/core/res/res/values-mcc310-mnc260-mn-rMN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-mn-rMN/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi Calling одоогоор боломжгүй байна. Wi-Fi Calling  идэвхжүүлэхийн тулд оператортойгоо холбогдоно уу."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi Дуудлага"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-mr-rIN/strings.xml b/core/res/res/values-mcc310-mnc260-mr-rIN/strings.xml
index e191a68..86aa1b0 100644
--- a/core/res/res/values-mcc310-mnc260-mr-rIN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-mr-rIN/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"वाय-फाय कॉलिंग उपलब्‍ध नाही. वाय-फाय कॉलिंग सक्षम करण्‍यासाठी आपल्‍या वाहकाशी संपर्क साधा."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s वाय-फाय कॉलिंग"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-ms-rMY/strings.xml b/core/res/res/values-mcc310-mnc260-ms-rMY/strings.xml
index aa13114..94e9705 100644
--- a/core/res/res/values-mcc310-mnc260-ms-rMY/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ms-rMY/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Panggilan Wi-Fi tidak tersedia. Hubungi pembawa anda untuk mendayakan Panggilan Wi-Fi."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Panggilan Wi-Fi"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-my-rMM/strings.xml b/core/res/res/values-mcc310-mnc260-my-rMM/strings.xml
index 90e99af..bf624ce 100644
--- a/core/res/res/values-mcc310-mnc260-my-rMM/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-my-rMM/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"ဝိုင်ဖိုင်ခေါ်ဆိုမှု မရပါ။ ဝိုင်ဖိုင် ခေါ်နိုင်ရန် သင်၏ ဖုန်းဝန်ဆောင်မှုပေးသူအား ဆက်သွယ်ပါ။"</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s ဝိုင်ဖိုင် ခေါ်ဆိုမှု"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-nb/strings.xml b/core/res/res/values-mcc310-mnc260-nb/strings.xml
index 7ece702..c4e9f6e 100644
--- a/core/res/res/values-mcc310-mnc260-nb/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-nb/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi-anrop er ikke tilgjengelig. Ta kontakt med operatøren din for å slå på Wi-Fi-anrop."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi-anrop"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-ne-rNP/strings.xml b/core/res/res/values-mcc310-mnc260-ne-rNP/strings.xml
index 72f94f3..2ba19e5 100644
--- a/core/res/res/values-mcc310-mnc260-ne-rNP/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ne-rNP/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi कलिङ उपलब्ध छैन। Wi-Fi कलिङ सक्षम पार्न तपाईँको वाहकलाई सम्पर्क गर्नुहोस्।"</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi कलिङ"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-nl/strings.xml b/core/res/res/values-mcc310-mnc260-nl/strings.xml
index 1fbe404..20b7aa4 100644
--- a/core/res/res/values-mcc310-mnc260-nl/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-nl/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Bellen via wifi is niet beschikbaar. Neem contact op met uw provider om bellen via wifi in te schakelen."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Bellen via wifi van %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-pl/strings.xml b/core/res/res/values-mcc310-mnc260-pl/strings.xml
index 41bb1e6..ef3e2e3 100644
--- a/core/res/res/values-mcc310-mnc260-pl/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-pl/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Połączenia przez Wi-Fi są niedostępne. Skontaktuj się z operatorem sieci, by je włączyć."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Połączenia przez Wi-Fi (%s)"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-pt-rPT/strings.xml b/core/res/res/values-mcc310-mnc260-pt-rPT/strings.xml
index bb22d9efc7..8ea4cb4c 100644
--- a/core/res/res/values-mcc310-mnc260-pt-rPT/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-pt-rPT/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"As chamadas Wi-Fi não estão disponíveis. Contacte o seu operador para as ativar."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Chamadas por Wi-Fi da %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-pt/strings.xml b/core/res/res/values-mcc310-mnc260-pt/strings.xml
index d422e83..e01fc4a 100644
--- a/core/res/res/values-mcc310-mnc260-pt/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-pt/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"A chamada por Wi-Fi não está disponível. Entre em contato com sua operadora para ativá-la."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s chamada Wi-Fi"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-ro/strings.xml b/core/res/res/values-mcc310-mnc260-ro/strings.xml
index 422280f..0a78caa 100644
--- a/core/res/res/values-mcc310-mnc260-ro/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ro/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Apelarea prin Wi-Fi nu este disponibilă. Contactați-vă operatorul pentru a activa Apelarea prin Wi-Fi."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Apelare prin Wi-Fi %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-ru/strings.xml b/core/res/res/values-mcc310-mnc260-ru/strings.xml
index eeceb7c..3266bdb 100644
--- a/core/res/res/values-mcc310-mnc260-ru/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ru/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Звонки по Wi-Fi недоступны. Чтобы включить эту функцию, свяжитесь со своим оператором."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Звонки по Wi-Fi (%s)"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-si-rLK/strings.xml b/core/res/res/values-mcc310-mnc260-si-rLK/strings.xml
index dd4da30..d1e5c27 100644
--- a/core/res/res/values-mcc310-mnc260-si-rLK/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-si-rLK/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi ඇමතීම ලබාගත නොහැක. Wi-Fi ඇමතීම ක්‍රියාත්මක කිරීමට ඔබගේ වාහකයා සම්බන්ධ කරගන්න."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi අමතමින්"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-sk/strings.xml b/core/res/res/values-mcc310-mnc260-sk/strings.xml
index 527388f..2adccc8 100644
--- a/core/res/res/values-mcc310-mnc260-sk/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-sk/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Volanie cez Wi-Fi nie je k dispozícii. Kontaktujte svojho operátora a požiadajte ho o povolenie volania cez Wi-Fi."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Volanie siete Wi-Fi %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-sl/strings.xml b/core/res/res/values-mcc310-mnc260-sl/strings.xml
index 2dc873b..e4bedcf 100644
--- a/core/res/res/values-mcc310-mnc260-sl/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-sl/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Klicanje prek Wi-Fi-ja ni na voljo. Obrnite se na operaterja, da vam omogoči klicanje prek Wi-Fi-ja."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Klicanje prek Wi-Fi-ja (%s)"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-sr/strings.xml b/core/res/res/values-mcc310-mnc260-sr/strings.xml
index 76f8aef..6b00845 100644
--- a/core/res/res/values-mcc310-mnc260-sr/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-sr/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Позивање преко Wi-Fi-ја није доступно. Контактирајте мобилног оператера да бисте омогућили позивање преко Wi-Fi-ја."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Wi-Fi позивање преко оператера %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-sv/strings.xml b/core/res/res/values-mcc310-mnc260-sv/strings.xml
index 8e0d159..4984914 100644
--- a/core/res/res/values-mcc310-mnc260-sv/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-sv/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Det går inte att ringa Wi-Fi-samtal. Kontakta operatören om du vill aktivera Wi-Fi-samtal."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi-samtal"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-sw/strings.xml b/core/res/res/values-mcc310-mnc260-sw/strings.xml
index ff9bf3f..eab7344 100644
--- a/core/res/res/values-mcc310-mnc260-sw/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-sw/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Huduma ya Upigaji simu kwa Wi-Fi haipatikani. Wasiliana na mtoa huduma wako ili awashe Upigaji simu kwa Wi-Fi."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Upigaji Simu kwa Wi-Fi"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-ta-rIN/strings.xml b/core/res/res/values-mcc310-mnc260-ta-rIN/strings.xml
index d8b3130..e7da4b6 100644
--- a/core/res/res/values-mcc310-mnc260-ta-rIN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ta-rIN/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"வைஃபை அழைப்பு கிடைக்கவில்லை. வைஃபை அழைப்பை இயக்க, மொபைல் நிறுவனத்தைத் தொடர்புகொள்ளவும்."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s வைஃபை அழைப்பு"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-te-rIN/strings.xml b/core/res/res/values-mcc310-mnc260-te-rIN/strings.xml
index ffbab18..98de7b7 100644
--- a/core/res/res/values-mcc310-mnc260-te-rIN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-te-rIN/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi కాలింగ్ అందుబాటులో లేదు. Wi-Fi కాలింగ్‌ను ప్రారంభించడానికి మీ క్యారియర్‌ను సంప్రదించండి."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi కాలింగ్"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-th/strings.xml b/core/res/res/values-mcc310-mnc260-th/strings.xml
index 52dc04f..eb64b8f 100644
--- a/core/res/res/values-mcc310-mnc260-th/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-th/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"การโทรผ่าน Wi-Fi ไม่พร้อมใช้งาน โปรดติดต่อผู้ให้บริการของคุณเพื่อเปิดใช้การโทรผ่าน Wi-Fi"</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"กำลังเรียก Wi-Fi ของ %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-tl/strings.xml b/core/res/res/values-mcc310-mnc260-tl/strings.xml
index 52d97a87..1ac32d0 100644
--- a/core/res/res/values-mcc310-mnc260-tl/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-tl/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Hindi available ang Pagtawag sa pamamagitan ng Wi-Fi. Makipag-ugnayan sa iyong carrier upang i-enable ang Pagtawag sa pamamagitan ng Wi-Fi."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Pagtawag sa Pamamagitan ng Wi-Fi ng %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-tr/strings.xml b/core/res/res/values-mcc310-mnc260-tr/strings.xml
index b28702b82..5a479f7 100644
--- a/core/res/res/values-mcc310-mnc260-tr/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-tr/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Kablosuz Çağrı kullanılamıyor. Kablosuz Çağrı özelliğini etkinleştirmek için operatörünüzle iletişim kurun."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Kablosuz Çağrı"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-uk/strings.xml b/core/res/res/values-mcc310-mnc260-uk/strings.xml
index 243c7b0..f878528 100644
--- a/core/res/res/values-mcc310-mnc260-uk/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-uk/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Ви не можете телефонувати через Wi-Fi. Щоб увімкнути цю функцію, зв’яжіться з оператором."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Дзвінок через Wi-Fi від оператора %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-ur-rPK/strings.xml b/core/res/res/values-mcc310-mnc260-ur-rPK/strings.xml
index 77ee52b..a9e56d2 100644
--- a/core/res/res/values-mcc310-mnc260-ur-rPK/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-ur-rPK/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"‏Wi-Fi کالنگ دستیاب نہیں ہے۔ Wi-Fi کالنگ فعال کرنے کیلئے اپنے کیریئر سے رابطہ کریں۔"</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"‏‎%s Wi-Fi کالنگ"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-uz-rUZ/strings.xml b/core/res/res/values-mcc310-mnc260-uz-rUZ/strings.xml
index 1cd4795..26c6cd8 100644
--- a/core/res/res/values-mcc310-mnc260-uz-rUZ/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-uz-rUZ/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Wi-Fi qo‘ng‘iroq mavjud emas. Wi-Fi qo‘ng‘iroqni yoqish uchun tarmoq operatori bilan bog‘laning."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi qo‘ng‘iroqlar"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-vi/strings.xml b/core/res/res/values-mcc310-mnc260-vi/strings.xml
index d47da6d..242ec73 100644
--- a/core/res/res/values-mcc310-mnc260-vi/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-vi/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Tính năng Gọi qua Wi-Fi không khả dụng. Hãy liên hệ với nhà cung cấp dịch vụ để bật tính năng Gọi qua Wi-Fi."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"Gọi điện qua Wi-Fi %s"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-zh-rCN/strings.xml b/core/res/res/values-mcc310-mnc260-zh-rCN/strings.xml
index 561484e..a359441 100644
--- a/core/res/res/values-mcc310-mnc260-zh-rCN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-zh-rCN/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"WLAN 通话功能不可用。请与您的运营商联系,以便启用 WLAN 通话功能。"</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s WLAN 通话功能"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-zh-rHK/strings.xml b/core/res/res/values-mcc310-mnc260-zh-rHK/strings.xml
index d93be6d..984e55f 100644
--- a/core/res/res/values-mcc310-mnc260-zh-rHK/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-zh-rHK/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"無法使用 Wi-Fi 通話。請聯絡您的流動網絡供應商,以啟用 Wi-Fi 通話。"</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi 通話"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-zh-rTW/strings.xml b/core/res/res/values-mcc310-mnc260-zh-rTW/strings.xml
index 3acfb84..6a82063 100644
--- a/core/res/res/values-mcc310-mnc260-zh-rTW/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-zh-rTW/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"無法使用 Wi-Fi 通話功能。請與您的行動通訊業者聯絡,為您啟用 Wi-Fi 通話功能。"</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s Wi-Fi 通話"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260-zu/strings.xml b/core/res/res/values-mcc310-mnc260-zu/strings.xml
index a037bb8..2c06e9a 100644
--- a/core/res/res/values-mcc310-mnc260-zu/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-zu/strings.xml
@@ -25,4 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"Ukushaya kwe-Wi-Fi akutholakali. Xhumana nenkampani yakho yenethiwekhi ukuze unike amandla ukushaya kwe-Wi-Fi."</item>
   </string-array>
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s ukushaya kwe-Wi-Fi"</string>
 </resources>
diff --git a/core/res/res/values-mcc310-mnc260/strings.xml b/core/res/res/values-mcc310-mnc260/strings.xml
index 5cadc2a..dc79877 100644
--- a/core/res/res/values-mcc310-mnc260/strings.xml
+++ b/core/res/res/values-mcc310-mnc260/strings.xml
@@ -25,8 +25,14 @@
     <string-array name="wfcOperatorErrorCodes" translatable="false">
         <item>REG09</item>
     </string-array>
-    <!-- WFC Operator Error Messages -->
-    <string-array name="wfcOperatorErrorMessages">
-        <item>Wi-Fi Calling isn\&apos;t available. Contact your carrier to enable Wi-Fi Calling.</item>
+    <!-- WFC Operator Error Messages showed as alerts -->
+    <string-array name="wfcOperatorErrorAlertMessages">
+        <item>To make calls and send messages over Wi-Fi, first ask your carrier to set up this service. Then turn on Wi-Fi calling again from Settings.</item>
     </string-array>
+    <!-- WFC Operator Error Messages showed as notifications -->
+    <string-array name="wfcOperatorErrorNotificationMessages">
+        <item>Register with your carrier</item>
+    </string-array>
+    <!-- Template for showing cellular network operator name while WFC is active -->
+    <string name="wfcSpnFormat">%s Wi-Fi Calling</string>
 </resources>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index 2080d50..a3cfd66 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Повикување преку Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не е препратено"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> по <xliff:g id="TIME_DELAY">{2}</xliff:g> секунди"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Овозможува апликацијата да прави фотографии и да снима видеа со камерата. Оваа дозвола овозможува апликацијата да ја користи камерата во кое било време без ваша потврда."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"оневозможи пренесување на LED показателот при употреба на фотоапаратот"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Овозможува претходно инсталираниот систем на апликацијата да оневозможи фотоапаратот да го користи LED показателот."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"трајно оневозможи го таблетот"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"трајно оневозможи го телевизорот"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"трајно оневозможи го телефонот"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Дозволува апликацијата да повика начини за додавање и бришење шаблони на отпечатоци за користење."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"користи хардвер за отпечатоци"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Дозволува апликацијата да користи хардвер за отпечатоци за проверка"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Откриен е делумен отпечаток. Обидете се повторно."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Отпечатокот не можеше да се обработи. Обидете се повторно."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Сензорот за отпечатоци е валкан. Исчистете го и обидете се повторно."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Прстот се движеше премногу брзо. Обидете се повторно."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Прстот се движеше премногу бавно. Обидете се повторно."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Добивање порака за грешка карактеристична за снабдувачот 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Не може да се обработи. Обидете се повторно."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Хардвер не е достапен."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Отпечатокот не може да се складира. Отстранете го постоечкиот отпечаток."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Се достигна времето на истекување на отпечатокот. Обидете се повторно."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Се достигна времето на истекување на отпечатокот. Обидете се повторно."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Порака за грешка карактеристична за снабдувачот."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"прочитај синхронизирани подесувања"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Овозможува апликацијата да ги чита подесувањата за синхронизирање на сметка. На пример, така може да се утврди дали апликацијата „Луѓе“ е синхронизирана со сметка."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"вклучи и исклучи синхронизација"</string>
@@ -1119,6 +1138,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Дозволува апликацијата да провери дали пакетот може да се инсталира."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"врзи се со проверувач на пакет"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Дозволува сопственикот да поднесува барања до верификаторите на пакети. Не треба да се користи за стандардни апликации."</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"пристапи кон сериски порти"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Му овозможува на сопственикот да пристапи кон сериски порти употребувајќи го SerialManager API."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"однадвор пристапи кон обезбедувачи на содржина"</string>
@@ -1544,6 +1571,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Намали ден"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Зголеми година"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Намали година"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Копче „Alt“"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Копче „Откажи“"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Копче „Избриши“"</string>
@@ -1796,11 +1827,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Приказ на минути во кружно движење"</string>
     <string name="select_hours" msgid="6043079511766008245">"Избери часови"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Избери минути"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Рамка на месец со денови"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Список по години"</string>
     <string name="select_day" msgid="7774759604701773332">"Избери месец и ден"</string>
     <string name="select_year" msgid="7952052866994196170">"Избери година"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"Избрано <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"Избришано <xliff:g id="KEY">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Работа <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"За да го откачите екранот, допрете и задржете Назад и Краток преглед во исто време."</string>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index 2ae1177..9528628 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi കോളിംഗ്"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: കൈമാറിയില്ല"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> നിമിഷത്തിനുശേഷം <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"ക്യാമറ ഉപയോഗിച്ച് ചിത്രങ്ങളും വീഡിയോകളും എടുക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. നിങ്ങളുടെ സ്ഥിരീകരണമില്ലാതെ ഏതുസമയത്തും ക്യാമറ ഉപയോഗിക്കാൻ ഈ അനുമതി അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"ക്യാമറ ഉപയോഗത്തിലായിരിക്കുമ്പോൾ ട്രാൻസ്‌മിറ്റ് ഇൻഡിക്കേറ്റർ LED പ്രവർത്തനരഹിതമാക്കുക"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"ക്യാമറയുടെ ഇൻഡിക്കേറ്റർ LED-യുടെ ഉപയോഗം പ്രവർത്തനരഹിതമാക്കാൻ മുൻകൂട്ടി ഇൻസ്റ്റാളുചെയ്‌ത സിസ്റ്റം അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"ശാശ്വതമായി ടാബ്‌ലെറ്റ് പ്രവർത്തനരഹിതമാക്കുക"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"ടിവിയെ ശാശ്വതമായി പ്രവർത്തനരഹിതമാക്കുക"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"ശാശ്വതമായി ഫോൺ പ്രവർത്തനരഹിതമാക്കുക"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"ഉപയോഗിക്കാനായി വിരലടയാള ടെംപ്ലേറ്റുകൾ ചേർക്കാനും ഇല്ലാതാക്കാനുമുള്ള രീതികൾ അഭ്യർത്ഥിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"ഫിംഗർപ്രിന്റ് ഹാർഡ്‌വെയർ ഉപയോഗിക്കുക"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"പ്രാമാണീകരണത്തിനായി വിരലടയാളം ഉപയോഗിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"വിരലടയാളം ഭാഗികമായി തിരിച്ചറിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"വിരലടയാളം പ്രോസസ്സ് ചെയ്യാനായില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"വിരലടയാള സെൻസറിന് വൃത്തിയില്ല. അത് ശുചിയാക്കി വീണ്ടും ശ്രമിക്കുക."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"വിരൽ വേഗത്തിൽ നീക്കി. വീണ്ടും ശ്രമിക്കുക."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"വിരൽ പതുക്കെ നീക്കി. വീണ്ടും ശ്രമിക്കുക."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"വെൻഡർ നിർദ്ദിഷ്‌ട നേടൽ പിശക് സന്ദേശം 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"പ്രോസസ്സ് ചെയ്യാനാവുന്നില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"ഹാർഡ്‌വെയർ ലഭ്യമല്ല."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"വിരലടയാളം സംഭരിക്കാനാവില്ല. നിലവിലുള്ള വിരലടയാളം നീക്കംചെയ്യുക."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"വിരലടയാളം നൽകേണ്ട സമയം കഴിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"വിരലടയാളം നൽകേണ്ട സമയം കഴിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"വെൻഡർ നിർദ്ദിഷ്‌ട പിശക് സന്ദേശം."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"സമന്വയ ക്രമീകരണങ്ങൾ റീഡുചെയ്യുക"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ഒരു അക്കൗണ്ടിനായി സമന്വയ ക്രമീകരണങ്ങൾ റീഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു. ഉദാഹരണത്തിന്, ആളുകൾ അപ്ലിക്കേഷൻ ഒരു അക്കൗണ്ടിൽ സമന്വയിപ്പിച്ചിട്ടുണ്ടോയെന്നത് നിർണ്ണയിക്കാൻ ഇതിനാകും."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"സമന്വയം ഓണാക്കുക, ഓഫാക്കുക ടോഗിൾചെയ്യുക"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"ഒരു പാക്കേജ് ഇൻസ്റ്റാളുചെയ്യാനാവുന്നതാണോ എന്ന് പരിശോധിച്ചുറപ്പിക്കുന്നതിന് അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"ഒരു പാക്കേജ് വെരിഫയറുമായി ബന്ധപ്പെടുത്തുക"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"പാക്കേജ് പരിശോധകരുടെ അഭ്യർത്ഥനകൾക്ക് ഹോൾഡറിനെ അനുവദിക്കുന്നു. സാധാരണ അപ്ലിക്കേഷനുകൾക്ക് ഒരിക്കലും ആവശ്യമില്ല."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"ഉദ്ദേശിത ഫിൽട്ടർ പരിശോധിച്ചുറപ്പിക്കുക"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"ഒരു ഉദ്ദേശിത ഫിൽട്ടർ പരിശോധിച്ചുറപ്പിച്ചോ ഇല്ലയോ എന്ന് പരിശോധിക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"ഒരു ഉദ്ദേശിത ഫിൽട്ടർ പരിശോധകനിലേക്ക് ബന്ധിപ്പിക്കുക"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"ഉദ്ദേശിത ഫിൽട്ടർ പരിശോധകരുടെ അഭ്യർത്ഥനകൾക്ക് ഉടമയെ അനുവദിക്കുന്നു. സാധാരണ അപ്ലിക്കേഷനുകൾക്ക് ഒരിക്കലും ആവശ്യമില്ല."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"സീരിയൽ പോർട്ടുകൾ ആക്‌സസ്സ് ചെയ്യുക"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"SerialManager API ഉപയോഗിക്കുന്ന സീരിയൽ പോർട്ടുകൾ ആക്‌സസ്സ് ചെയ്യാൻ ദാതാവിനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"ഉള്ളടക്ക ദാതാക്കളെ ബാഹ്യമായി ആക്‌സസ്സുചെയ്യുക"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"ദിവസം കുറയ്ക്കുക"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"വർഷം വർദ്ധിപ്പിക്കുക"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"വർഷം കുറയ്‌ക്കുക"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"മുമ്പത്തെ മാസം"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"അടുത്ത മാസം"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"റദ്ദാക്കുക"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"ഇല്ലാതാക്കുക"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"ചാക്രികമായി മിനിറ്റുകൾ ദൃശ്യമാകുന്ന സ്ലൈഡർ"</string>
     <string name="select_hours" msgid="6043079511766008245">"മണിക്കൂർ തിരഞ്ഞെടുക്കുക"</string>
     <string name="select_minutes" msgid="3974345615920336087">"മിനിറ്റ് തിരഞ്ഞെടുക്കുക"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"മാസപ്രകാരമുള്ള ദിവസ ഗ്രിഡ്"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"വർഷങ്ങളുടെ ലിസ്റ്റ്"</string>
     <string name="select_day" msgid="7774759604701773332">"മാസവും ദിവസവും തിരഞ്ഞെടുക്കുക"</string>
     <string name="select_year" msgid="7952052866994196170">"വർഷം തിരഞ്ഞെടുക്കുക"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> തിരഞ്ഞെടുത്തു"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> ഇല്ലാതാക്കി"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"ഔദ്യോഗികം <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"ഈ സ്‌ക്രീൻ അൺപിൻ ചെയ്യാൻ \'മടങ്ങുക\', \'കാഴ്ച\' എന്നിവ ഒരേ സമയം സ്‌പർശിച്ച് പിടിക്കുക."</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index 4a8b5b9..37e9165 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi Calling"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: дамжуулагдаагүй"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> <xliff:g id="TIME_DELAY">{2}</xliff:g> секундын дараа"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Апп нь камераар зураг авах болон видео бичих боломжтой. Энэ зөвшөөрөл нь апп-д ямар ч үед таны зөвшөөрөлгүйгээр камер ашиглах боломжийг олгоно."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"камер ашиглаж байх үед дамжууллыг заагч LED-г идэвхгүй болгох"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Урьдчилан суусан систем аппликешн нь камер ашиглалтыг заасан LED-г идэвхгүй болгох боломжтой."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"таблетыг бүрмөсөн идэвхгүй болгох"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"телевиз-г бүр мөсөн идэвхгүй болгох"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"утсыг бүрмөсөн идэвхгүй болгох"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Хурууны хээний загварыг нэмэх эсвэл усгтах үйлдлийг хийх зөвшөөрлийг програмд олгодог."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"хурууны хээний програм хангамжийг ашиглах"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Баталгаажуулалт хийх зорилгоор хурууны хээний програм хамгамжийг ашиглах зөвшөөрлийг програмд олгодог"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Хурууны хээг дутуу уншуулсан байна. Дахин оролдоно уу."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Хурууны хээ боловсруулж чадахгүй байна. Дахин оролдоно уу."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Хурууны хээ мэдрэгч бохирдсон байна. Та цэвэрлэсний дараагаар дахин оролдоно уу."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Хурууг уншиж дуусаагүй байхад авсан байна. Та дахин уншуулна уу."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Хурууг хэт удаан уншуулсан байна. Та дахин уншуулна уу."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Борлуулагч-хурууны хээ авахад гардаг алдаа 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Мэдээллийг оруулах боломжгүй байна. Дахин оролдоно уу."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Техник хангамж бэлэн бус байна"</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Хурууны хээг хадгалах боломжгүй байна. Одоо байгаа хурууны хээг арилгана уу."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Хурууны хээ оруулах хугацаа өнгөрсөн байна. Дахин оруулна уу."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Хурууны хээ оруулах хугацаа өнгөрсөн байна. Дахин оруулна уу."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Борлуулаг-тодорхой алдааны зурвас"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"синк тохиргоог унших"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Апп нь акаунтын синк тохиргоог унших боломжтой. Жишээ нь энэ нь Хүмүүс апп акаунттай синк хийгдсэн эсэхийг тодорхойлох боломжтой."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"синкийг унтрааж асаах тохиргоо"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Апп нь багцыг суулгаж болох эсэхийг шалгах боломжтой."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"багц тулгагчтэй холбох"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Эзэмшигч нь багц тулгагчдад хүсэлт тавих боломжтой. Энгийн апп-д хэрэглэгдэхгүй."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"зориулалтын шүүлтүүрийг баталгаажуулах"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Зориулалтын шүүлтүүрийг баталгаажуулсан эсэхээс үл хамааран апп-д шалгах зөвшөөрөл өгөх"</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"зориулалтын шүүлтүүр шалгагч руу холбох"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Эзэмшигчид зориулалтын шүүлтүүрийг шалгах хүсэлт гаргахыг зөвшөөрөх. Энэ нь энгийн апп-уудад огт шаардлагагүй."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"сериал портруу хандах"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Эзэмшигч нь SerialManager API ашиглан сериал портод хандах боломжтой."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"контент нийлүүлэгчид гаднаас хандах"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Өдрийг бууруулах"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Жилийг өсгөх"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Жил бууруулах"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Өмнөх сар"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Дараагийн сар"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Цуцлах"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Устгах"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Минут гүйлгэгч"</string>
     <string name="select_hours" msgid="6043079511766008245">"Цаг сонгоно уу"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Минут сонгоно уу"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Өдрүүдийг сараар"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Жилийн жагсаалт"</string>
     <string name="select_day" msgid="7774759604701773332">"Сар болон өдрийг сонгоно уу"</string>
     <string name="select_year" msgid="7952052866994196170">"Жилийг сонгоно уу"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> сонгогдсон"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> устсан"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Ажлын <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Энэ дэлгэцийг цуцлахын тулд Буцах болон Тойм-д зэрэг хүрч барина."</string>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index 3fc5663..6ccf440 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"वाय-फाय कॉलिंग"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: अग्रेषित केला नाही"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> सेकंदांनंतर <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"कॅमेर्‍यासह चित्रे आणि व्हिडिओ घेण्यासाठी अॅप ला अनुमती देते. ही परवानगी आपल्या पुष्टीकरणाशिवाय कोणत्याही वेळी कॅमेरा वापरण्यासाठी अॅप ला परवानगी देते."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"कॅमेरा वापरात असताना प्रक्षेपण सूचक LED अक्षम करा"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"पूर्व-स्‍थापित सिस्‍टम अनुप्रयोगाला कॅमेरा वापर सूचक LED अक्षम करण्‍याची अनुमती देते."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"टॅब्लेट कायमचा अक्षम करा"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"टीव्ही कायमचा अक्षम करा"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"फोन कायमचा अक्षम करा"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"वापर करण्याकरिता फिंगरप्रिंट टेम्पलेट जोडण्यासाठी आणि हटविण्यासाठी पद्धती रद्द करण्यास अॅपला अनुमती देते."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"फिंगरप्रिंट हार्डवेअर वापरा"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"प्रमाणीकरणाकरिता फिंगरप्रिंट हार्डवेअरचा वापर करण्यासाठी अॅपला अनुमती देते"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक फिंगरप्रिंट आढळली. कृपया पुन्हा प्रयत्न करा."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"फिंगरप्रिंटवर प्रक्रिया करणे शक्य झाले नाही. कृपया पुन्हा प्रयत्न करा."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"फिंगरप्रिंट सेन्सर खराब आहे. कृपया साफ करा आणि पुन्हा प्रयत्न करा."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"बोट लवकर हलविले. कृपया पुन्हा प्रयत्न करा."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"बोट हळू हलविले. कृपया पुन्हा प्रयत्न करा."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"व्यापारी-विशिष्‍ट प्राप्ती त्रुटी संदेश 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"प्रक्रिया करण्यात अयशस्वी. पुन्हा प्रयत्न करा."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"हार्डवेअर उपलब्ध नाही."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"फिंगरप्रिंट संचयित केले जाऊ शकत नाही. कृपया विद्यमान फिंगरप्रिंट काढा."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"फिंगरप्रिंट कालबाह्य झाले. पुन्हा प्रयत्न करा."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"फिंगरप्रिंट कालबाह्य झाले. पुन्हा प्रयत्न करा."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"व्यापारी-विशिष्‍ट त्रुटी संदेश."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"संकालन सेटिंग्‍ज वाचा"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"खात्याच्या संकालन सेटिंग्ज वाचण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, हे खात्यासह लोकांचा अॅप संकालित केला आहे किंवा नाही हे निर्धारित करू शकते."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"संकालन चालू आणि बंद करा टॉगल करा"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"पॅकेज स्थापित करण्यायोग्य आहे हे सत्यापित करण्यासाठी अॅप ला अनुमती देते."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"पॅकेज सत्यापकावर प्रतिबद्ध व्हा"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"पॅकेज सत्यापित करणार्‍यांच्या विनंत्या करण्यासाठी होल्डरला अनुमती देते. सामान्य अॅप्सकरिता कधीही आवश्यक नसते."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"हेतू फिल्टर सत्यापित करा"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"हेतू फिल्टर सत्यापित केले आहे किंवा नाही हे तपासण्यासाठी अॅपला अनुमती देते."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"हेतू फिल्टर सत्यापनकर्त्यावर प्रतिबद्ध व्हा"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"हेतू फिल्टर सत्यापनकर्त्यांच्या विनंत्या करण्यासाठी होल्डरला अनुमती देते. सामान्य अॅप्सकरिता कधीही आवश्यकता नसावी."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"क्रमांक पोर्टवर प्रवेश करा"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"SerialManager API वापरून क्रमांक पोर्टवर प्रवेश करण्यास होल्डरला अनुमती देते."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"सामग्री प्रदात्यांवर बाह्यरित्या प्रवेश करेल"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"दिवस कमी करा"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"वर्ष वाढवा"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"वर्ष कमी करा"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"मागील महिना"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"पुढील महिना"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"रद्द करा"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"हटवा"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"मिनिटे परिपत्रक स्लायडर"</string>
     <string name="select_hours" msgid="6043079511766008245">"तास निवडा"</string>
     <string name="select_minutes" msgid="3974345615920336087">"मिनिटे निवडा"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"दिवसांची महिना ग्रिड"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"वर्ष सूची"</string>
     <string name="select_day" msgid="7774759604701773332">"महिना आणि दिवस निवडा"</string>
     <string name="select_year" msgid="7952052866994196170">"वर्ष निवडा"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> निवडले"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> हटविली"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"कार्य <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"ही स्क्रीन अनपिन करण्यासाठी, एकाच वेळी परत आणि विहंगावलोकनास स्पर्श करा आणि धरून ठेवा."</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index d849680..5fc99e6 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Panggilan Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Tidak dimajukan"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> selepas <xliff:g id="TIME_DELAY">{2}</xliff:g> saat"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Membenarkan apl mengambil gambar dan video menggunakan kamera. Kebenaran ini membenarkan apl untuk menggunakan kamera pada bila-bila masa tanpa pengesahan anda."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"lumpuhkan LED penunjuk penghantaran semasa kamera sedang digunakan"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Membenarkan aplikasi sistem yang diprapasang untuk melumpuhkan LED penunjuk penggunaan kamera."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"melumpuhkan tablet secara kekal"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"lumpuhkan TV secara kekal"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"lumpuhkan telefon secara kekal"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Membenarkan apl menggunakan kaedah untuk menambahkan dan memadamkan templat cap jari untuk digunakan."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"gunakan perkakasan cap jari"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Membenarkan apl menggunakan perkakasan cap jari untuk pengesahan"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Cap jari separa dikesan. Sila cuba lagi."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Tidak dapat memproses cap jari. Sila cuba lagi."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Penderia cap jari kotor. Sila bersihkan dan cuba lagi."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Jari digerakkan terlalu cepat. Sila cuba lagi."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Jari digerakkan terlalu perlahan. Sila cuba lagi."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Mesej ralat pemerolehan khusus vendor 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Tidak dapat memproses. Cuba lagi."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Perkakasan tidak tersedia."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Cap jari tidak dapat disimpan. Sila alih keluar cap jari sedia ada."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Tamat masa cap jari dicapai. Cuba lagi."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Tamat masa cap jari dicapai. Cuba lagi."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Mesej ralat khusus vendor"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"membaca tetapan penyegerakan"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Membenarkan apl membaca tetapan segerak untuk akaun. Sebagai contoh, ini boleh menentukan sama ada apl Orang disegerakkan dengan akaun."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"togol segerak hidup dan mati"</string>
@@ -1119,6 +1138,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Membenarkan apl untuk mengesahkan bahawa pakej boleh dipasang."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"ikat kepada pengesah pakej"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Membenarkan pemegang membuat permintaan pengesah pakej. Tidak sekali-kali diperlukan untuk apl normal."</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"akses port bersiri"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Membenarkan pemegang mengakses port bersiri menggunakan API SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"akses pembekal kandungan secara luaran"</string>
@@ -1544,6 +1571,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Kurangkan hari"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Tingkatkan tahun"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Kurangkan tahun"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Batal"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Padam"</string>
@@ -1794,11 +1825,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Penggelangsar bulatan minit"</string>
     <string name="select_hours" msgid="6043079511766008245">"Pilih jam"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Pilih minit"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Grid hari bulanan"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Senarai tahun"</string>
     <string name="select_day" msgid="7774759604701773332">"Pilih bulan dan hari"</string>
     <string name="select_year" msgid="7952052866994196170">"Pilih tahun"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> dipilih"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> dipadamkan"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Kerja <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Untuk menyahsemat skrin ini, sentuh dan tahan Kembali serta Ikhtisar pada masa yang sama."</string>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index 36a6cde..3470c1d 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"ဝိုင်ဖိုင် ခေါ်ဆိုမှု"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ထပ်ဆင့်မပို့နိုင်ပါ"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> နောက် <xliff:g id="TIME_DELAY">{2}</xliff:g> စက္ကန့်"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"အပလီကေးရှင်းအား အလိုအလျောက် ဓာတ်ပုံရိုက်ခွင့်၊ ဗီဒီယို ရိုက်ကူးခွင့် ပြုပါ။ ဒီခွင့်ပြုချက်က အပလီကေးရှင်းကို အချိန်မရွေး ကင်မရာအား ခွင့်ပြုချက် မလိုအပ်ပဲ သုံးခွင့်ပြုပါသည်။"</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"ထုတ်လွှင့်မှုပြ အချက်ပေး မီးအား ကင်မရာ သုံးနေစဉ် ပိတ်ရန်"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"ကြိုတင်သွင်းထားသော စစ်စတန် စနစ်တစ်ခုကို ကင်မရာ သုံးနေသော မီးအား ထိန်းချုပ်ခွင့်ပေးခြင်း"</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"တက်ပလက်ကို အမြဲတမ်း အလုပ်မလုပ်ရန်ပိတ်ခြင်း"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"တီဗွီအား အပြီးပိတ်ရန်"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"ဖုန်းကို အမြဲတမ်း အလုပ်မလုပ်ရန်ပိတ်ခြင်း"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"အသုံးပြုရန်အတွက် လက်ဗွေရာပုံစံများကို ပေါင်းထည့်ရန် သို့မဟုတ် ဖျက်ရန်နည်းလမ်းများကို အပ်ဖ်အား အသုံးပြုခွင့်ပြုသည်။"</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"လက်ဗွေရာပစ္စည်းကို အသုံးပြုမည်"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"စစ်မှန်ကြောင်းအထောက်အထားပြသခြင်းအတွက် လက်ဗွေရာပစ္စည်းကို အသုံးပြုရန် အပ်ဖ်အားခွင့်ပြုသည်။"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"လက်ဗွေရဦ တစ်ပိုင်းတစ်စ တွေ့ရှိသည်။ ကျေးဇူးပြု၍ ထပ်မံကြိုးစားပါ။"</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"လက်ဗွေရာယူခြင်း မဆောင်ရွက်နိုင်ပါ။ ထပ်မံကြိုးစားပါ။"</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"လက်ဗွေရာဖတ်ကိရိယာ ညစ်ပေနေသည်။ ကျေးဇူးပြု၍ ရှင်းလင်းကာ ထပ်မံကြိုးစားပါ။"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"လက်ရွှေ့လျားခြင်း အလွန်မြန်သည်။ ကျေးဇူးပြု၍ ထပ်မံကြိုးစားပါ။"</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"လက်ရွှေ့လျားခြင်း အလွန်နှေးသည်။ ကျေးဇူးပြု၍ ထပ်မံကြိုးစားပါ။"</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"ရောင်းသူ-တိကျသတ်မှတ်သော ရယူခြင်းဆိုင်ရာ မှားယွင်းချက် စာ ၀"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"လုပ်ဆောင်၍မရပါ။ ထပ်မံကြိုးစားပါ။"</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"ပစ္စည်းမရနိုင်ပါ။"</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"လက်ဗွေရာ သိုလှောင်၍မရပါ။ ကျေးဇူးပြု၍ ရှိပြီးလက်ဗွေရာအား ဖယ်ရှားပါ။"</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"လက်ဗွေရာအချိန်ကုန် သွားပါသည်။ ထပ်မံကြိုးစားပါ။"</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"လက်ဗွေရာအချိန်ကုန် သွားပါသည်။ ထပ်မံကြိုးစားပါ။"</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"ရောင်းသူ-တိကျသတ်မှတ်သော မှားယွင်းချက် စာ။"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ထပ်တူပြုအဆင်အပြင်အားဖတ်ခြင်း"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"အပလီကေးရှင်းအား အကောင့်တစ်ခုအတွက် ထပ်တူညီအောင် လုပ်ဆောင်မှု ဆက်တင်အား ကြည့်ခွင့် ပြုပါ။ ဥပမာ People app က အကောင့်တစ်ခုနဲ့ ထပ်တူညီအောင် လုပ်ရန် ဆက်သွယ်ထားမှု ရှိမရှိ သိရှိနိုင်ခြင်း"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ထပ်တူညီအောင် လုပ်ခြင်းအား ပြုနိုင်၊ မပြုနိုင် အပြောင်းအလဲလုပ်ခြင်း"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"appအား အထုပ် တစ်ခု၏ မတည်ငြိမ်မှုကို စိစစ်ခွင့် ပြုသည်။"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"package အတည်ပြုခြင်းနှင့် ပူးပေါင်းရန်"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"စွဲကိုင်ထားသူအား အထုပ်များအား စိစစ်ရေး တောင်းဆိုချက်များကို ပြုလုပ်ခွင့် ပေးသည်။ သာမန် appများ အတွက် ဘယ်တော့မှ မလိုအပ်နိုင်ပါ။"</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"လိုအပ်သည့်စစ်ထုတ်ခြင်းကို အတည်ပြုမည်"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"လိုအပ်သည့်စစ်ထုတ်ခြင်း အတည်ပြုပြီးခြင်း ရှိမရှိ အပ်ဖ်အား စစ်ဆေးခွင့်ပြုမည်။"</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"လိုအပ်သည့်စစ်ထုတ်ခြင်း အတည်ပြုကိရိယာဖြင့် ပေါင်းမည်"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"စွဲကိုင်ထားသူအား လိုအပ်အသည့်စစ်ထုတ်ခြင်း စိစစ်ရေး တောင်းဆိုချက်များကို ပြုလုပ်ခွင့် ပေးသည်။ သာမန် အပ်ဖ်များ အတွက် ဘယ်တော့မှ မလိုအပ်နိုင်ပါ။"</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"အစဥ်လိုက်ပို့များကို ဝင်ရောက်ချိတ်ဆက်ခြင်း"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"SerialManager APIအားအသုံးပြုကာ ကိုင်ဆောင်သူကို စီရီယာပို့မျာကို ဝင်ရောက်အသုံးပြုခြင်းအား ခွင့်ပြုသည်။"</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"အချက်အလက်များ ပံ့ပိုသူများအား အပြင်ဖက်မှ ရယူခြင်း"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"ရက် လျှော့ရန်"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"ခုနှစ် တိုးရန်"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"ခုနှစ် လျှော့ရန်"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"ပြီးခဲ့သော လ"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"လာမည့် လ"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Altခလုတ်"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"ပယ်ဖျက်ရန်ခလုတ်"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"ဖျက်ရန်ခလုတ်"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"မိနစ်လှည့်သော ရွေ့လျားတန်"</string>
     <string name="select_hours" msgid="6043079511766008245">"နာရီများ ရွေးပါ"</string>
     <string name="select_minutes" msgid="3974345615920336087">"မိနစ်များ ရွေးပါ"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"လအလိုက် ရွေးနိုင်သော ရက်များ"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"ခုနှစ် အစဉ်"</string>
     <string name="select_day" msgid="7774759604701773332">"လ နှင့် ရက် ရွေးပါ"</string>
     <string name="select_year" msgid="7952052866994196170">"ခုနှစ်ကို ရွေးပါ"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> ခုရွေးချယ်ထားပြီး"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> ကို ဖျက်ပြီးပါပြီ"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"အလုပ် <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"ဒီမျက်နှာပြင် ပင်ထိုးမှုကို ဖြုတ်ရန်၊ နောက်သို့ နှင့် ခြုံကြည့်မှု ခလုတ်များကို တစ်ချိန်တည်း ထိကိုင်ထားပါ။"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 022bb16..ccdf23a 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi-anrop"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Ikke viderekoblet"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> etter <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunder"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Appen tillates å ta bilder og filme med kameraet. Det betyr at appen kan bruke kameraet når som helst uten bekreftelse fra deg."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"deaktiver LED-lyset for indikering av overføring når kameraet er i bruk"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Tillater at forhåndsinnstallerte systemapper deaktiverer LED-indikatoren for kamerabruk."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"deaktivere nettbrettet permanent"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"deaktiver TV-en permanent"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"deaktivere telefonen permanent"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Lar appen fremkalle metoder for å legge til og slette fingeravtrykkmaler for bruk."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"bruke fingeravtrykkmaskinvare"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Lar appen bruke fingeravtrykkmaskinvare til godkjenning"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Deler av fingeravtrykket er registrert. Prøv på nytt."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Kunne ikke registrere fingeravtrykket. Prøv på nytt."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingeravtrykksensoren er skitten. Rengjør den og prøv på nytt."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Fingeren ble fjernet for raskt. Prøv på nytt."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Fingeren ble fjernet for sakte. Prøv på nytt."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Feilmelding 0 for leverandørspesifikk brukeranskaffelse"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Kunne ikke behandle fingeravtrykket. Prøv på nytt."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Maskinvaren er ikke tilgjengelig."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingeravtrykket kan ikke lagres. Fjern et eksisterende fingeravtrykk."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Tidsavbrudd for fingeravtrykk er nådd. Prøv på nytt."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Tidsavbrudd for fingeravtrykk er nådd. Prøv på nytt."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Leverandørspesifikk feilmelding."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"lese synkroniseringsinnstillinger"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Lar appen lese synkroniseringsinnstillingene for en konto. For eksempel kan den finne ut om Personer-appen er synkronisert med en konto."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"slå synkronisering av og på"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Lar appen bekrefte om en pakke kan installeres."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"bind til en pakkeverifikator"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Lar innehaveren sende forespørsler om pakkeverifikatorer. Skal aldri være nødvendig for normale apper."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"bekreft intensjonsfilter"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Gjør at appen kan sjekke om et bestemt intensjonsfilter er bekreftet eller ikke."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"knytt til en verifikator for intensjonsfiltre"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Lar innehaveren sende forespørsler om verifikatorer for intensjonsfiltre. Skal aldri være nødvendig for vanlige apper."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"bruke serieporter"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Gir innehaveren tilgang til serielle porter ved hjelp av SerialManager API."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"gå til innholdsleverandører eksternt"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Reduser dager"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Øk år"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reduser år"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Forrige måned"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Neste måned"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Avbryt"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Slett"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Sirkulær glidebryter for minutter"</string>
     <string name="select_hours" msgid="6043079511766008245">"Angi timer"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Angi minutter"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Månedsrutenett med dager"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Årsliste"</string>
     <string name="select_day" msgid="7774759604701773332">"Velg måneden og dagen"</string>
     <string name="select_year" msgid="7952052866994196170">"Velg året"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> er valgt"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> er slettet"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Jobb-<xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Hvis du vil avslutte én-appsmodusen for denne skjermen, trykker og holder du på Tilbake og Oversikt samtidig."</string>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index 54fe0e5..e1d9a21 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi कलिङ"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: अगाडि पठाइएको छैन"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> पछि <xliff:g id="TIME_DELAY">{2}</xliff:g> सेकेन्ड"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"अनुप्रयोगलाई क्यामेरासँग तस्बिर र भिडियोहरू लिन अनुमति दिन्छ। यस अनुमतिले अनुप्रयोगलाई तपाईंको पुष्टिकरण बिना कुनै पनि समयमा क्यामेरा प्रयोग गर्न स्वीकृति दिन्छ।"</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"क्यामेरा प्रयोगमा हुँदा सूचक LED प्रसारण असक्षम गर्नुहोस्"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"पूर्व-स्थापित प्रणाली अनुप्रयोगलाई क्यामेरा उपयोग सूचक LED अक्षम गर्न अनुमति दिन्छ।"</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"स्थायी रूपमा ट्याब्लेट असक्षम पार्नुहोस्"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"स्थायी रूपमा TV अक्षम गर्नुहोस्"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"फोनलाई स्थायी रूपमा असक्षम पार्नहोस्"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"अनुप्रयोगलाई प्रयोगको लागि औठाछाप टेम्प्लेट थप्न र मेटाउने तरिका आह्वान गर्न अनुमति दिन्छ।"</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"औठाछाप हार्डवेयर प्रयोग गर्नुहोस्"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"अनुप्रयोगलाई प्रमाणीकरणको लागि औठाछाप हार्डवेयर प्रयोग गर्न अनुमति दिन्छ"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"आंशिक औठाछाप पत्ता लाग्यो। कृपया फेरि प्रयास गर्नुहोस्।"</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"औठाछाप प्रशोधन गर्न सकिएन। कृपया फेरि प्रयास गर्नुहोस्।"</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"औँठाछाप सेन्सर फोहोर छ। कृपया सफा गरेर फेरि प्रयास गर्नुहोस्।"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"औंला निकै छिटो सारियो। कृपया फेरि प्रयास गर्नुहोस्।"</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"औंला निकै ढिला सारियो। कृपया फेरि प्रयास गर्नुहोस्।"</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"विक्रेता-निर्दिष्ट अधिग्रहण त्रुटि सन्देश 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"प्रसोधन गर्न असमर्थ। फेरि प्रयास गर्नुहोस्।"</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"हार्डवेयर उपलब्ध छैन।"</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"औँठाछाप भण्डारण गर्न सकिँदैन। कृपया अवस्थित औठाछाप हटाउनुहोस्।"</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"औँठाछापको समय सकिएको छ। फेरि प्रयास गर्नुहोस्।"</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"औँठाछापको समय सकिएको छ। फेरि प्रयास गर्नुहोस्।"</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"विक्रेता-निर्दिष्ट त्रुटि सन्देश।"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"समीकरण सेटिङहरू पढ्नुहोस्"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"अनुप्रयोगलाई खाताको लागि सिङ्क सेटिङहरू पढ्न अनुमति दिन्छ। उदाहरणको लागि यसले व्यक्तिहरको अनुप्रयोग खातासँग सिङ्क भएको नभएको निर्धारण गर्न सक्दछ।"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"टगल सिङ्क खुला र बन्द"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"प्याकेज स्थापना योग्य छ कि भनेर रुजु गर्न अनुप्रयोगलाई अनुमति दिन्छ।"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"एउटा प्याकेज रुजुकर्तामा बाँध्नुहोस्"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"होल्डरलाई प्याकेज प्रमाणितकर्ताहरूको अनुरोधहरू बनाउन अनुमति दिन्छ। सामान्य अनुप्रयोगहरूलाई कहिले पनि आवश्यक नपर्न सक्दछ।"</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"उद्देश्य फिल्टर प्रमाणिकरण गर्नुहोस्"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"एउटा उद्देश्य फिल्टर प्रमाणित भएको छ वा छैन भन्ने जाँच्न अनुप्रयोगलाई अनुमति दिन्छ।"</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"उद्देश्य फिल्टर प्रमाणिकरणकर्ता बाँध्नुहोस्"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"होल्डरलाई उद्देश्य फिल्टर प्रमाणिकरणकर्ताहरूका अनुरोध गर्न अनुमति दिन्छ। सामान्य अनुप्रयोगहरूको लागि कहिल्यै आवश्यकता पर्दैन।"</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"पहुँच सिरियल पोर्टहरू"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"होल्डरलाई SerialManager API प्रयोग गरेर सिरियल पोर्टहरू पहुँच गर्न अनुमति दिन्छ।"</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"विषयसूची प्रदातालाई बाह्य रूपमा पहुँच गर्नुहोस्"</string>
@@ -1550,6 +1573,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"दिन घटाउनुहोस्"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"वर्ष बढाउनुहोस्"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"वर्ष घटाउनुहोस्"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"अघिल्लो महिना"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"अर्को महिना"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"रद्द गर्नुहोस्"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"मेट्नुहोस्"</string>
@@ -1800,11 +1825,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"मिनेट गोलाकार स्लाइडर"</string>
     <string name="select_hours" msgid="6043079511766008245">"घण्टा चयन गर्नुहोस्"</string>
     <string name="select_minutes" msgid="3974345615920336087">"मिनेट चयन गर्नुहोस्"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"दिनहरुको महिना ग्रिड"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"वर्ष सूची"</string>
     <string name="select_day" msgid="7774759604701773332">"महिना र दिन चयन गर्नुहोस्"</string>
     <string name="select_year" msgid="7952052866994196170">"वर्ष चयन गर्नुहोस्"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> चयन गरियो"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> हटाइयो"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"कार्य <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"यस पर्दालाई अनपिन गर्न एकै समय फिर्ता र सारांशलाई छोई पक्डिनुहोस्।"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 3318bff..6735e89 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Bellen via wifi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: niet doorgeschakeld"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> na <xliff:g id="TIME_DELAY">{2}</xliff:g> seconden"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Hiermee kan de app foto\'s en video\'s maken met de camera. Met deze toestemming kan de app de camera altijd gebruiken, zonder uw bevestiging."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"indicatielampje uitschakelen wanneer camera wordt gebruikt"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Staat toe dat een vooraf geïnstalleerde systeemapp het indicatielampje voor cameragebruik uitschakelt."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"tablet permanent uitschakelen"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"tv permanent uitschakelen"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"telefoon permanent uitschakelen"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Hiermee kan de app methoden aanroepen om vingerafdruksjablonen toe te voegen en te verwijderen voor gebruik."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"vingerafdrukhardware gebruiken"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Hiermee kan de app vingerafdrukhardware gebruiken voor verificatie"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Gedeeltelijke vingerafdruk gedetecteerd. Probeer het opnieuw."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Kan vingerafdruk niet verwerken. Probeer het opnieuw."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"De vingerafdruksensor moet worden schoongemaakt. Probeer het daarna opnieuw."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Vinger te snel bewogen. Probeer het opnieuw."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Vinger te langzaam bewogen. Probeer het opnieuw."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Leveranciersspecifieke foutmelding voor acquisitie 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Kan niet verwerken. Probeer het opnieuw."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware niet beschikbaar."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Vingerafdruk kan niet worden opgeslagen. Verwijder een bestaande vingerafdruk."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Time-out bereikt voor vingerafdruk. Probeer het opnieuw."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Time-out bereikt voor vingerafdruk. Probeer het opnieuw."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Leveranciersspecifieke foutmelding"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"synchronisatie-instellingen lezen"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Hiermee kan de app de synchronisatie-instellingen voor een account lezen. Dit kan bijvoorbeeld bepalen of de app Personen wordt gesynchroniseerd met een account."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"synchronisatie in- en uitschakelen"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Hiermee kan de app controleren of een pakket kan worden geïnstalleerd."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"koppelen aan pakketcontroleprogramma"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Hiermee kan de houder verzoeken indienen voor pakketcontroles. Nooit vereist voor normale apps."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"intentiefilter verifiëren"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Hiermee kan de app controleren of een intentiefilter is geverifieerd."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"verbinden aan intentiefilterverificatie"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Hiermee kan de houder verzoeken indienen voor verificatie van intentiefilters. Nooit vereist voor normale apps."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"toegang krijgen tot seriële poorten"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"De houder toestaan toegang tot seriële poorten te krijgen met de SerialManager API."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"externe toegang tot inhoudsproviders"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Lagere waarde voor dag"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Hogere waarde voor jaar"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Lagere waarde voor jaar"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Vorige maand"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Volgende maand"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Annuleren"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Ronde schuifregelaar voor minuten"</string>
     <string name="select_hours" msgid="6043079511766008245">"Uren selecteren"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Minuten selecteren"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Maandraster van dagen"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Jaarlijst"</string>
     <string name="select_day" msgid="7774759604701773332">"Maand en dag selecteren"</string>
     <string name="select_year" msgid="7952052866994196170">"Jaar selecteren"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> geselecteerd"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> verwijderd"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Werk <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Blijf \'Terug\' en \'Overzicht\' tegelijk aanraken om dit scherm los te maken."</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 4e56e90..2e41660 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -128,6 +128,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Połączenia przez Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: nieprzekierowane"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po <xliff:g id="TIME_DELAY">{2}</xliff:g> sekundach"</string>
@@ -586,6 +587,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Pozwala aplikacji na robienie zdjęć i nagrywanie filmów przy użyciu aparatu. Aplikacja z tym uprawnieniem może użyć aparatu w dowolnym momencie bez Twojego potwierdzenia."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"wyłącz wskaźnik LED transmisji, gdy aparat jest w użyciu"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Zezwala wstępnie zainstalowanej aplikacji systemowej na wyłączenie wskaźnika LED użycia kamery."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"trwałe wyłączenie tabletu"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"trwałe wyłączenie telewizora"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"wyłączenie telefonu na stałe"</string>
@@ -748,6 +751,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Zezwala aplikacji aktywować metody dodawania i usuwania szablonów odcisków palców."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"używanie czytnika linii papilarnych"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Zezwala aplikacji na używanie czytnika linii papilarnych na potrzeby autoryzacji"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Odcisk palca został odczytany tylko częściowo. Spróbuj ponownie."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Nie udało się przetworzyć linii papilarnych. Spróbuj ponownie."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Czytnik linii papilarnych jest zabrudzony. Wyczyść go i spróbuj ponownie."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Palec został uniesiony zbyt szybko. Spróbuj ponownie."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Palec został przesunięty zbyt wolno. Spróbuj ponownie."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Komunikat o błędzie pozyskania specyficzny dla dostawcy 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Nie można przetworzyć. Spróbuj ponownie."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Czytnik jest niedostępny."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Nie można zapisać odcisku palca. Usuń istniejący odcisk palca."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Osiągnięto limit czasu odczytu linii papilarnych. Spróbuj ponownie."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Osiągnięto limit czasu odczytu linii papilarnych. Spróbuj ponownie."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Komunikat o błędzie specyficzny dla dostawcy."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"czytanie ustawień synchronizacji"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Zezwala aplikacji na odczyt ustawień synchronizacji konta. Pozwala to na przykład określić, czy aplikacja Ludzie jest zsynchronizowana z kontem."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"włączanie i wyłączanie synchronizacji"</string>
@@ -1121,6 +1140,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Pozwala aplikacji na zweryfikowanie, czy pakiet można zainstalować."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"powiązanie z weryfikatorem pakietów"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Pozwala na wysyłanie żądań weryfikacji pakietu. To uprawnienie nie powinno być potrzebne zwykłym aplikacjom."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"weryfikacja filtru intencji"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Zezwala aplikacji na sprawdzenie, czy filtr intencji został zweryfikowany."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"tworzenie powiązania z weryfikacją filtru intencji"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Zezwala na wysyłanie żądań weryfikacji filtrów intencji. Nieprzeznaczone dla zwykłych aplikacji."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"dostęp do portów szeregowych"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Umożliwia posiadaczowi dostęp do portów szeregowych przy użyciu interfejsu API narzędzia SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"Dostęp do dostawców treści z zewnątrz"</string>
@@ -1560,6 +1583,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Zmień dzień na wcześniejszy"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Zmień rok na późniejszy"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Zmień rok na wcześniejszy"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Poprzedni miesiąc"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Następny miesiąc"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Anuluj"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
@@ -1812,11 +1837,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Kołowy suwak minut"</string>
     <string name="select_hours" msgid="6043079511766008245">"Wybierz godziny"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Wybierz minuty"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Siatka miesięczna z dniami"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Lista lat"</string>
     <string name="select_day" msgid="7774759604701773332">"Wybierz miesiąc i dzień"</string>
     <string name="select_year" msgid="7952052866994196170">"Wybierz rok"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"Wybrałeś <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> usunięte"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> (praca)"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Aby odpiąć ten ekran, naciśnij i przytrzymaj jednocześnie Wstecz i Przegląd."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 1ddef71..f62e2fd 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Chamadas Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não reencaminhado"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> após <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permite que a aplicação tire fotografias e grave vídeos com a câmara. Esta autorização permite que a aplicação utilize a câmara sem a sua confirmação em qualquer altura."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"desativar LED indicador de transmissão com a câmara em utilização"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Permite que uma aplicação de sistema pré-instalada desative o LED indicador de utilização da câmara."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"desativar tablet de forma permanente"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"desativar a TV permanentemente"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"desativar telefone de forma permanente"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Permite que a aplicação invoque métodos para adicionar e eliminar modelos de impressão digital para utilização."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"utilizar o hardware de impressão digital"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Permite que a aplicação utilize o hardware de impressão digital para autenticação"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Impressão digital detetada. Tente novamente."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Não foi possível processar a impressão digital. Tente novamente."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"O sensor de impressões digitais está sujo. Limpe-o e tente novamente."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"O dedo moveu-se demasiado rápido. Tente novamente."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"O dedo moveu-se demasiado devagar. Tente novamente."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Mensagem de erro de aquisição específica do fornecedor 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Não é possível processar. Tente novamente."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware não disponível."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Não é possível armazenar a impressão digital. Remova uma impressão digital existente."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Foi atingido o limite de tempo da impressão digital. Tente novamente."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Foi atingido o limite de tempo da impressão digital. Tente novamente."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Mensagem de erro específica do fornecedor."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ler definições de sincronização"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite que a aplicação leia as definições de sincronização de uma conta. Por exemplo, pode determinar se a aplicação Pessoas está sincronizada com uma conta."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ativar e desativar a sincronização"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Permite que a aplicação verifique se um pacote é instalável."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"vincular a um verificador de pacotes"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permite ao titular solicitar verificadores de pacotes. Nunca deverá ser necessário para aplicações normais."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"validar filtro de intenções"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Permite que uma aplicação verifique se um filtro de intenções está ou não validado."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"vincular a um verif. de filtros intenç."</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Permite ao titular solicitar verificadores de filtros de intenções. Nunca deve ser necessário para aplicações normais."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"aceder a portas série"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Permite ao titular aceder a portas de série através da API SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"aceder a fornecedores de conteúdos externamente"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Diminuir dia"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aumentar ano"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Diminuir ano"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Mês anterior"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Mês seguinte"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancelar"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Controlo de deslize circular dos minutos"</string>
     <string name="select_hours" msgid="6043079511766008245">"Selecionar horas"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Selecionar minutos"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Grelha de dias do mês"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Lista de anos"</string>
     <string name="select_day" msgid="7774759604701773332">"Selecionar mês e dia"</string>
     <string name="select_year" msgid="7952052866994196170">"Selecionar ano"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> selecionado"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> eliminado"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> de trabalho"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Para soltar este ecrã, toque sem soltar em Retroceder e Visão geral em simultâneo."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 35ae345..e28c4d6 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Chamadas por Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Não encaminhado"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> após <xliff:g id="TIME_DELAY">{2}</xliff:g> segundos"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permite que o app tire fotos e filme vídeos com a câmera. Esta permissão autoriza o app a usar a câmera a qualquer momento sem sua confirmação."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"desativar a transmissão do LED indicador quando a câmera estiver em uso"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Permite que um app do sistema pré-instalado desative o LED indicador de uso da câmera."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"desativar permanentemente o tablet"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"desativar TV permanentemente"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"desativar permanentemente o telefone"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Permite que o app execute métodos para adicionar e excluir modelos de impressão digital para uso."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"usar hardware de impressão digital"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Permite que o app use hardware de impressão digital para autenticação."</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Impressão digital parcial detectada. Tente novamente."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Não foi possível processar a impressão digital. Tente novamente."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"O sensor de impressão digital está sujo. Limpe-o e tente novamente."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"O dedo foi retirado rápido demais. Tente novamente."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"O movimento do dedo está muito lento. Tente novamente."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Mensagem de erro de aquisição específica de fornecedor 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Não foi possível concluir o processo. Tente novamente."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware indisponível."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Não foi possível armazenar a impressão digital. Remova uma impressão digital já existente."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Tempo máximo para captura da impressão digital atingido. Tente novamente."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Tempo máximo para captura da impressão digital atingido. Tente novamente."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Mensagem de erro específica de fornecedor."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ler as configurações de sincronização"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite que o app leia as configurações de sincronização de uma conta. Por exemplo, pode determinar se o app People está sincronizado com uma conta."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ativar e desativar sincronização"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Permite que o app verifique se um pacote pode ser instalado."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"vincular a um verificador de pacote"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permite que o titular solicite verificadores de pacote. Nunca deve ser necessário para apps normais."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"verificar filtro de intenção"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Permite que o app verifique se um filtro de intenção foi confirmado ou não."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"usar verificador de filtro de intenção"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Permite que o titular solicite verificadores de filtro de intenção. Nunca é necessário para apps normais."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"acessar portas seriais"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Permite que o detentor tenha acesso a portas seriais usando a API SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"acessar fornec. de conteúdo externamente"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Diminuir dia"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Aumentar ano"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Diminuir ano"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Mês passado"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Próximo mês"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Cancelar"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Excluir"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Controle deslizante circular dos minutos"</string>
     <string name="select_hours" msgid="6043079511766008245">"Selecione as horas"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Selecione os minutos"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Grade mensal de dias"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Lista de anos"</string>
     <string name="select_day" msgid="7774759604701773332">"Selecione o mês e o dia"</string>
     <string name="select_year" msgid="7952052866994196170">"Selecione o ano"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> selecionado"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> excluído"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Trabalho: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Para liberar esta tela, toque e mantenha pressionados \"Voltar\" e \"Visão geral\" ao mesmo tempo."</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 360b966..d62a4d7 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -127,6 +127,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Apelare prin Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: neredirecţionată"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> după <xliff:g id="TIME_DELAY">{2}</xliff:g> (de) secunde"</string>
@@ -585,6 +586,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Permite aplicației să realizeze fotografii și videoclipuri cu camera foto. Cu această permisiune aplicația utilizează camera foto oricând și fără confirmare."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"dezactivează ledul care indică când este utilizată camera foto"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Permite unei aplicații de sistem preinstalate să dezactiveze ledul care indică utilizarea camerei foto."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"dezactivarea permanentă a computerului tablet PC"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"dezactivează definitiv televizorul"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"dezactivare permanentă a telefonului"</string>
@@ -747,6 +750,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Permite aplicației să invoce metode pentru a adăuga și pentru a șterge șabloane de amprentă pentru utilizare."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"folosește hardware-ul pentru amprentă"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Permite aplicației să folosească hardware pentru amprentă pentru autentificare"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"S-a detectat parțial amprenta. Încercați din nou."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Amprenta nu a putut fi procesată. Încercați din nou."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Senzorul pentru amprente este murdar. Curățați-l și încercați din nou."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Ați mișcat degetul prea repede. Încercați din nou."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Ați mișcat degetul prea încet. Încercați din nou."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Mesaj de eroare 0 specific furnizorului pentru achiziție"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Nu se poate procesa. Încercați din nou."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardware-ul nu este disponibil."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Amprenta nu poate fi stocată. Eliminați o amprentă existentă."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Timpul pentru amprentare a expirat. Încercați din nou."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Timpul pentru amprentare a expirat. Încercați din nou."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Mesaj de eroare specific furnizorului."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"citire setări sincronizare"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Permite aplicației să citească setările de sincronizare ale unui cont. De exemplu, cu această permisiune aplicația poate determina dacă aplicația Persoane este sincronizată cu un anumit cont."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"activează/dezactivează sincronizarea"</string>
@@ -1120,6 +1139,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Permite aplicației să verifice dacă un pachet poate fi instalat."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"conectare la un verificator de pachete"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permite proprietarului să efectueze solicitări pentru verificatori de pachete. Nu ar trebui să fie niciodată necesară pentru aplicațiile obişnuite."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"verifică filtrul de intenții"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Permite aplicației să afle dacă filtrul de intenții este verificat sau nu."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"se conectează la verificator de filtre de intenții"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Permite aplicației să efectueze solicitări pentru verificatorii filtrelor de intenții. Nu ar trebui să fie niciodată necesară pentru aplicațiile obişnuite."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"acces la porturi seriale"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Permite posesorului accesul la porturile serial utilizând API-ul SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"accesaţi furniz. de conţin. din exterior"</string>
@@ -1552,6 +1575,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Reduceţi valoarea pentru zi"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Creşteţi valoarea pentru an"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Reduceţi valoarea pentru an"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Luna trecută"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Luna viitoare"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Anulați"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Ștergeţi"</string>
@@ -1803,11 +1828,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Selector circular pentru minute"</string>
     <string name="select_hours" msgid="6043079511766008245">"Selectați orele"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Selectați minutele"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Afișare pe luni"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Listă de ani"</string>
     <string name="select_day" msgid="7774759604701773332">"Selectați luna și ziua"</string>
     <string name="select_year" msgid="7952052866994196170">"Selectați anul"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> selectat"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> a fost șters"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> de serviciu"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Pentru a anula fixarea pe ecran, apăsați lung, simultan, pe Înapoi și pe Vizualizare generală."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 5aa3b39..d1626242 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -128,6 +128,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Звонки по Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не переадресовано"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> через <xliff:g id="TIME_DELAY">{2}</xliff:g> с."</string>
@@ -586,6 +587,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Приложение сможет снимать фотографии и видеоролики с помощью камеры в любое время без вашего разрешения."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"Отключать светодиодный индикатор во время использования камеры"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Предустановленное системное приложение сможет отключать светодиодный индикатор использования камеры."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"Выключение планшета"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"навсегда отключать телевизор"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"Отключение телефона"</string>
@@ -748,6 +751,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Приложение сможет добавлять и удалять шаблоны отпечатков пальцев."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"использование сканера отпечатков"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Приложение сможет использовать сканер отпечатков пальцев для аутентификации."</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Отсканирована только часть пальца. Повторите попытку."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Не удалось распознать отпечаток. Повторите попытку."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Очистите сканер и повторите попытку."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Вы слишком быстро убрали палец. Повторите попытку."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Вы слишком долго удерживали палец. Повторите попытку."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Сообщение об ошибке при сканировании отпечатка (0)"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Не удалось распознать отпечаток. Повторите попытку."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Сканер недоступен."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Чтобы сохранить новый отпечаток, удалите существующий."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Превышено время ожидания. Повторите попытку."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Превышено время ожидания. Повторите попытку."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Сообщение об ошибке."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"Просмотр настроек синхронизации"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Приложение сможет просматривать настройки синхронизации аккаунта, например определять, включена ли синхронизация для приложения \"Контакты\"."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"Включение/выключение синхронизации"</string>
@@ -1121,6 +1140,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Приложение сможет проверять возможность установки пакетов."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"Подключение к верификаторам пакетов"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Приложение сможет запрашивать проверку пакетов. Это разрешение не используется обычными приложениями."</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"Доступ к последовательным портам"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Открыть владельцу доступ к последовательным портам с помощью SerialManager API."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"Доступ к контенту без приложения"</string>
@@ -1560,6 +1587,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"На день назад"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"На год вперед"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"На год назад"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Клавиша ALT"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Отмена"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Клавиша удаления"</string>
@@ -1812,11 +1843,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Выбор минут на циферблате"</string>
     <string name="select_hours" msgid="6043079511766008245">"Выберите часы"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Выберите минуты"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Окно выбора даты"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Меню выбора года"</string>
     <string name="select_day" msgid="7774759604701773332">"Выберите месяц и число"</string>
     <string name="select_year" msgid="7952052866994196170">"Выберите год"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"Выбран элемент <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"Цифра <xliff:g id="KEY">%1$s</xliff:g> удалена"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Рабочий <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Чтобы открепить экран, нажмите и удерживайте кнопки \"Назад\" и \"Обзор\" одновременно."</string>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index 8a83db9..54a74c6 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi ඇමතීම"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ඉදිරියට නොයවන ලදි"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: තත්පර <xliff:g id="TIME_DELAY">{2}</xliff:g> ට පසුව <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"කැමරාවෙන් පින්තූර ගැනීමට සහ වීඩියෝ කිරීමට යෙදුමට අවසර දෙන්න. මෙම අවසරය මඟින් ඔබගේ අනුදැනුමකින් තොරව ඕනෑම වේලාවකදී කැමරාව භාවිතා කිරීමට යෙදුමට අවසර දෙන්න."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"කැමරාව භාවිතයේදී LED දර්ශක සම්ප්‍රේෂණය අබල කරන්න"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"කැමරා භාවිතය පිළිබඳ LED දර්ශකය අක්‍රිය කිරීමට, කලින් පිහිටුවා ඇති පද්ධති යෙදුමට අවසර දෙන්න."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"ටැබ්ලටය ස්ථිරවම අබල කිරීම"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"රූපවාහිනිය ස්ථිරවම අබල කරන්න"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"දුරකථනය ස්ථිරව අබල කිරීම"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"ඇඟිලි සලකුණු සැකිලි එකතු කිරීමට සහ ඉවත් කිරීමට අදාළ විධික්‍රම භාවිතය සඳහා මෙම යෙදුමට අවසර දෙයි."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"ඇඟිලි සලකුණු දෘඩාංග භාවිතා කරන්න."</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"අනන්‍යතාවය තහවුරු කරගැනීමට ඇඟිලි සලකුණු දෘඩාංග භාවිතා කිරීමට මෙම යෙදුමට ඉඩ දෙන්න."</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ඇඟිලි සලකුණ අඩ වශයෙන් අනාවරණය කර ගැනිණි. කරුණාකර නැවත උත්සාහ කරන්න."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ඇඟිලි සලකුණ පිරිසැකසීමට නොහැකි විය. කරුණාකර නැවත උත්සාහ කරන්න."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"ඇඟිලි සලකුණු සංවේදකය අපිරිසිදුයි. කරුණාකර පිරිසිදු කර නැවත උත්සාහ කරන්න."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"ඇඟිල්ල වේගයෙන් ගෙනයන ලදි. කරුණාකර නැවත උත්සාහ කරන්න."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"ඇඟිල්ල සෙමින් ගෙන යන ලදී. කරුණාකර නැවත උත්සාහ කරන්න."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"විකුණුම්කරු-විශේෂිත අත්පත්පත් කර ගැනීමේ දෝෂ පණිවිඩය 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"ක්‍රියාවලිය සම්පූර්ණ කිරීමට නොහැක. නැවත උත්සාහ කරන්න."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"දෘඪාංගය ලද නොහැකිය."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"ඇඟිලි සලකුණ ගබඩා කළ නොහැක. දැනට පවතින ඇඟිලි සලකුණක් ඉවත් කරන්න."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"ඇඟිලි සලකුණු කාල නිමාව ළඟා විය. නැවත උත්සාහ කරන්න."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"ඇඟිලි සලකුණු කාල නිමාව ළඟා විය. නැවත උත්සාහ කරන්න."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"විකුණුම්කරු-විශේෂිත දෝෂ පණිවිඩය."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"සමමුහුර්ත සැකසීම් කියවන්න"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ගිණුම සඳහා සමමුහුර්ත සැකසීම් කියවීමට යෙදුමට අවසර දෙන්න. උදාහරණයක් ලෙස, ගිණුමක් සමඟ පුද්ගල යෙදුම සමමුහුර්ත දැයි මෙයට හඳුනා ගත හැක."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"සමමුහුර්ත කිරීම සක්‍රිය කරන්න සහ අක්‍රිය කරන්න"</string>
@@ -1121,6 +1140,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"ස්ථාපිත කොට ඇති පැකේජයක් සත්‍යාපනයට යෙදුමට අවසර දෙන්න."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"පැකේජ සත්‍යාපකයක් වෙත බඳින්න"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"පැකේජ සත්‍යාපක ඉල්ලීම් වලට දරන්නාට ඉඩ ලබා දේ. සාමාන්‍ය යෙදුම් සඳහා කිසිසේත් අවශ්‍ය නොවේ."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"චේතනාන්විත පෙරහන සත්‍යාපනය කරන්න"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"චේතනාන්විත පෙරහනක් සත්‍යාපනය කර තිබේද නැද්ද යන්න පරීක්ෂා කිරීමට යෙදුමට ඉඩ දෙයි."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"චේතනාන්විත පෙරහන් සත්‍යාපනකාරකයක් වෙත බඳින්න"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"චේතනාන්විත පෙරහන් සත්‍යාපනකාරකවල ඉල්ලීම් සිදු කිරීමට දරන්නාට ඉඩ දෙයි. සාමාන්‍ය යෙදුම් සඳහා කිසිදා අවශ්‍ය නොවිය යුතුය."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"ශ්‍රේණිගත පොට ප්‍රවේශ කිරීම"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"SerialManager API භාවිතයෙන් අනුක්‍රම තොට වෙත ප්‍රවේශ වීමට රඳවනයට අවසර දෙන්න."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"බාහිර අන්තර්ගත සැපයුම්කරුවන් වෙත ප්‍රවේශය"</string>
@@ -1546,6 +1569,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"දවස අඩු කරන්න"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"වසර වැඩි කරන්න"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"වසර අඩු කරන්න"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"පෙර මාසය"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"ඊළඟ මාසය"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"අවලංගු කරන්න"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"මකන්න"</string>
@@ -1796,11 +1821,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"මිනිත්තු කවාකාර සර්පනය"</string>
     <string name="select_hours" msgid="6043079511766008245">"පැය තෝරන්න"</string>
     <string name="select_minutes" msgid="3974345615920336087">"මිනිත්තු තෝරන්න"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"දින ජාලයකින් මාසය"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"වසර ලැයිස්තුව"</string>
     <string name="select_day" msgid="7774759604701773332">"මාසය සහ දිනය තෝරන්න"</string>
     <string name="select_year" msgid="7952052866994196170">"වසර තෝරන්න"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> තෝරාගෙන ඇත"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> මකා දමන ලදි"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"වැඩ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"මෙම තීරයේ ඇමුණුම ඉවත් කිරීමට, ආපසු සහ දළ විශ්ලේෂණය එකම වේලාවේ ස්පර්ශ කර අල්ලා සිටින්න."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 676ffe2..6964f65 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -128,6 +128,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Volanie cez Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nepresmerované"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po <xliff:g id="TIME_DELAY">{2}</xliff:g> s"</string>
@@ -586,6 +587,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Umožňuje aplikácii fotografovať a nahrávať videá pomocou fotoaparátu. Toto povolenie umožňuje aplikácii používať fotoaparát kedykoľvek a bez vášho potvrdenia."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"Zakázať indikátor LED prenosu pri používaní fotoaparátu"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Umožňuje v predinštalovanej systémovej aplikácii zakázať indikátor LED používania fotoaparátu."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"trvalé zakázanie tabletu"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"trvalé zakázanie televízora"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"trvalé vypnutie telefónu"</string>
@@ -748,6 +751,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Umožňuje aplikácii zavolať metódy, ktoré pridávajú a odstraňujú vzory odtlačkov prstov."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"použiť hardvér na snímanie odtlačkov prstov"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Umožňuje aplikácii používať na overenie totožnosti hardvér na snímanie odtlačkov prstov."</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Podarilo sa rozpoznať iba časť odtlačku prsta. Skúste to znova."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Odtlačok prsta sa nepodarilo spracovať. Skúste to znova."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Snímač odtlačkov je špinavý. Vyčistite ho a skúste to znova."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Pohli ste prstom príliš rýchlo. Skúste to znova."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Pohli ste prstom príliš pomaly. Skúste to znova."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Chybové hlásenie o akvizícii týkajúcej sa konkrétneho dodávateľa 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Došlo k chybe spracovania. Skúste to znova."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hardvér nie je k dispozícii"</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Odtlačok prsta nie je možné uložiť. Odstráňte existujúci odtlačok."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Časový limit rozpoznania odtlačku vypršal. Skúste to znova."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Časový limit rozpoznania odtlačku vypršal. Skúste to znova."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Chybové hlásenie týkajúce sa konkrétneho dodávateľa"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"čítať nastavenia synchronizácie"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Umožňuje aplikácii čítať nastavenia synchronizácie v účte. Môže napríklad určiť, či je s účtom synchronizovaná aplikácia Ľudia."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"zapnúť alebo vypnúť synchronizáciu"</string>
@@ -1121,6 +1140,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Umožňuje aplikácii overiť, či je možné balík nainštalovať."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"naviazať na overovateľa balíka"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Umožňuje držiteľovi podávať žiadosti o overenie balíkov. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"overiť filter intencií"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Umožňuje aplikácii overiť, či bol filter intencií overený."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"viazať na overenie filtra intencií"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Umožňuje držiteľovi podávať žiadosti o overenie filtra intencií. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"prístup k sériovým portom"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Držiteľa oprávňuje na prístup k sériovým portom pomocou rozhrania API SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"externý prístup k poskytovateľom obsahu"</string>
@@ -1560,6 +1583,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Ubrať deň"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Pridať rok"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Ubrať rok"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Predchádzajúci mesiac"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Nasledujúci mesiac"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Zrušiť"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Odstrániť"</string>
@@ -1812,11 +1837,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Kruhový posúvač minút"</string>
     <string name="select_hours" msgid="6043079511766008245">"Vyberte hodiny"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Vyberte minúty"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Tabuľka dní v mesiaci"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Zoznam rokov"</string>
     <string name="select_day" msgid="7774759604701773332">"Vyberte mesiac a deň"</string>
     <string name="select_year" msgid="7952052866994196170">"Vyberte rok"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"Bola vybratá položka <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"Číslo <xliff:g id="KEY">%1$s</xliff:g> bolo odstránené"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Práca – <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Ak chcete uvoľniť túto obrazovku, súčasne klepnite na tlačidlá Späť a Prehľad a podržte ich."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 07d4bb7..4e779f3 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -128,6 +128,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Klicanje prek Wi-Fi-ja"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ni posredovano"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> po toliko sekundah: <xliff:g id="TIME_DELAY">{2}</xliff:g>"</string>
@@ -586,6 +587,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Aplikaciji omogoča fotografiranje in snemanje videoposnetkov s kamero. S tem dovoljenjem lahko aplikacija kadar koli uporablja kamero brez vaše potrditve."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"onemogoči LED-indikator prenašanja, ko je fotoaparat v uporabi"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Dovoli že nameščeni sistemski aplikaciji, da onemogoči LED-indikator uporabe fotoaparata."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"trajno onemogočenje tabličnega računalnika"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"trajno onemogočanje televizorja"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"trajno onemogočenje telefona"</string>
@@ -748,6 +751,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Aplikaciji omogoča sprožanje načinov za dodajanje in brisanje predlog s prstnimi odtisi za uporabo."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"uporaba strojne opreme za prstne odtise"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Aplikaciji omogoča uporabo strojne opreme za prstne odtise za preverjanje pristnosti"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Zaznan delni prstni odtis. Poskusite znova."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Prstnega odtisa ni bilo mogoče obdelati. Poskusite znova."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Tipalo prstnih odtisov je umazano. Očistite ga in poskusite znova."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Prehiter premik prsta. Poskusite znova."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Prepočasen premik prsta. Poskusite znova."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Vendor-specific acquisition error message 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Obdelava ni mogoča. Poskusite znova."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Strojna oprema ni na voljo."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Prstnega odtisa ni mogoče shraniti. Odstranite obstoječi prstni odtis."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Dosežena časovna omejitev za prstni odtis. Poskusite znova."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Dosežena časovna omejitev za prstni odtis. Poskusite znova."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Vendor-specifc error message."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"branje nastavitev sinhronizacije"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Aplikaciji omogoča branje nastavitev sinhronizacije za račun. S tem lahko aplikacija na primer ugotovi, ali je aplikacija Ljudje sinhronizirana z računom."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"vklop in izklop sinhronizacije"</string>
@@ -1121,6 +1140,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Aplikaciji omogoča, da preveri, ali je paket mogoče namestiti."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"poveži s preverjanjem paketov"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Imetniku omogoča zahtevanje preverjanja paketov. Tega nikoli ni treba uporabiti za navadne aplikacije."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"preverjanje filtra namena"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Aplikaciji omogoča, da preveri, ali je filter namena preverjen ali ne."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"povez. s preverjevalnikom filtra namena"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Imetniku omogoča zahtevanje preverjevalnikov filtra namena. Tega se ne sme nikoli uporabiti za navadne aplikacije."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"dostop do serijskih vrat"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Imetniku omogoča, da z API-jem za SerialManager dostopa do serijskih vrat."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"zunanji dostop do ponudnikov vsebine"</string>
@@ -1560,6 +1583,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Zmanjšanje vrednosti za dan"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Povečanje vrednosti za leto"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Zmanjšanje vrednosti za leto"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Prejšnji mesec"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Naslednji mesec"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Tipka Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Prekliči"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Tipka Delete"</string>
@@ -1812,11 +1837,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Okrogli drsnik za minute"</string>
     <string name="select_hours" msgid="6043079511766008245">"Izberite ure"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Izberite minute"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Mesečna mreža dni"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Seznam let"</string>
     <string name="select_day" msgid="7774759604701773332">"Izberite mesec in dan"</string>
     <string name="select_year" msgid="7952052866994196170">"Izberite leto"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"Izbrano: <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"Številka <xliff:g id="KEY">%1$s</xliff:g> je izbrisana"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> za delo"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Če želite odpeti ta zaslon, se hkrati dotaknite tipk Nazaj in Pregled ter ju pridržite."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 54da9a4..72da7fc 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -127,6 +127,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Позивање преко Wi-Fi-ја"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Није прослеђено"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> након <xliff:g id="TIME_DELAY">{2}</xliff:g> секунде(и)"</string>
@@ -585,6 +586,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Дозвољава апликацији да снима слике и видео снимке камером. Ова дозвола омогућава апликацији да у било ком тренутку користи камеру без ваше потврде."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"онемогући пренос LED осветљења индикатора док се камера користи"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Дозвољава унапред инсталираној системској апликацији да онемогући LED осветљење индикатора за коришћење камере."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"трајно онемогућавање таблета"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"трајно онемогућавање ТВ-а"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"трајно онемогућавање телефона"</string>
@@ -747,6 +750,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Дозвољава апликацији да активира методе за додавање и брисање шаблона отисака прстију који ће се користити."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"користи хардвер за отиске прстију"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Дозвољава апликацији да користи хардвер за отиске прстију ради потврде аутентичности"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Откривен је делимични отисак прста. Покушајте поново."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Није успела обрада отиска прста. Покушајте поново."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Сензор за отиске прстију је прљав. Очистите га и покушајте поново."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Превише брзо сте померили прст. Покушајте поново."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Превише споро сте померили прст. Покушајте поново."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Порука о грешци за аквизицију специфична за произвођача 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Обрада није могућа. Покушајте поново."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Хардвер није доступан."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Није могуће сачувати отисак прста. Уклоните неки од постојећих отисака прстију."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Временско ограничење за отисак прста је истекло. Покушајте поново."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Временско ограничење за отисак прста је истекло. Покушајте поново."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Порука о грешци специфична за произвођача."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"читање подешавања синхронизације"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Дозвољава апликацији да чита подешавања синхронизације за налог. На пример, овако може да се утврди да ли је апликација Људи синхронизована са налогом."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"укључивање и искључивање синхронизације"</string>
@@ -1120,6 +1139,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Дозвољава апликацији да верификује да ли је пакет могуће инсталирати."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"обавезивање на верификатор пакета"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Омогућава да власник упућује захтеве верификаторима пакета. Уобичајене апликације никада не би требало да је користе."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"верификација intent филтера"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Дозвољава апликацији са провери да ли је intent филтер верификован."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"повезив. са верификатором intent филтера"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Дозвољава власнику да упућује захтеве верификаторима intent филтера. Уобичајене апликације никада не би требало да је користе."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"приступ серијским портовима"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Омогућава власнику да приступи серијским портовима помоћу SerialManager API-ја."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"приступ добављачима садржаја споља"</string>
@@ -1552,6 +1575,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Смањивање дана"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Повећавање године"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Смањивање године"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Претходни месец"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Следећи месец"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Откажи"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Избриши"</string>
@@ -1803,11 +1828,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Кружни клизач за минуте"</string>
     <string name="select_hours" msgid="6043079511766008245">"Изаберите сате"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Изаберите минуте"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Приказ дана у месецу у виду мреже"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Листа година"</string>
     <string name="select_day" msgid="7774759604701773332">"Изаберите месец и дан"</string>
     <string name="select_year" msgid="7952052866994196170">"Изаберите годину"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"Изабрали сте <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"Избрисали сте <xliff:g id="KEY">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> на послу"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Да бисте откачили овај екран, истовремено додирните и задржите Назад и Преглед."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index dd8a883..9ca2dc8 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi-samtal"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Vidarebefordras inte"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g><xliff:g id="DIALING_NUMBER">{1}</xliff:g> efter <xliff:g id="TIME_DELAY">{2}</xliff:g> sekunder"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Tillåter att appen tar bilder och spelar in videor med kameran. Med den här behörigheten tillåts appen att använda kameran när som helst utan ditt godkännande."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"inaktivera LED-sändningsindikator när kameran används"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Tillåter att en förinstallerad systemapp inaktiverar LED-indikatorn för kameranvändning."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"inaktivera surfplattan permanent"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"inaktivera tv:n permanent"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"inaktivera telefonen permanent"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Tillåter att appen anropar metoder för att lägga till och radera fingeravtrycksmallar."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"använda maskinvara för fingeravtryck"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Tillåter att appen använder maskinvara för fingeravtryck vid autentisering"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Ofullständigt fingeravtryck. Försök igen."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Det gick inte att bearbeta fingeravtrycket. Försök igen."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Fingeravtryckssensorn är smutsig. Rengör den och försök igen."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Du flyttade fingret för snabbt. Försök igen."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Du flyttade fingret för långsamt. Försök igen."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Leverantörsspecifikt felmeddelande om förvärv 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Det går inte att bearbeta fingeravtrycket. Försök igen."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Maskinvaran är inte tillgänglig."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Fingeravtrycket kan inte lagras. Ta bort ett befintligt fingeravtryck."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Tidsgränsen för fingeravtrycket har uppnåtts. Försök igen."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Tidsgränsen för fingeravtrycket har uppnåtts. Försök igen."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Leverantörsspecifikt felmeddelande"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"läsa synkroniseringsinställningar"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Tillåter att appen läser synkroniseringsinställningarna för ett konto. Detta kan användas till exempel för att avgöra om appen Personer är synkroniserad med ett konto."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"aktivera/inaktivera synkronisering"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Tillåter att appen kontrollerar om ett paket går att installera."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"binda till en paketverifierare"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Tillåter att innehavaren skickar förfrågningar till paketverifierare. Det ska inte behövas för vanliga appar."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"verifiera intent-filter"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Tillåter att appen kontrollerar om ett intent-filter har verifierats eller inte."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"binda till en intent-filterverifierare"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Tillåter att innehavaren skickar begäranden till intent-filterverifierare. Behövs inte för vanliga appar."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"åtkomst till serieportar"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Innebär att innehavaren får åtkomst till serieportar med programmeringsgränssnittet för SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"komma åt innehållsleverantörer externt"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Minska dagar"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Öka år"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Minska år"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Föregående månad"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Nästa månad"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Avbryt"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Cirkelreglage för minuter"</string>
     <string name="select_hours" msgid="6043079511766008245">"Välj timmar"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Välj minuter"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Rutnät för månad"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Lista över år"</string>
     <string name="select_day" msgid="7774759604701773332">"Välj månad och dag"</string>
     <string name="select_year" msgid="7952052866994196170">"Välj år"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> har markerats"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> har tagits bort"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> för arbetet"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Om du vill lossa skärmen trycker du länge på Tillbaka och Översikt samtidigt."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index cfa56de..e48fa23 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Upigaji Simu kwa Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Haijatumiwa mwingine"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> baada ya sekunde <xliff:g id="TIME_DELAY">{2}</xliff:g>"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Inaruhusu programu kupiga picha na video kwa kamera. Kibali hiki kinaruhusu programu kutumia kamera kwa wakati wowote bila uthibitisho wako."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"zima LED ya kisambaza kiashirio wakati kamera inatumika"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Huruhusu mfumo wa programu iliyosakinishwa awali kuzima kamera isitumie kiashirio cha LED."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"zima kompyuta ndogo kabisa"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"zima runinga kabisa"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"simu iliyolemazwa kabisa"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Huruhusu programu kuomba njia za kuongeza na kufuta violezo vya kitambulisho kwa matumizi."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"tumia maunzi ya kitambulisho"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Huruhusu programu kutumia maunzi ya kitambulisho kwa uthibitisho"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Kihisi kimegundua sehemu ya kitambulisho. Tafadhali jaribu tena."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Haikuweza kuchakata kitambulisho. Tafadhali jaribu tena."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Kihisi kitambulisho ni kichafu. Tafadhali kisafishe na ujaribu tena."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Ulisogeza kidole kwa kasi mno. Tafadhali jaribu tena."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Ulisogeza kidole pole pole mno. Tafadhali jaribu tena."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Ujumbe maalum kwa muuzaji kuhusu hitilafu ya uletaji wa kitambulisho 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Haikuweza kuchakata. Jaribu tena."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Maunzi hayapatikani."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Kitambulisho hakiwezi kuhifadhiwa. Tafadhali ondoa kitambulisho kilichopo."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Muda wa kitambulisho umekwisha. Jaribu tena."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Muda wa kitambulisho umekwisha. Jaribu tena."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Ujumbe maalum kwa muuzaji kuhusu hitilafu."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"kusoma mipangilio ya usawazishaji"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Inaruhusu programu kusoma mipangilio ya upatanishi wa akaunti. Kwa mfano, huku kunaweza kuamua kama programu ya Watu imepatanishwa na akaunti."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"kuwasha na kuzima usawazishaji"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Inaruhusu programu kuthibitisha kuwa furushi linaweza kusakinishwa."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"Funga kwa kithibitishaji cha furushi"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Inaruhusu mmiliki kutuma maombi ya vibainishi furushi. Kamwe hazitahitajika kwa programu za kawaida."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"thibitisha kichujio cha kutoa maagizo"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Inaruhusu programu kukagua ikiwa kuchujio cha kutoa maagizo kimethibitishwa au bado."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"funga kwenye kithibitishaji cha kichujio cha kutoa maagizo."</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Inaruhusu mmiliki kutuma maombi ya vithibitishaji vya kichujio cha kutoa maagizo. Haviwezi kuhitajika katika programu za kawaida."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"kituo tambulishi cha ufikivu"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Inaruhusu mmiliki kufikia vituo tambulishi kwa kutumia KisimamiziTambulishi cha API."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"fikia watoa huduma nje"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Punguza siku"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Ongeza mwaka"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Punguza mwaka"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Mwezi uliopita"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Mwezi ujao"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Ghairi"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Futa"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Kitelezi cha mviringo wa dakika"</string>
     <string name="select_hours" msgid="6043079511766008245">"Chagua saa"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Chagua dakika"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Gridi ya mwezi ya siku"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Orodha ya miaka"</string>
     <string name="select_day" msgid="7774759604701773332">"Chagua mwezi na siku"</string>
     <string name="select_year" msgid="7952052866994196170">"Chagua mwaka"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> kimechaguliwa"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> kimefutwa"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Ya kazini <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Ili ubanue skrini hii, gusa na ushikilie Nyuma na Muhtasari kwa wakati mmoja."</string>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index 0e5bf3c..c059b32 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"வைஃபை அழைப்பு"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: பகிரப்படவில்லை"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> வினாடிகளுக்குப் பிறகு <xliff:g id="DIALING_NUMBER">{1}</xliff:g> ஐப் பகிர்"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"கேமரா மூலமாகப் படங்களையும், வீடியோக்களையும் எடுக்க பயன்பாட்டை அனுமதிக்கிறது. உங்கள் உறுதிப்படுத்தல் இன்றி கேமராவை எந்நேரத்திலும் பயன்படுத்தப் பயன்பாட்டை இது அனுமதிக்கிறது."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"கேமரா பயன்பாட்டில் இருக்கும்போது டிரான்ஸ்மிட் இன்டிகேட்டர் LED ஐ முடக்குதல்"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"இன்டிகேட்டர் LED ஐ கேமரா பயன்படுத்துவதை முடக்க, முன்நிறுவப்பட்ட அமைப்பு பயன்பாட்டை அனுமதிக்கிறது."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"டேப்லெட்டை நிரந்தரமாக முடக்குதல்"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"டிவியை நிரந்தரமாக முடக்குதல்"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"தொலைபேசியை நிரந்தரமாக முடக்குதல்"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"பயன்படுத்துவதற்காக, கைரேகை டெம்ப்ளேட்களைச் சேர்க்க மற்றும் நீக்குவதற்கான செயல்முறைகளை இயக்குவதற்குப் பயன்பாட்டை அனுமதிக்கும்."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"கைரேகை வன்பொருளைப் பயன்படுத்து"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"அங்கீகரிப்பதற்கு, கைரேகை வன்பொருளைப் பயன்படுத்த, பயன்பாட்டை அனுமதிக்கும்"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"கைரேகையை ஓரளவுதான் கண்டறிய முடிந்தது. மீண்டும் முயலவும்."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"கைரேகையைச் செயலாக்க முடியவில்லை. மீண்டும் முயலவும்."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"கைரேகை உணர்வியில் தூசி உள்ளது. சுத்தம் செய்து, முயலவும்."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"விரலை வேகமாக எடுத்துவிட்டீர்கள். மீண்டும் முயலவும்."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"விரலை மெதுவாக எடுத்துவிட்டீர்கள். மீண்டும் முயலவும்."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"விற்பனையாளர் சார்ந்த பெறுதல் தொடர்பான பிழைச் செய்தி 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"செயல்படுத்த முடியவில்லை. மீண்டும் முயலவும்."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"வன்பொருள் இல்லை."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"கைரேகையைச் சேமிக்க முடியவில்லை. ஏற்கனவே உள்ள கைரேகையை அகற்றவும்."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"கைரேகைக்கான நேரம் முடிந்தது. மீண்டும் முயலவும்."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"கைரேகைக்கான நேரம் முடிந்தது. மீண்டும் முயலவும்."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"விற்பனையாளர் சார்ந்த பிழைச் செய்தி."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"ஒத்திசைவு அமைப்புகளைப் படித்தல்"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"கணக்கிற்கான ஒத்திசைவு அமைப்புகளைப் படிக்க பயன்பாட்டை அனுமதிக்கிறது. எடுத்துக்காட்டாக, பீப்பிள் பயன்பாடு கணக்குடன் ஒத்திசைக்கப்பட்டுள்ளதா என்பதை இது தீர்மானிக்கலாம்."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"ஒத்திசைவை இயக்குவதையும், முடக்குவதையும் மாற்றுதல்"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"பேக்கேஜ் நிறுவுவதற்கு ஏற்றதா என்பதைச் சரிபார்க்க, பயன்பாட்டை அனுமதிக்கிறது."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"தொகுப்பு சரிபார்ப்பானுடன் இணைத்தல்"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"தொகுப்பைச் சரிபார்ப்பிற்கான கோரிக்கைகளை உருவாக்க, ஹோல்டரை அனுமதிக்கிறது. சாதாரண பயன்பாடுகளுக்கு எப்போதுமே தேவைப்படாது."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"இன்டென்ட் வடிப்பானைச் சரிபார்த்தல்"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"இன்டென்ட் வடிப்பான் சரிபார்க்கப்பட்டதா அல்லது இல்லையா என்பதைச் சோதிக்க, பயன்பாட்டை அனுமதிக்கும்."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"இன்டென்ட் வடிப்பான் சரிபார்ப்பியுடன் இணைத்தல்"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"இன்டென்ட் வடிப்பான் சரிபார்ப்பிகளின் கோரிக்கைகளை உருவாக்க ஹோல்டரை அனுமதிக்கும். சாதாரணப் பயன்பாடுகளுக்கு எப்போதுமே தேவைப்படாது."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"தொடர் போர்ட்களின் அணுகல்"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"SerialManager API ஐப் பயன்படுத்தி தொடர் போர்ட்களை அணுக ஹோல்டரை அனுமதிக்கிறது."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"வெளிப்புறமாக வழங்கப்படும் உள்ளடக்கத்திற்கான அணுகல்"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"நாளினைக் குறை"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"வருடத்தை அதிகரி"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"வருடத்தைக் குறை"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"முந்தைய மாதம்"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"அடுத்த மாதம்"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"ரத்துசெய்"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"நீக்கு"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"நிமிடங்களுக்கான வட்டவடிவ ஸ்லைடர்"</string>
     <string name="select_hours" msgid="6043079511766008245">"மணிநேரத்தைத் தேர்ந்தெடுக்கவும்"</string>
     <string name="select_minutes" msgid="3974345615920336087">"நிமிடத்தைத் தேர்ந்தெடுக்கவும்"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"நாட்களின் மாதக் கட்டம்"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"ஆண்டு பட்டியல்"</string>
     <string name="select_day" msgid="7774759604701773332">"மாதம் மற்றும் தேதியைத் தேர்ந்தெடுக்கவும்"</string>
     <string name="select_year" msgid="7952052866994196170">"ஆண்டைத் தேர்ந்தெடுக்கவும்"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> தேர்ந்தெடுக்கப்பட்டது"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> நீக்கப்பட்டது"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"பணியிடம் <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"இந்தத் திரையை விலக்க, பின் மற்றும் மேலோட்டப் பார்வையை ஒரே நேரத்தில் தொட்டுப் பிடித்திருக்கவும்."</string>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index 4684adf..f7b661e 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi కాలింగ్"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ఫార్వార్డ్ చేయబడలేదు"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> సెకన్ల తర్వాత <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"కెమెరాతో చిత్రాలు మరియు వీడియోలను తీయడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఈ అనుమతి మీ నిర్ధారణ లేకుండానే ఎప్పుడైనా కెమెరాను ఉపయోగించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"కెమెరా ఉపయోగంలో ఉన్నప్పుడు ప్రసరణ సూచీ LEDని నిలిపివేయడం"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"కెమెరా వినియోగ సూచీ LEDని నిలిపివేయడానికి ముందే ఇన్‌స్టాల్ చేయబడిన సిస్టమ్ అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"టాబ్లెట్‌ను శాశ్వతంగా నిలిపివేయడం"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"టీవీని శాశ్వతంగా నిలిపివేయడం"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"ఫోన్‌ను శాశ్వతంగా నిలిపివేయడం"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"వినియోగం కోసం వేలిముద్ర టెంప్లేట్‌లను జోడించే మరియు తొలగించే పద్ధతులను అమలు చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"వేలిముద్ర హార్డ్‌వేర్‌ని ఉపయోగించడానికి అనుమతి"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"ప్రామాణీకరణ కోసం వేలిముద్ర హార్డ్‌వేర్‌ను ఉపయోగించడానికి అనువర్తనాన్ని అనుమతిస్తుంది"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"పాక్షిక వేలిముద్ర గుర్తించబడింది. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"వేలిముద్రను ప్రాసెస్ చేయడం సాధ్యపడలేదు. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"వేలిముద్ర సెన్సార్ మురికిగా ఉంది. దయచేసి శుభ్రపరిచి, మళ్లీ ప్రయత్నించండి."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"వేలిని చాలా తొందరగా కదిలించారు. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"వేలిని చాలా నిదానంగా కదిలించారు. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"విక్రేత-నిర్దిష్ట సేకరణ లోప సందేశం 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"ప్రాసెస్ చేయడం సాధ్యపడలేదు. మళ్లీ ప్రయత్నించండి."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"హార్డ్‌వేర్ అందుబాటులో లేదు."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"వేలిముద్రను నిల్వ చేయడం సాధ్యపడదు. దయచేసి ఇప్పటికే ఉన్న వేలిముద్రను తీసివేయండి."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"వేలిముద్ర గడువు సమయం చేరుకుంది. మళ్లీ ప్రయత్నించండి."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"వేలిముద్ర గడువు సమయం చేరుకుంది. మళ్లీ ప్రయత్నించండి."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"విక్రేత-నిర్దిష్ట లోప సందేశం."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"సమకాలీకరణ సెట్టింగ్‌లను చదవడం"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"ఖాతా యొక్క సమకాలీకరణ సెట్టింగ్‌లను చదవడానికి అనువర్తనాన్ని అనుమతిస్తుంది. ఉదాహరణకు, వ్యక్తుల అనువర్తనం ఖాతాతో సమకాలీకరించబడాలా లేదా అనే విషయాన్ని ఇది నిశ్చయించవచ్చు."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"సమకాలీకరణను ఆన్ మరియు ఆఫ్‌కు టోగుల్ చేయడం"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"ప్యాకేజీ ఇన్‌స్టాల్ చేయవచ్చని ధృవీకరించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"ప్యాకేజీ తనిఖీదారుకు అనుబంధించడం"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"ప్యాకేజీ తనిఖీదారుల యొక్క అభ్యర్థనలు చేయడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"ఉద్దేశిత ఫిల్టర్‌ను ధృవీకరించడం"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"ఉద్దేశిత ఫిల్టర్ ధృవీకరించబడిందో లేదో తనిఖీ చేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"ఉద్దేశిత ఫిల్టర్ వెరిఫైయర్‌కి నిర్బంధించడం"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"ఉద్దేశిత ఫిల్టర్ వెరిఫైయర్‌ల అభ్యర్థనలు చేయడానికి హోల్డర్‌ను అనుమతిస్తుంది. సాధారణ అనువర్తనాలకు ఎప్పటికీ అవసరం ఉండదు."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"శ్రేణి పోర్ట్‌లను ప్రాప్యత చేయడం"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"శ్రేణి నిర్వాహికి APIని ఉపయోగించి శ్రేణి పోర్ట్‌లను ప్రాప్యత చేయడానికి హోల్డర్‌ను అనుమతిస్తుంది."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"కంటెంట్ ప్రదాతలను బాహ్యంగా ప్రాప్యత చేయడం"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"రోజును తగ్గించండి"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"సంవత్సరాన్ని పెంచండి"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"సంవత్సరాన్ని తగ్గించండి"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"మునుపటి నెల"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"తదుపరి నెల"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"రద్దు చేయి"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"తొలగించు"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"నిమిషాల వృత్తాకార స్లయిడర్"</string>
     <string name="select_hours" msgid="6043079511766008245">"గంటలను ఎంచుకోండి"</string>
     <string name="select_minutes" msgid="3974345615920336087">"నిమిషాలను ఎంచుకోండి"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"రోజుల యొక్క నెల గ్రిడ్"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"సంవత్సర జాబితా"</string>
     <string name="select_day" msgid="7774759604701773332">"నెల మరియు రోజును ఎంచుకోండి"</string>
     <string name="select_year" msgid="7952052866994196170">"సంవత్సరాన్ని ఎంచుకోండి"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> ఎంచుకోబడింది"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> తొలగించబడింది"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"కార్యాలయం <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"ఈ స్క్రీన్‌ను అన్‌పిన్ చేయడానికి, వెనుకకు మరియు అవలోకనం బటన్‌లను ఒకేసారి నొక్కి, ఉంచండి."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 39be8c7..e42017b 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"การโทรผ่าน Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: ไม่ได้โอนสาย"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> หลังผ่านไป <xliff:g id="TIME_DELAY">{2}</xliff:g> วินาที"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"อนุญาตให้แอปพลิเคชันถ่ายภาพและวิดีโอด้วยกล้องถ่ายรูปนี้ การอนุญาตนี้จะทำให้แอปพลิเคชันสามารถใช้กล้องถ่ายรูปได้ทุกเมื่อโดยไม่ต้องรอการยืนยันจากคุณ"</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"ปิดไฟสัญญาณ LED เมื่อใช้งานกล้อง"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"อนุญาตให้แอปพลิเคชันระบบที่ติดตั้งล่วงหน้าปิดไฟสัญญาณ LED ของกล้อง"</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"ปิดการใช้งานแท็บเล็ตอย่างถาวร"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"ปิดใช้ทีวีถาวร"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"ปิดการใช้งานโทรศัพท์ถาวร"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"อนุญาตให้แอปเรียกใช้วิธีการเพื่อเพิ่มและลบเทมเพลตลายนิ้วมือสำหรับการใช้งาน"</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"ใช้ฮาร์ดแวร์ลายนิ้วมือ"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"อนุญาตให้แอปใช้ฮาร์ดแวร์ลายนิ้วมือเพื่อตรวจสอบสิทธิ์"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"ตรวจพบลายนิ้วมือเพียงบางส่วน โปรดลองอีกครั้ง"</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"ไม่สามารถประมวลผลลายนิ้วมือได้ โปรดลองอีกครั้ง"</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"เซ็นเซอร์ลายนิ้วมือไม่สะอาด โปรดทำความสะอาดและลองอีกครั้ง"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"เคลื่อนนิ้วเร็วเกินไป โปรดลองอีกครั้ง"</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"เคลื่อนนิ้วช้าเกินไป โปรดลองอีกครั้ง"</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"ข้อความแสดงข้อผิดพลาดการกระทำเฉพาะผู้ขาย 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"ไม่สามารถดำเนินการได้ โปรดลองอีกครั้ง"</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"ฮาร์ดแวร์ไม่พร้อมใช้งาน"</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"ไม่สามารถเก็บลายนิ้วมือได้ โปรดนำลายนิ้วมือที่มีอยู่ออก"</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"หมดเวลาใช้ลายนิ้วมือแล้ว โปรดลองอีกครั้ง"</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"หมดเวลาใช้ลายนิ้วมือแล้ว โปรดลองอีกครั้ง"</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"ข้อความแสดงข้อผิดพลาดเฉพาะผู้ขาย"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"อ่านการตั้งค่าการซิงค์แล้ว"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"อนุญาตให้แอปพลิเคชันอ่านการตั้งค่าการซิงค์ของบัญชี ตัวอย่างเช่น การอนุญาตนี้สามารถระบุได้ว่าแอปพลิเคชัน People ซิงค์กับบัญชีหรือไม่"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"สลับระหว่างเปิดและปิดการซิงค์"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"อนุญาตให้แอปพลิเคชันยืนยันว่าแพ็กเกจสามารถติดตั้งได้หรือไม่"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"เชื่อมโยงกับการยืนยันแพ็กเกจ"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"อนุญาตให้ผู้ใช้ส่งคำขอให้มีการยืนยันแพ็กเกจ ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"ยืนยันตัวกรองความตั้งใจ"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"อนุญาตให้แอปตรวจสอบว่ามีการยืนยันตัวกรองความตั้งใจหรือไม่"</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"เชื่อมโยงกับการยืนยันตัวกรองความตั้งใจ"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"อนุญาตให้แอปส่งคำขอการยืนยันตัวกรองความตั้งใจ แอปทั่วไปไม่จำเป็นต้องใช้"</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"เข้าถึงพอร์ตอนุกรม"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"อนุญาตให้ผู้ถือสามารถเข้าถึงพอร์ตอนุกรมโดยใช้ SerialManager API"</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"เข้าถึงผู้ให้บริการเนื้อหาจากภายนอก"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"ลดวัน"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"เพิ่มปี"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"ลดปี"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"เดือนที่แล้ว"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"เดือนหน้า"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"ยกเลิก"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"ลบ"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"ตัวเลื่อนหมุนระบุนาที"</string>
     <string name="select_hours" msgid="6043079511766008245">"เลือกชั่วโมง"</string>
     <string name="select_minutes" msgid="3974345615920336087">"เลือกนาที"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"ตารางเดือนของวัน"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"รายการปี"</string>
     <string name="select_day" msgid="7774759604701773332">"เลือกเดือนและวัน"</string>
     <string name="select_year" msgid="7952052866994196170">"เลือกปี"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"เลือก <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"ลบ <xliff:g id="KEY">%1$s</xliff:g> แล้ว"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g>ที่ทำงาน"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"หากต้องการเลิกตรึงหน้าจอนี้ แตะ \"กลับ\" และ \"ภาพรวม\" ค้างไว้พร้อมกัน"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 95a1480..6dccdec 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Pagtawag sa pamamagitan ng Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Hindi naipasa"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> pagkatapos ng <xliff:g id="TIME_DELAY">{2}</xliff:g> (na) segundo"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Pinapayagan ang app na kumuha ng mga larawan at video gamit ang camera. Pinapayagan ng pahintulot na ito ang app na gamitin ang camera anumang oras nang wala ng iyong kumpirmasyon."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"i-disable ang LED na tagapagpahiwatig kapag ginagamit ang camera"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Pinapayagan ang isang paunang na-install na application ng system na i-disable ang LED na tagapagpahiwatig ng paggamit sa camera."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"permanenteng huwag paganahin ang tablet"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"permanenteng i-disable ang TV"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"permanenteng huwag paganahin ang telepono"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Pinapayagan ang app na mag-invoke ng mga paraan upang magdagdag at mag-delete ng mga template ng fingerprint na magagamit."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"gamitina ng hardware ng fingerprint"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Pinapayagan ang app na gumamit ng hardware ng fingerprint para sa pagpapatotoo"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Hindi buo ang natukoy na fingerprint. Pakisubukang muli."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Hindi maproseso ang fingerprint. Pakisubukang muli."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Marumi ang sensor ng fingerprint. Pakilinis at subukang muli."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Masyadong mabilis ang paggalaw ng daliri. Pakisubukang muli."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Masyadong mabagal ang paggalaw ng daliri. Pakisubukang muli."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Mensahe ng error sa pagkuha na partikular sa vendor 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Hindi maproseso. Subukang muli."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Hindi available ang hardware."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Hindi maiimbak ang fingerprint. Mangyaring mag-alis ng umiiral nang fingerprint."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Nag-time out ang fingerprint. Subukang muli."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Nag-time out ang fingerprint. Subukang muli."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Mensahe ng error na partikular sa vendor."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"basahin ang mga setting ng sync"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Pinapayagan ang app na basahin ang mga setting ng pag-sync para sa isang account. Halimbawa, matutukoy nito kung naka-sync ang app na Mga Tao sa isang account."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"I-toggle on at off ang pag-sync"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Pinapayagan ang app na i-verify kung ang isang package ay nai-install."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"sumailalim sa taga-verify ng package"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Pinapayagan ang may-ari na gumawa ng mga kahilingan ng mga taga-verify ng package. Hindi kailanman dapat na kailanganin para sa normal na apps."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"i-verify ang intent filter"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Nagbibigay-daan sa app na suriin kung na-verify o hindi ang isang intent filter."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"isailalim sa verifier ng intent filter"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Nagbibigay-daan sa may-ari na gumawa ng mga kahilingan ng mga verifier ng intent filter. Hindi kailanman dapat kailanganin para sa mga normal na app."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"mag-access sa mga serial port"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Binibigyang-daan ang may-ari na mag-access ng mga serial port gamit ang SerialManager API."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"panlabas na mag-access ng mga provider ng nilalaman"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Bawasan ang araw"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Dagdagdan ang taon"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Bawasan ang taon"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Nakaraang buwan"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Susunod na buwan"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Kanselahin"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Tanggalin"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Pabilog na slider ng mga minuto"</string>
     <string name="select_hours" msgid="6043079511766008245">"Pumili ng mga oras"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Pumili ng mga minuto"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Grid ng mga araw ayon sa buwan"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Listahan ng taon"</string>
     <string name="select_day" msgid="7774759604701773332">"Pumili ng buwan at araw"</string>
     <string name="select_year" msgid="7952052866994196170">"Pumili ng taon"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"Napili ang <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"Tinanggal ang <xliff:g id="KEY">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> sa Trabaho"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Upang i-unpin ang screen na ito, pindutin nang matagal ang Bumalik at Overview nang sabay-sabay."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 373ef66..ae34ae5 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Kablosuz Çağrı"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Yönlendirilmedi"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="TIME_DELAY">{2}</xliff:g> saniye sonra <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Uygulamaya kamerayla fotoğraf ve video çekme izni verir. Bu izin, uygulamanın sizin onayınız olmadan istediği zaman kamerayı kullanmasına olanak sağlar."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"Kamera kullanımda iken iletim göstergesi LED\'ini devre dışı bırak"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Önceden yüklenmiş bir sistem uygulamasına kamera kullanım göstergesi LED\'ini devre dışı bırakma izni verir."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"tableti kalıcı olarak devre dışı bırak"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"TV\'yi kalıcı olarak devre dışı bırakma"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"telefonu tamamen devre dışı bırak"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Uygulamanın, kullanılacak parmak izi şablonlarını ekleme ve silme yöntemlerini başlatmasına izin verir."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"parmak izi donanımını kullanma"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Uygulamanın kimlik doğrulama için parmak izi donanımını kullanmasına izin verir."</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Parmak izinin tümü algılanamadı. Lütfen tekrar deneyin."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Parmak izi işlenemedi. Lütfen tekrar deneyin."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Parmak izi sensörü kirli. Lütfen temizleyin ve tekrar deneyin."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Parmak hareketi çok hızlıydı. Lütfen tekrar deneyin."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Parmak hareketi çok yavaştı. Lütfen tekrar deneyin."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Tedarikçi firmaya özel edinme hata iletisi 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"İşlenemedi. Tekrar deneyin."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Donanım yok."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Parmak izi depolanamıyor. Lütfen mevcut parmak izlerinden birini kaldırın."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Parmak izi için zaman aşımı oluştu. Tekrar deneyin."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Parmak izi için zaman aşımı oluştu. Tekrar deneyin."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Tedarikçi firmaya özel hata iletisi."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"senk. ayarlarını okuma"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Uygulamaya bir hesaba ait senkronizasyon ayarlarını okuma izni verir. Örneğin, bu izne sahip bir uygulama Kişiler uygulamasının bir hesapla senkronize olup olmadığını belirleyebilir."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"senkronizasyonu açma/kapatma"</string>
@@ -1119,6 +1138,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Uygulamaya, bir paketin yüklenebilir olduğunu doğrulama izni verir."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"paket doğrulayıcıya bağlan"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Cihazın sahibine, paket doğrulayıcıları için istek yapma izni verir. Normal uygulamalar için gerekli olmaz."</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"seri bağlantı noktalarına eriş"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"İzin sahibinin, SerialManager API\'sını kullanarak seri bağlantı noktalarına erişmesine olanak sağlar."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"içerik sağlayıcılara harici olarak eriş"</string>
@@ -1544,6 +1571,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Günü azalt"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Yılı artır"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Yılı azalt"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"İptal"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Sil"</string>
@@ -1794,11 +1825,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Dakika kaydırma çemberi"</string>
     <string name="select_hours" msgid="6043079511766008245">"Saati seçin"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Dakikayı seçin"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Ayın günleri tablosu"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Yıl listesi"</string>
     <string name="select_day" msgid="7774759604701773332">"Ayı ve günü seçin"</string>
     <string name="select_year" msgid="7952052866994196170">"Yılı seçin"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> seçildi"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> silindi"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> (İş)"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Bu ekranın sabitlemesini kaldırmak için Geri ve Genel Bakış\'a aynı anda dokunup basılı tutun."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index c3cfe6d..67e47e6 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -128,6 +128,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Дзвінок через Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: не переслано"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> після <xliff:g id="TIME_DELAY">{2}</xliff:g> сек."</string>
@@ -586,6 +587,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Дозволяє програмі фотографувати та знімати відео за допомогою камери. Такий дозвіл дає програмі змогу будь-коли використовувати камеру без вашого підтвердження."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"вимикати світлодіодний індикатор передавання, коли використовується камера"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Дозволяє попередньо встановленій системній програмі вимикати світлодіодний індикатор використання камери."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"остаточно вимкнути пристрій"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"назавжди вимкнути телевізор"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"остаточно вимкнути телефон"</string>
@@ -748,6 +751,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Увімкнути в додатку функції для додавання й видалення шаблонів цифрових відбитків."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"користуватися апаратним забезпеченням для цифрових відбитків"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Дозволити додатку використовувати апаратне забезпечення для автентифікації"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Відбиток розпізнано частково. Повторіть спробу."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Не вдалось обробити відбиток. Повторіть спробу."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Датчик відбитків забруднився. Очистьте його та повторіть спробу."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Ви забрали палець надто швидко. Повторіть спробу."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Ви забрали палець надто повільно. Повторіть спробу."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Повідомленя про помилку: 0. Не отримано відбиток постачальника"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Не вдалось обробити. Повторіть спробу."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Апаратне забезпечення недоступне."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Не вдалося зберегти відбиток. Видаліть наявний відбиток."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Час очікування відбитка минув. Повторіть спробу."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Час очікування відбитка минув. Повторіть спробу."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Повідомлення про помилку щодо відбитка постачальника."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"читати налаштування синхронізації"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Дозволяє програмі читати налаштування синхронізації для облікового запису, наприклад, визначати, чи програма Люди синхронізується з обліковим записом."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"вмикати й вимикати синхронізацію"</string>
@@ -1121,6 +1140,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Дозволяє програмі перевіряти можливість встановлення пакета."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"прив’язуватися до програми перевірки пакетів"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Дозволяє власникові робити запити на програми перевірки пакетів. Ніколи не застосовується для звичайних програм."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"перевірка фільтра команд"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Додаток може визначати, чи перевірено фільтр команд."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"пов’язувати з перевіркою фільтра команд"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Власник може перевірити фільтр команд. Ніколи не застосовується для звичайних додатків."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"отримувати доступ до послідовних портів"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Дозволяє власнику отримувати доступ до послідовних портів за допомогою API SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"отримув. ззовні доступ до постач. вмісту"</string>
@@ -1560,6 +1583,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"На день назад"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"На рік уперед"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"На рік назад"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Попередній місяць"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Наступний місяць"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Скасувати"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
@@ -1812,11 +1837,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Вибір хвилин на циферблаті"</string>
     <string name="select_hours" msgid="6043079511766008245">"Виберіть години"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Виберіть хвилини"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Вікно вибору дати"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Меню вибору року"</string>
     <string name="select_day" msgid="7774759604701773332">"Виберіть місяць і день"</string>
     <string name="select_year" msgid="7952052866994196170">"Виберіть рік"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"Вибрано: <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> видалено"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Робоча <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Щоб відкріпити екран, одночасно натисніть і утримуйте кнопки \"Назад\" та \"Огляд\"."</string>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index f315112..d787b7388 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"‏Wi-Fi کالنگ"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"‎%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> : فارورڈ نہیں کی گئی"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> بعد از <xliff:g id="TIME_DELAY">{2}</xliff:g> سیکنڈ"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"ایپ کو کیمرے سے تصویریں لینے اور ویڈیوز بنانے کی اجازت دیتا ہے۔ یہ اجازت ایپ کو آپ کی تصدیق کے بغیر کسی بھی وقت کیمرا استعمال کرنے کی اجازت دیتی ہے۔"</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"‏کیمرا استعمال میں ہونے پر ٹرانسمیٹ انڈیکیٹر LED کو غیر فعال کریں"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"‏پہلے سے انسٹال کردہ کسی سسٹم ایپلیکیشن کو کیمرا کے استعمال کے انڈیکیٹر LED کو غیر فعال کرنے کی اجازت دیتا ہے۔"</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"ٹیبلیٹ کو مستقل طور پر غیر فعال کریں"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"‏مستقل طور پر TV غیر فعال کریں"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"فون کو مستقل طور پر غیر فعال کریں"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"ایپ کو استعمال کیلئے فنگر پرنٹ کی تمثیلات شامل کرنے اور حذف کرنے کیلئے طریقوں کو کالعدم قرار دینے کی اجازت دیتا ہے۔"</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"فنگر پرنٹ ہارڈ ویئر استعمال کریں"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"ایپ کو توثیق کیلئے فنگر پرنٹ ہارڈ ویئر استعمال کرنے کی اجازت دیتا ہے"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"جزوی فنگر پرنٹ کی شناخت ہوئی۔ براہ کرم دوبارہ کوشش کریں۔"</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"فنگر پرنٹ پر کارروائی نہیں کی جا سکی۔ براہ کرم دوبارہ کوشش کریں۔"</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"فنگر پرنٹ سینسر گندا ہے۔ براہ کرم صاف کریں اور دوبارہ کوشش کریں۔"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"انگلی کو کافی تیزی سے ہٹا لیا گیا۔ براہ کرم دوبارہ کوشش کریں۔"</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"انگلی کو بہت آہستہ ہٹا لیا گیا۔ براہ کرم دوبارہ کوشش کریں۔"</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"وینڈر کیلئے مخصوص حصول کی خرابی کا پیغام 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"کارروائی کرنے سے قاصر ہے۔ دوبارہ کوشش کریں۔"</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"ہارڈ ویئر دستیاب نہیں ہے۔"</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"فنگر پرنٹ اسٹور نہیں کیا جا سکتا ہے۔ براہ کرم ایک موجودہ فنگر پرنٹ ہٹائیں۔"</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"فنگر پرنٹ کی میعاد ختم ہوگئی۔ دوبارہ کوشش کریں۔"</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"فنگر پرنٹ کی میعاد ختم ہوگئی۔ دوبارہ کوشش کریں۔"</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"وینڈر کیلئے مخصوص خرابی کا پیغام۔"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"مطابقت پذیری کی ترتیبات پڑھیں"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"‏ایپ کو کسی اکاؤنٹ کیلئے مطابقت پذیری کی ترتیبات پڑھنے کی اجازت دیتا ہے۔ مثلا، یہ تعین کرسکتا ہے کہ آیا People ایپ کسی اکاؤنٹ کے ساتھ مطابقت پذیر ہے۔"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"مطابقت پذیری آن اور آف ٹوگل کریں"</string>
@@ -1119,6 +1138,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"ایپ کو اس بات کی توثیق کرنے کی اجازت دیتا ہے کہ پیکج انسٹال کرنے قابل ہے۔"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"پیکج کے ایک توثیق کار کے پابند بنیں"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"حامل کو پیکیج کے توثیق کاروں کی درخواستیں کرنے دیتا ہے۔ عام ایپس کیلئے کبھی بھی اس کی ضرورت نہيں ہونی چاہئے۔"</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"سیریل پورٹس تک رسائی حاصل کریں"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"‏SerialManager API کا استعمال کرکے حامل کو سیریل پورٹز تک رسائی دیتا ہے۔"</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"مواد فراہم کنندگان تک خارجی رسائی حاصل کریں"</string>
@@ -1544,6 +1571,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"دن گھٹائیں"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"سال بڑھائیں"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"سال گھٹائیں"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"منسوخ کریں"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"حذف کریں"</string>
@@ -1794,11 +1825,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"منٹس سرکلر سلائیڈر"</string>
     <string name="select_hours" msgid="6043079511766008245">"گھنٹے منتخب کریں"</string>
     <string name="select_minutes" msgid="3974345615920336087">"منٹ منتخب کریں"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"دنوں کا ماہ کا گرڈ"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"سال کی فہرست"</string>
     <string name="select_day" msgid="7774759604701773332">"ماہ اور دن منتخب کریں"</string>
     <string name="select_year" msgid="7952052866994196170">"سال منتخب کریں"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> کو منتخب کیا گیا"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> کو حذف کر دیا گیا"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"دفتر <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"اس اسکرین سے پن ہٹانے کیلئے، واپس جائیں اور مجموعی جائزہ کو ایک ساتھ ٹچ کریں اور دبا کر رکھیں۔"</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index 78bad61..e79f52d 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi qo‘ng‘iroq"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Yo‘naltirilmadi"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>  <xliff:g id="TIME_DELAY">{2}</xliff:g> soniyadan so‘ng"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Ilovaga kameradan foydalanib rasm va videoga olishga ruxsat beradi. Bu ruxsat ilovaga sizdan tasdiqlashni so‘ramasdan kameradan foydalanishga imkon beradi."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"LED ko‘rsatkichni kamera faolligida boshqarish imkoniyatini o‘chirish"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Zavodda o‘rnatilgan tizim dasturiga kamerani o‘chirish uchun LED ko‘rsatkichidan foydalanish imkonini beradi."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"doimo planshetni o‘chirish"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"televizorni butunlay o‘chirib qo‘yish"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"doimo telefonni o‘chirish"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Ilova foydalanish uchun barmoq izi namunalarini qo‘shish va o‘chirish usullarini qo‘llashi mumkin."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"barmoq izi sensoridan foydalanish"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Ilova haqiqiylikni tekshirish uchun barmoq izi sensoridan foydalanishi mumkin"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Barmoq izi qisman aniqlandi. Qayta urinib ko‘ring."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Barmoq izi aniqlanmadi. Qayta urinib ko‘ring."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Barmoq izi sensori kirlangan. Uni tozalab, keyin qayta urinib ko‘ring."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Barmoq juda tez harakatlandi. Qayta urinib ko‘ring."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Barmoq juda sekin harakatlandi. Qayta urinib ko‘ring."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Sotuvchidan barmoq izini olishda sodir bo‘ladigan xatolik xabari 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Barmoq izini aniqlab bo‘lmadi. Qayta urinib ko‘ring."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Barmoq izi sensoridan foydalanib bo‘lmaydi."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Barmoq izini saqlab bo‘lmadi. Mavjud barmoq izlaridan birini o‘chirib tashlang."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Barmoq izini aniqlash vaqti tugab qoldi. Qayta urinib ko‘ring."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Barmoq izini aniqlash vaqti tugab qoldi. Qayta urinib ko‘ring."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Xizmat bilan bog‘liq xatolik xabari."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"sinx-sh sozlamalarini o‘qish"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Ilovaga hisobning sinxronlash sozlamalarini o‘qish uchun ruxsat beradi. Masalan, bu \"Odamlar\" ilovasi hisob bilan sinxronlangan yoki aksini aniqlay oladi."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"sinx.ni yoqish/o‘chirish"</string>
@@ -1119,6 +1138,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Ilova paketlarni o‘rnatish imkoniyatini tekshirishi mumkin."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"paketni tekshirgichga bog‘lash"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Ilova paketlarni tekshirishni talab qilishi mumkin. Oddiy ilovalar uchun talab qilinmaydi."</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"serial portlarga kirish"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Foydalanuvchiga SerialManager API’dan foydalanib, serial portlarga kirishga ruxsat beradi."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"kontent provayderiga tashqi ruxsat"</string>
@@ -1544,6 +1571,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Kunni kamaytirish"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Yilni ko‘paytirish"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Yilni kamaytirish"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Bekor qilish"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"O‘chirish"</string>
@@ -1794,11 +1825,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Doiradan daqiqani tanlang"</string>
     <string name="select_hours" msgid="6043079511766008245">"Soatlarni tanlash"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Daqiqalarni tanlash"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Oy kunlari (jadval ko‘rinishida)"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Yil ro‘yxati"</string>
     <string name="select_day" msgid="7774759604701773332">"Oy va kunni tanlash"</string>
     <string name="select_year" msgid="7952052866994196170">"Yilni tanlash"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"<xliff:g id="ITEM">%1$s</xliff:g> tanlandi"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> o‘chirildi"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Ish <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Ushbu ekrandan chiqish uchun “Orqaga” va “Umumiy nazar” tugmalarini bir vaqtda bosib turing."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index a2ebfd4..07f291a 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Gọi qua Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Không được chuyển tiếp"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> sau <xliff:g id="TIME_DELAY">{2}</xliff:g> giây"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Cho phép ứng dụng chụp ảnh và quay video bằng máy ảnh. Quyền này cho phép ứng dụng sử dụng máy ảnh bất kỳ lúc nào mà không cần sự xác nhận của bạn."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"vô hiệu hóa tính năng phát đèn LED chỉ báo khi máy ảnh đang được sử dụng"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Cho phép cài đặt trước ứng dụng hệ thống để vô hiệu hóa việc máy ảnh sử dụng đèn LED chỉ báo."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"vô hiệu hóa vĩnh viễn máy tính bảng"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"tắt vĩnh viễn TV"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"vĩnh viễn vô hiệu hóa điện thoại"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Cho phép ứng dụng gọi các phương pháp để thêm và xóa các mẫu vân tay để sử dụng."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"sử dụng phần cứng vân tay"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Cho phép ứng dụng sử dụng phần cứng vân tay để xác thực"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Đã phát hiện được một phần vân tay. Vui lòng thử lại."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Không thể xử lý vân tay. Vui lòng thử lại."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Cảm biến vân tay bị bẩn. Hãy làm sạch và thử lại."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Ngón tay đã di chuyển quá nhanh. Vui lòng thử lại."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Ngón tay đã di chuyển quá chậm. Vui lòng thử lại."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Thông báo lỗi lấy vân tay từ người bán cụ thể 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Không thể xử lý. Hãy thử lại."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Phần cứng không có sẵn."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Không thể lưu vân tay. Vui lòng xóa vân tay hiện có."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Đã hết thời gian chờ vân tay. Hãy thử lại."</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Đã hết thời gian chờ vân tay. Hãy thử lại."</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Thông báo lỗi cho người bán cụ thể."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"đọc cài đặt đồng bộ hóa"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Cho phép ứng dụng đọc cài đặt đồng bộ hóa cho tài khoản. Ví dụ: việc này có thể xác định liệu ứng dụng Mọi người đã được đồng bộ hóa với tài khoản chưa."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"chuyển đổi bật và tắt đồng bộ hóa"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Cho phép ứng dụng xác minh gói có thể cài đặt."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"liên kết với trình xác minh gói"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Cho phép chủ sở hữu yêu cầu trình xác minh gói. Không cần thiết cho các ứng dụng thông thường."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"xác minh bộ lọc theo mục đích"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Cho phép ứng dụng kiểm tra xem bộ lọc theo mục đích đã được xác minh hay chưa."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"liên kết với trình xác minh bộ lọc theo mục đích"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Cho phép chủ sở hữu yêu cầu trình xác minh bộ lọc theo mục đích. Không cần thiết cho các ứng dụng thông thường."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"truy cập cổng nối tiếp"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Cho phép chủ sở hữu truy cập cổng nối tiếp sử dụng API SerialManager."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"truy cập vào nhà cung cấp nội dung từ bên ngoài"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Giảm ngày"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Tăng năm"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Giảm năm"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Tháng trước"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Tháng sau"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Hủy"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Xóa"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Thanh trượt phút hình tròn"</string>
     <string name="select_hours" msgid="6043079511766008245">"Chọn giờ"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Chọn phút"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Lưới ngày theo tháng"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Danh sách năm"</string>
     <string name="select_day" msgid="7774759604701773332">"Chọn tháng và ngày"</string>
     <string name="select_year" msgid="7952052866994196170">"Chọn năm"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"Đã chọn <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"Đã xóa <xliff:g id="KEY">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"<xliff:g id="LABEL">%1$s</xliff:g> làm việc"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Để bỏ khóa màn hình này, chạm và giữ Quay lại và Tổng quan cùng lúc."</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 269afba..e932190 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"WLAN 通话"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:无法转接"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="TIME_DELAY">{2}</xliff:g>秒后<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -203,8 +204,7 @@
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"已开启飞行模式"</string>
     <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"未开启飞行模式"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"设置"</string>
-    <!-- no translation found for global_action_assist (3892832961594295030) -->
-    <skip />
+    <string name="global_action_assist" msgid="3892832961594295030">"助理"</string>
     <string name="global_action_voice_assist" msgid="7751191495200504480">"语音助理"</string>
     <string name="global_action_lockdown" msgid="8751542514724332873">"立即锁定"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
@@ -585,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"允许该应用使用相机拍摄照片和视频。此权限可让该应用随时使用相机,而无需您的确认。"</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"在相机使用过程中停用传输指示灯 LED"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"允许预装的系统应用禁止相机使用指示灯 LED。"</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"永久停用平板电脑"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"永久停用电视"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"永久停用手机"</string>
@@ -747,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"允许该应用调用方法来添加和删除可用的指纹模板。"</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"使用指纹硬件"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"允许该应用使用指纹硬件进行身份验证"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"仅检测到部分指纹,请重试。"</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"无法处理指纹,请重试。"</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指纹传感器有脏污。请擦拭干净,然后重试。"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"手指移动太快,请重试。"</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"手指移动太慢,请重试。"</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"针对供应商的指纹获取错误消息 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"无法处理指纹,请重试。"</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"硬件无法使用。"</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"无法存储指纹。请移除一个现有的指纹。"</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"指纹录入操作超时,请重试。"</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"指纹录入操作超时,请重试。"</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"针对供应商的错误消息。"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"读取同步设置"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"允许该应用读取某个帐户的同步设置。例如,此权限可确定“联系人”应用是否与某个帐户同步。"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"启用和停用同步"</string>
@@ -1120,6 +1138,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"允许应用验证软件包是否可安装。"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"绑定到软件包验证程序"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"允许应用申请使用软件包验证程序。普通应用绝不需要此权限。"</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"访问串行端口"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"允许应用通过SerialManager API使用串行端口。"</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"从外部访问内容提供程序"</string>
@@ -1545,6 +1571,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"减小日期值"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"增大年份值"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"减小年份值"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"取消"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete"</string>
@@ -1795,11 +1825,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"分钟转盘"</string>
     <string name="select_hours" msgid="6043079511766008245">"选择小时"</string>
     <string name="select_minutes" msgid="3974345615920336087">"选择分钟"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"按月份划分的日期网格"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"年份列表"</string>
     <string name="select_day" msgid="7774759604701773332">"选择月份和日期"</string>
     <string name="select_year" msgid="7952052866994196170">"选择年份"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"已选择<xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"已删除<xliff:g id="KEY">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"工作<xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"要取消固定此屏幕,请同时触摸并按住“返回”和“概览”按钮。"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index a62cf11..5454bd2 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi 通話"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:尚未轉接"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> 於 <xliff:g id="TIME_DELAY">{2}</xliff:g> 秒後轉接"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"允許應用程式使用相機拍照和錄影。這項權限允許應用程式隨時使用相機,而不需經您確認。"</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"使用相機時停用傳輸指示燈"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"允許預先安裝的系統應用程式停用相機指示燈。"</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"永久停用平板電腦"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"永遠停用電視"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"永久停用手機"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"允許應用程式調用加入和刪除指紋模板的方法以供使用。"</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"使用指紋硬件"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"允許應用程式使用指紋硬件驗證"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"只偵測到部分指紋。請再試一次。"</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"無法處理指紋。請再試一次。"</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指紋感應器不乾淨。請清潔後再試一次。"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"手指移動太快。請再試一次。"</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"手指移動太慢。請再試一次。"</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"供應商專用採集錯誤訊息 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"無法處理。請再試一次。"</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"硬件無法使用。"</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"指紋無法儲存。請移除現有指紋。"</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"指紋已逾時。請再試一次。"</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"指紋已逾時。請再試一次。"</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"供應商專用錯誤訊息。"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"讀取同步處理設定"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"允許應用程式讀取帳戶的同步設定,例如確定「通訊錄」應用程式是否和某個帳戶保持同步。"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"開啟和關閉同步功能"</string>
@@ -1119,6 +1138,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"允許應用程式驗證套件是否可安裝。"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"繫結至套件驗證程序"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"允許應用程式要求驗證套件 (不建議一般應用程式使用)。"</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"接入串列通訊埠"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"允許應用程式使用 SerialManager API 接入串列通訊埠。"</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"從外部存取內容供應商"</string>
@@ -1544,6 +1571,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"減少日數"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"增加年數"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"減少年數"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"取消"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"刪除"</string>
@@ -1794,11 +1825,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"分鐘環形滑桿"</string>
     <string name="select_hours" msgid="6043079511766008245">"選取小時"</string>
     <string name="select_minutes" msgid="3974345615920336087">"選取分鐘"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"顯示每日的月曆方格"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"年份清單"</string>
     <string name="select_day" msgid="7774759604701773332">"選取月份和日期"</string>
     <string name="select_year" msgid="7952052866994196170">"選取年份"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"已選取<xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"<xliff:g id="KEY">%1$s</xliff:g> 已刪除"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"公司<xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"如要取消固定這個畫面,請同時輕觸並按住 [返回] 和 [概覽]。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 5d1821a..7e7b0d3 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Wi-Fi 通話"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:未轉接"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="TIME_DELAY">{2}</xliff:g> 秒後 <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"允許應用程式使用相機拍照和錄影。這項權限可讓應用程式隨時使用相機,而不需請求您進行確認。"</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"使用攝影機時停用傳輸指示器 LED"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"允許預先安裝的系統應用程式停用攝影機指示器 LED。"</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"永久停用平板電腦"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"永久停用電視"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"永久停用電話"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"允許應用程式呼叫方法來新增及移除可用的指紋範本"</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"使用指紋硬體"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"允許應用程式使用指紋硬體進行驗證"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"僅偵測到部分指紋,請再試一次。"</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"無法處理指紋,請再試一次。"</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指紋感應器有髒汙。請清潔感應器,然後再試一次。"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"手指移動速度過快,請再試一次。"</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"手指移動速度過慢,請再試一次。"</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"供應商自訂的指紋擷取錯誤訊息 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"無法辨識指紋,請再試一次。"</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"硬體無法使用。"</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"無法儲存指紋,請先移除現有指紋。"</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"指紋處理作業逾時,請再試一次。"</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"指紋處理作業逾時,請再試一次。"</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"供應商自訂的錯誤訊息。"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"讀取同步處理設定"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"允許應用程式讀取帳戶的同步處理設定,例如判斷「使用者」應用程式是否和某個帳戶進行同步處理。"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"開啟及關閉同步功能"</string>
@@ -1119,6 +1138,14 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"允許應用程式驗證是否可安裝特定套件。"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"繫結至套件驗證程序"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"允許應用程式要求驗證套件 (一般應用程式不需使用)。"</string>
+    <!-- no translation found for permlab_intentFilterVerificationAgent (1135788294400437497) -->
+    <skip />
+    <!-- no translation found for permdesc_intentFilterVerificationAgent (5853902808424716312) -->
+    <skip />
+    <!-- no translation found for permlab_bindIntentFilterVerifier (8567268159430779210) -->
+    <skip />
+    <!-- no translation found for permdesc_bindIntentFilterVerifier (681128728719578778) -->
+    <skip />
     <string name="permlab_serialPort" msgid="546083327654631076">"存取序列埠"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"允許應用程式使用 SerialManager API 存取序列埠。"</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"從外部存取內容供應端"</string>
@@ -1544,6 +1571,10 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"減少日數"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"增加年數"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"減少年數"</string>
+    <!-- no translation found for date_picker_prev_month_button (2858244643992056505) -->
+    <skip />
+    <!-- no translation found for date_picker_next_month_button (5559507736887605055) -->
+    <skip />
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt 鍵"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"取消"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Delete 鍵"</string>
@@ -1794,11 +1825,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"分鐘數環狀滑桿"</string>
     <string name="select_hours" msgid="6043079511766008245">"選取小時數"</string>
     <string name="select_minutes" msgid="3974345615920336087">"選取分鐘數"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"日期網格 (按月顯示)"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"年份清單"</string>
     <string name="select_day" msgid="7774759604701773332">"選取月份和日期"</string>
     <string name="select_year" msgid="7952052866994196170">"選取年份"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"已選取 <xliff:g id="ITEM">%1$s</xliff:g>"</string>
     <string name="deleted_key" msgid="7659477886625566590">"已刪除 <xliff:g id="KEY">%1$s</xliff:g>"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"公司<xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"如要取消固定這個畫面,請同時輕觸並按住返回按鈕和總覽按鈕。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 4c3a400..4abcfea 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -126,6 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"Ukushaya kwe-Wi-Fi"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Akudlulisiwe"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g> emuva kwamasekhondi angu-<xliff:g id="TIME_DELAY">{2}</xliff:g>"</string>
@@ -584,6 +585,8 @@
     <string name="permdesc_camera" msgid="8497216524735535009">"Ivumela uhlelo lokusebenza ukuthatha izithombe namavidiyo ngekhamera. Le mvume ivumela uhlelo lokusebenza ukusebenzisa ikhamera nganoma isiphi isikhathi ngaphandle kwemvume yakho."</string>
     <string name="permlab_cameraDisableTransmitLed" msgid="2651072630501126222">"khubaza i-LED yesikhombi sokudlulisa uma ikhamera isebenza"</string>
     <string name="permdesc_cameraDisableTransmitLed" msgid="4764585465480295341">"Ivumela isistimu efakwe ngaphambili yohlelo lokusebenza ukuze ikhubaze i-LED yesikhombi sokusetshenziswa kwekhamera."</string>
+    <!-- no translation found for permdesc_cameraSendSystemEvent (8642231538552298107) -->
+    <skip />
     <string name="permlab_brick" product="tablet" msgid="2961292205764488304">"vimbela ngokuphelele ithebhulethi"</string>
     <string name="permlab_brick" product="tv" msgid="4912674222121249410">"khubaza unaphakade i-TV"</string>
     <string name="permlab_brick" product="default" msgid="8337817093326370537">"ngokwaphakade vimbela ifoni"</string>
@@ -746,6 +749,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"Ivumela uhlelo lokusebenza ukuthi libuyisele izindlela zokungeza nokususa izifanekiso zezigxivizo zeminwe ngokusetshenziswa."</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"sebenzisa izingxenyekazi zekhompyutha zezigxivizo zeminwe"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"Ivumela uhlelo lokusebenza ukuthi lusebenzise izingxenyekazi zekhompyutha zezigxivizo zeminwe ukuze kuqinisekiswe"</string>
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"Izigxivizo zeminwe ezincane zitholiwe. Sicela uzame futhi."</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"Ayikwazanga ukucubungula izigxivizo zeminwe. Sicela uzame futhi."</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"Inzwa yezigxivizo zeminwe ingcolile. Sicela uyihlanze uphinde uzame futhi."</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"Umunwe uhanjiswe ngokushesha kakhulu. Sicela uzame futhi."</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"Umunwe uhanjiswe kancane kakhulu. Sicela uzame futhi."</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"Umlayezo wephutha wokutholwa okucacisiwe komthengisi ongu-0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"Ayikwazi ukucubungula. Zama futhi."</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"Izingxenyekazi zekhompuyutha azitholakali."</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Izigxivizo zeminwe azikwazi ukugcinwa. Sicela ususe izigxivizo zeminwe ezikhona."</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Kufinyelelwe isikhathi sokuvala sezigxivizo zeminwe. Zama futhi"</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"Kufinyelelwe isikhathi sokuvala sezigxivizo zeminwe. Zama futhi"</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"Umlayezo wephutha ocacile womthengisi."</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"funda izilungiselelo zokuvumelanisa"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"Ivumela uhlelo lokusebenza ukufunda izilungiselelo zokuvumelanisa ze-akhawunti. Isibonelo, lokhu kungacacisa ukuthi noma ngabe uhlelo lokusebenza le-People livumelanisiwe ne-akhawunti."</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"shintsha phakathi kokuvula kanye nokucisha ukuvumelanisa"</string>
@@ -1119,6 +1138,10 @@
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Ivumela ukuthi isisetshenziswa siqinisekise ukuthi ngabe iphakheji iyafakeka."</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"bopha okokuqinisekisa iphakheji"</string>
     <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Ivumela umnikazi ukuthi enze izicelo zezinsiza eziqinisekisa iphakheji. Akumele kudingeke ekusetshenzisweni okujwayelekile."</string>
+    <string name="permlab_intentFilterVerificationAgent" msgid="1135788294400437497">"qinisekisa isihlungi esihlosiwe"</string>
+    <string name="permdesc_intentFilterVerificationAgent" msgid="5853902808424716312">"Ivumela uhlelo lokusebenza ukuthi lihlole uma ngabe isihlungi esihlosiwe siqinisekisiwe noma asiqinisekisiwe."</string>
+    <string name="permlab_bindIntentFilterVerifier" msgid="8567268159430779210">"hlanganisa kusiqinisekisi esihlosiwe sesihlungi"</string>
+    <string name="permdesc_bindIntentFilterVerifier" msgid="681128728719578778">"Ivumela umbambi ukuthi enze izicelo zeziqinisekisi ezihlosiwe zesihlungi. Akumele kudingelwe izinhlelo zokusebenza ezijwayelekile."</string>
     <string name="permlab_serialPort" msgid="546083327654631076">"finyelela kuma- serial port"</string>
     <string name="permdesc_serialPort" msgid="2991639985224598193">"Ivumela umnikai ukuthi athole inombolo ye-serial ukue angene kwiindawo ze-serial esebenzisa i-SerialManager API."</string>
     <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"finyelela abahlinzeki bokuqukethwe ngaphandle"</string>
@@ -1544,6 +1567,8 @@
     <string name="date_picker_decrement_day_button" msgid="4131881521818750031">"Yehlisa usuku"</string>
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"Khulisa unyaka"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"Yehlisa unyaka"</string>
+    <string name="date_picker_prev_month_button" msgid="2858244643992056505">"Inyanga edlule"</string>
+    <string name="date_picker_next_month_button" msgid="5559507736887605055">"Inyanga ezayo"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"i-ALT"</string>
     <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"Khansela"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"Susa"</string>
@@ -1794,11 +1819,8 @@
     <string name="minute_picker_description" msgid="8606010966873791190">"Amaminithi weslayidi esiyindingilizi"</string>
     <string name="select_hours" msgid="6043079511766008245">"Khetha amahora"</string>
     <string name="select_minutes" msgid="3974345615920336087">"Khetha amaminithi"</string>
-    <string name="day_picker_description" msgid="8990847925961297968">"Igridi yenyanga yezinsuku"</string>
-    <string name="year_picker_description" msgid="5524331207436052403">"Uhlu lonyaka"</string>
     <string name="select_day" msgid="7774759604701773332">"Khetha inyanga nosuku"</string>
     <string name="select_year" msgid="7952052866994196170">"Khetha unyaka"</string>
-    <string name="item_is_selected" msgid="949687401682476608">"I-<xliff:g id="ITEM">%1$s</xliff:g> ekhethiwe"</string>
     <string name="deleted_key" msgid="7659477886625566590">"I-<xliff:g id="KEY">%1$s</xliff:g> isusiwe"</string>
     <string name="managed_profile_label_badge" msgid="2355652472854327647">"Umsebenzi <xliff:g id="LABEL">%1$s</xliff:g>"</string>
     <string name="lock_to_app_toast" msgid="7570091317001980053">"Ukuze ususe ukuphina kulesi sikrini, thinta uphinde ubambe i-Emuva ne-Buka konke ngesikhathi esisodwa."</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 15797dd..3945222 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1036,7 +1036,7 @@
         <attr name="colorSwitchThumbNormal" format="color" />
 
         <!-- @hide The background used by framework controls. -->
-        <attr name="controlBackground" format="color" />
+        <attr name="controlBackground" format="reference" />
 
         <!-- The color applied to the edge effect on scrolling containers. -->
         <attr name="colorEdgeEffect" format="color" />
@@ -2633,6 +2633,70 @@
             <enum name="paddedBounds" value="3" />
         </attr>
 
+        <!-- Defines the drawable to draw over the content. This can be used as an overlay.
+             The foreground drawable participates in the padding of the content if the gravity
+             is set to fill. -->
+        <attr name="foreground" format="reference|color" />
+        <!-- Defines the gravity to apply to the foreground drawable. The gravity defaults
+             to fill. -->
+        <attr name="foregroundGravity">
+            <!-- Push object to the top of its container, not changing its size. -->
+            <flag name="top" value="0x30" />
+            <!-- Push object to the bottom of its container, not changing its size. -->
+            <flag name="bottom" value="0x50" />
+            <!-- Push object to the left of its container, not changing its size. -->
+            <flag name="left" value="0x03" />
+            <!-- Push object to the right of its container, not changing its size. -->
+            <flag name="right" value="0x05" />
+            <!-- Place object in the vertical center of its container, not changing its size. -->
+            <flag name="center_vertical" value="0x10" />
+            <!-- Grow the vertical size of the object if needed so it completely fills its container. -->
+            <flag name="fill_vertical" value="0x70" />
+            <!-- Place object in the horizontal center of its container, not changing its size. -->
+            <flag name="center_horizontal" value="0x01" />
+            <!-- Grow the horizontal size of the object if needed so it completely fills its container. -->
+            <flag name="fill_horizontal" value="0x07" />
+            <!-- Place the object in the center of its container in both the vertical and horizontal axis, not changing its size. -->
+            <flag name="center" value="0x11" />
+            <!-- Grow the horizontal and vertical size of the object if needed so it completely fills its container. -->
+            <flag name="fill" value="0x77" />
+            <!-- Additional option that can be set to have the top and/or bottom edges of
+                 the child clipped to its container's bounds.
+                 The clip will be based on the vertical gravity: a top gravity will clip the bottom
+                 edge, a bottom gravity will clip the top edge, and neither will clip both edges. -->
+            <flag name="clip_vertical" value="0x80" />
+            <!-- Additional option that can be set to have the left and/or right edges of
+                 the child clipped to its container's bounds.
+                 The clip will be based on the horizontal gravity: a left gravity will clip the right
+                 edge, a right gravity will clip the left edge, and neither will clip both edges. -->
+            <flag name="clip_horizontal" value="0x08" />
+        </attr>
+        <!-- Defines whether the foreground drawable should be drawn inside the padding.
+             This property is turned on by default. -->
+        <attr name="foregroundInsidePadding" format="boolean" />
+        <!-- Tint to apply to the foreground. -->
+        <attr name="foregroundTint" format="color" />
+        <!-- Blending mode used to apply the foreground tint. -->
+        <attr name="foregroundTintMode">
+            <!-- The tint is drawn on top of the drawable.
+                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+            <enum name="src_over" value="3" />
+            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+                 color channels are thrown out. [Sa * Da, Sc * Da] -->
+            <enum name="src_in" value="5" />
+            <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+            <enum name="src_atop" value="9" />
+            <!-- Multiplies the color and alpha channels of the drawable with those of
+                 the tint. [Sa * Da, Sc * Dc] -->
+            <enum name="multiply" value="14" />
+            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+            <enum name="screen" value="15" />
+            <!-- Combines the tint and drawable color and alpha channels, clamping the
+                 result to valid color values. Saturate(S + D) -->
+            <enum name="add" value="16" />
+        </attr>
+
     </declare-styleable>
 
     <!-- Attributes that can be assigned to a tag for a particular View. -->
@@ -3366,72 +3430,9 @@
         <attr name="padding" />
     </declare-styleable>
     <declare-styleable name="FrameLayout">
-        <!-- Defines the drawable to draw over the content. This can be used as an overlay.
-             The foreground drawable participates in the padding of the content if the gravity
-             is set to fill. -->
-        <attr name="foreground" format="reference|color" />
-        <!-- Defines the gravity to apply to the foreground drawable. The gravity defaults
-             to fill. -->
-        <attr name="foregroundGravity">
-            <!-- Push object to the top of its container, not changing its size. -->
-            <flag name="top" value="0x30" />
-            <!-- Push object to the bottom of its container, not changing its size. -->
-            <flag name="bottom" value="0x50" />
-            <!-- Push object to the left of its container, not changing its size. -->
-            <flag name="left" value="0x03" />
-            <!-- Push object to the right of its container, not changing its size. -->
-            <flag name="right" value="0x05" />
-            <!-- Place object in the vertical center of its container, not changing its size. -->
-            <flag name="center_vertical" value="0x10" />
-            <!-- Grow the vertical size of the object if needed so it completely fills its container. -->
-            <flag name="fill_vertical" value="0x70" />
-            <!-- Place object in the horizontal center of its container, not changing its size. -->
-            <flag name="center_horizontal" value="0x01" />
-            <!-- Grow the horizontal size of the object if needed so it completely fills its container. -->
-            <flag name="fill_horizontal" value="0x07" />
-            <!-- Place the object in the center of its container in both the vertical and horizontal axis, not changing its size. -->
-            <flag name="center" value="0x11" />
-            <!-- Grow the horizontal and vertical size of the object if needed so it completely fills its container. -->
-            <flag name="fill" value="0x77" />
-            <!-- Additional option that can be set to have the top and/or bottom edges of
-                 the child clipped to its container's bounds.
-                 The clip will be based on the vertical gravity: a top gravity will clip the bottom
-                 edge, a bottom gravity will clip the top edge, and neither will clip both edges. -->
-            <flag name="clip_vertical" value="0x80" />
-            <!-- Additional option that can be set to have the left and/or right edges of
-                 the child clipped to its container's bounds.
-                 The clip will be based on the horizontal gravity: a left gravity will clip the right
-                 edge, a right gravity will clip the left edge, and neither will clip both edges. -->
-            <flag name="clip_horizontal" value="0x08" />
-        </attr>
-        <!-- Defines whether the foreground drawable should be drawn inside the padding.
-             This property is turned on by default. -->
-        <attr name="foregroundInsidePadding" format="boolean" />
         <!-- Determines whether to measure all children or just those in
              the VISIBLE or INVISIBLE state when measuring. Defaults to false. -->
         <attr name="measureAllChildren" format="boolean" />
-        <!-- Tint to apply to the foreground. -->
-        <attr name="foregroundTint" format="color" />
-        <!-- Blending mode used to apply the foreground tint. -->
-        <attr name="foregroundTintMode">
-            <!-- The tint is drawn on top of the drawable.
-                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
-            <enum name="src_over" value="3" />
-            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
-                 color channels are thrown out. [Sa * Da, Sc * Da] -->
-            <enum name="src_in" value="5" />
-            <!-- The tint is drawn above the drawable, but with the drawable’s alpha
-                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
-            <enum name="src_atop" value="9" />
-            <!-- Multiplies the color and alpha channels of the drawable with those of
-                 the tint. [Sa * Da, Sc * Dc] -->
-            <enum name="multiply" value="14" />
-            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
-            <enum name="screen" value="15" />
-            <!-- Combines the tint and drawable color and alpha channels, clamping the
-                 result to valid color values. Saturate(S + D) -->
-            <enum name="add" value="16" />
-        </attr>
     </declare-styleable>
     <declare-styleable name="ExpandableListView">
         <!-- Indicator shown beside the group View. This can be a stateful Drawable. -->
@@ -4282,6 +4283,15 @@
         <attr name="letterSpacing" />
         <!-- Font feature settings. -->
         <attr name="fontFeatureSettings" />
+        <!-- Break strategy (control over paragraph layout). -->
+        <attr name="breakStrategy">
+            <!-- Line breaking uses simple strategy. -->
+            <enum name="simple" value="0" />
+            <!-- Line breaking uses high-quality strategy, including hyphenation. -->
+            <enum name="high_quality" value="1" />
+            <!-- Line breaking stratgegy balances line lengths. -->
+            <enum name="balanced" value="2" />
+        </attr>
     </declare-styleable>
     <declare-styleable name="TextViewAppearance">
         <!-- Base text color, typeface, size, and style. -->
@@ -4443,46 +4453,39 @@
     </declare-styleable>
 
     <declare-styleable name="DatePicker">
-        <!-- The first year (inclusive), for example "1940".
-             {@deprecated Use minDate instead.} -->
-        <attr name="startYear" format="integer" />
-        <!-- The last year (inclusive), for example "2010".
-             {@deprecated Use maxDate instead.} -->
-        <attr name="endYear" format="integer" />
-        <!-- Whether the spinners are shown. -->
-        <attr name="spinnersShown" format="boolean" />
-        <!-- Whether the calendar view is shown. -->
-        <attr name="calendarViewShown" format="boolean" />
+        <!-- The first day of week according to {@link java.util.Calendar}. -->
+        <attr name="firstDayOfWeek" />
         <!-- The minimal date shown by this calendar view in mm/dd/yyyy format. -->
         <attr name="minDate" format="string" />
         <!-- The maximal date shown by this calendar view in mm/dd/yyyy format. -->
         <attr name="maxDate" format="string" />
-        <!-- The first day of week according to {@link java.util.Calendar}. -->
-        <attr name="firstDayOfWeek" />
+
+        <!-- Whether the spinners are shown. Only valid for "spinner" mode. -->
+        <attr name="spinnersShown" format="boolean" />
+        <!-- Whether the calendar view is shown. Only valid for "spinner" mode. -->
+        <attr name="calendarViewShown" format="boolean" />
+
         <!-- @hide The layout of the date picker. -->
         <attr name="internalLayout" format="reference"  />
         <!-- @hide The layout of the legacy DatePicker. -->
         <attr name="legacyLayout" />
-        <!-- The background color for the date selector 's day of week. -->
-        <attr name="dayOfWeekBackground" format="color|reference" />
-        <!-- The text color for the date selector's day of week. -->
-        <attr name="dayOfWeekTextAppearance" format="reference" />
-        <!-- The month's text appearance in the date selector. -->
-        <attr name="headerMonthTextAppearance" format="reference" />
-        <!-- The day of month's text appearance in the date selector. -->
-        <attr name="headerDayOfMonthTextAppearance" format="reference" />
-        <!-- The year's text appearance in the date selector. -->
-        <attr name="headerYearTextAppearance" format="reference" />
-        <!-- The background for the date selector. -->
+
+        <!-- The text color for the selected date header text, ex. "2014" or
+             "Tue, Mar 18". This should be a color state list where the
+             activated state will be used when the year picker or day picker is
+             active.-->
+        <attr name="headerTextColor" format="color" />
+        <!-- The background for the selected date header. -->
         <attr name="headerBackground" />
-        <!-- The list year's text appearance in the list. -->
+
+        <!-- The list year's text appearance in the list.
+             {@deprecated Use yearListTextColor. }-->
         <attr name="yearListItemTextAppearance" format="reference" />
-        <!-- The list year's selected circle color in the list. -->
-        <attr name="yearListSelectorColor" format="color" />
+        <!-- @hide The list year's text appearance in the list when activated. -->
+        <attr name="yearListItemActivatedTextAppearance" format="reference" />
         <!-- The text color list of the calendar. -->
         <attr name="calendarTextColor" format="color" />
-        <!-- @hide The activated background color for the calendar. -->
-        <attr name="calendarDayBackgroundColor" format="color" />
+
         <!-- Defines the look of the widget. Prior to the L release, the only choice was
              spinner. As of L, with the Material theme selected, the default layout is calendar,
              but this attribute can be used to force spinner to be used instead. -->
@@ -4492,6 +4495,31 @@
             <!-- Date picker with calendar to select the date. -->
             <enum name="calendar" value="2" />
         </attr>
+
+        <!-- The first year (inclusive), for example "1940".
+             {@deprecated Use minDate instead.} -->
+        <attr name="startYear" format="integer" />
+        <!-- The last year (inclusive), for example "2010".
+             {@deprecated Use maxDate instead.} -->
+        <attr name="endYear" format="integer" />
+        <!-- The text appearance for the month (ex. May) in the selected date header.
+             {@deprecated Use headerTextColor instead.} -->
+        <attr name="headerMonthTextAppearance" format="reference" />
+        <!-- The text appearance for the day of month (ex. 28) in the selected date header.
+             {@deprecated Use headerTextColor instead.} -->
+        <attr name="headerDayOfMonthTextAppearance" format="reference" />
+        <!-- The text appearance for the year (ex. 2014) in the selected date header.
+             {@deprecated Use headerTextColor instead.} -->
+        <attr name="headerYearTextAppearance" format="reference" />
+        <!-- The background color for the header's day of week.
+             {@deprecated No longer displayed.} -->
+        <attr name="dayOfWeekBackground" format="color" />
+        <!-- The text color for the header's day of week.
+             {@deprecated No longer displayed.} -->
+        <attr name="dayOfWeekTextAppearance" format="reference" />
+        <!-- The list year's selected circle color in the list.
+             {@deprecated No longer displayed.} -->
+        <attr name="yearListSelectorColor" format="color" />
     </declare-styleable>
 
     <declare-styleable name="TwoLineListItem">
@@ -4709,35 +4737,42 @@
     <declare-styleable name="CalendarView">
         <!-- The first day of week according to {@link java.util.Calendar}. -->
         <attr name="firstDayOfWeek" format="integer" />
-        <!-- Whether do show week numbers. -->
-        <attr name="showWeekNumber" format="boolean" />
         <!-- The minimal date shown by this calendar view in mm/dd/yyyy format. -->
         <attr name="minDate" />
         <!-- The minimal date shown by this calendar view in mm/dd/yyyy format. -->
         <attr name="maxDate" />
-        <!-- The number of weeks to be shown. -->
-        <attr name="shownWeekCount" format="integer"/>
-        <!-- The background color for the selected week. -->
-        <attr name="selectedWeekBackgroundColor" format="color|reference" />
-        <!-- The color for the dates of the focused month. -->
-        <attr name="focusedMonthDateColor" format="color|reference" />
-        <!-- The color for the dates of an unfocused month. -->
-        <attr name="unfocusedMonthDateColor" format="color|reference" />
-        <!-- The color for the week numbers. -->
-        <attr name="weekNumberColor" format="color|reference" />
-        <!-- The color for the separator line between weeks. -->
-        <attr name="weekSeparatorLineColor" format="color|reference" />
-        <!-- Drawable for the vertical bar shown at the beginning and at the end of the selected date. -->
-        <attr name="selectedDateVerticalBar" format="reference" />
-        <!-- The text appearance for the week day abbreviation of the calendar header. -->
+        <!-- The text appearance for the month and year in the calendar header. -->
+        <attr name="monthTextAppearance" format="reference" />
+        <!-- The text appearance for the week day abbreviation in the calendar header. -->
         <attr name="weekDayTextAppearance" format="reference" />
-        <!-- The text appearance for the calendar dates. -->
+        <!-- The text appearance for the day numbers in the calendar grid. -->
         <attr name="dateTextAppearance" format="reference" />
-        <!-- The number of weeks to be shown. -->
+        <!-- @hide The background color used for the day selection indicator. -->
+        <attr name="daySelectorColor" format="color" />
+        <!-- @hide The background color used for the day highlight indicator. -->
+        <attr name="dayHighlightColor" format="color" />
+        <!-- @hide Which style of calendar delegate to use. -->
         <attr name="calendarViewMode">
             <enum name="holo" value="0" />
             <enum name="material" value="1" />
         </attr>
+
+        <!-- @deprecated Whether do show week numbers. -->
+        <attr name="showWeekNumber" format="boolean" />
+        <!-- @deprecated The number of weeks to be shown. -->
+        <attr name="shownWeekCount" format="integer"/>
+        <!-- @deprecated The background color for the selected week. -->
+        <attr name="selectedWeekBackgroundColor" format="color|reference" />
+        <!-- @deprecated The color for the dates of the focused month. -->
+        <attr name="focusedMonthDateColor" format="color|reference" />
+        <!-- @deprecated The color for the dates of an unfocused month. -->
+        <attr name="unfocusedMonthDateColor" format="color|reference" />
+        <!-- @deprecated The color for the week numbers. -->
+        <attr name="weekNumberColor" format="color|reference" />
+        <!-- @deprecated The color for the separator line between weeks. -->
+        <attr name="weekSeparatorLineColor" format="color|reference" />
+        <!-- @deprecated Drawable for the vertical bar shown at the beginning and at the end of the selected date. -->
+        <attr name="selectedDateVerticalBar" format="reference" />
     </declare-styleable>
 
     <declare-styleable name="NumberPicker">
@@ -4770,24 +4805,27 @@
         <attr name="legacyLayout" format="reference" />
         <!-- @hide The layout of the time picker. -->
         <attr name="internalLayout" />
-        <!-- The text appearance for the AM/PM header. -->
-        <attr name="headerAmPmTextAppearance" format="reference" />
-        <!-- The text appearance for the time header. -->
-        <attr name="headerTimeTextAppearance" format="reference" />
+
+        <!-- The text color for the selected time header text, ex. "12" or
+             "PM". This should be a color state list where the activated state
+             will be used when the minute picker or hour picker is active.-->
+        <attr name="headerTextColor" />
         <!-- The background for the header containing the currently selected time. -->
         <attr name="headerBackground" />
-        <!-- The color for the hours/minutes numbers. -->
+
+        <!-- The color for the hours/minutes numbers. This should be a color
+             state list where the activated state will be used when the number
+             is active.-->
         <attr name="numbersTextColor" format="color" />
-        <!-- The color for the inner hours numbers used in 24-hour mode. -->
+        <!-- The color for the inner hours numbers used in 24-hour mode. This
+             should be a color state list where the activated state will be
+             used when the number is active.-->
         <attr name="numbersInnerTextColor" format="color" />
         <!-- The background color for the hours/minutes numbers. -->
         <attr name="numbersBackgroundColor" format="color" />
-        <!-- The color for the AM/PM selectors. -->
-        <attr name="amPmTextColor" format="color" />
-        <!-- The background color state list for the AM/PM selectors. -->
-        <attr name="amPmBackgroundColor" format="color" />
         <!-- The color for the hours/minutes selector. -->
         <attr name="numbersSelectorColor" format="color" />
+
         <!-- Defines the look of the widget. Prior to the L release, the only choice was
              spinner. As of L, with the Material theme selected, the default layout is clock,
              but this attribute can be used to force spinner to be used instead. -->
@@ -4797,6 +4835,19 @@
             <!-- Time picker with clock face to select the time. -->
             <enum name="clock" value="2" />
         </attr>
+
+        <!-- The text appearance for the AM/PM header.
+             @deprecated Use headerTextColor instead. -->
+        <attr name="headerAmPmTextAppearance" format="reference" />
+        <!-- The text appearance for the time header.
+             @deprecated Use headerTextColor instead. -->
+        <attr name="headerTimeTextAppearance" format="reference" />
+        <!-- The color for the AM/PM selectors.
+             {@deprecated Use headerTextColor instead.}-->
+        <attr name="amPmTextColor" format="color" />
+        <!-- The background color state list for the AM/PM selectors.
+             {@deprecated Use headerBackground instead.}-->
+        <attr name="amPmBackgroundColor" format="color" />
     </declare-styleable>
 
     <!-- ========================= -->
@@ -7717,6 +7768,8 @@
             <enum name="multi-select" value="4" />
             <enum name="integer" value="5" />
             <enum name="string" value="6" />
+            <enum name="bundle" value="7" />
+            <enum name="bundle_array" value="8" />
         </attr>
         <attr name="title" />
         <attr name="description" />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c0b2cbef..b0b4e3a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -389,13 +389,12 @@
          with the same {@link android.R.attr#taskAffinity} as it has. -->
     <attr name="allowTaskReparenting" format="boolean" />
 
-    <!-- Declare that this application may use cleartext traffic (e.g., HTTP rather than HTTPS;
-         WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP without STARTTLS or TLS).
-         Defaults to true. If set to false {@code false}, the app declares that it does not
-         intend to use cleartext network traffic, in which case platform components (e.g.,
-         HTTP stacks, {@code WebView}, {@code MediaPlayer}) will refuse app's requests to use
-         cleartext traffic. Third-party libraries are encouraged to honor this flag as well.
-         @hide -->
+    <!-- Declare that this application may use cleartext traffic, such as HTTP rather than HTTPS;
+         WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP without STARTTLS or TLS.
+         Defaults to true. If set to false {@code false}, the application declares that it does not
+         intend to use cleartext network traffic, in which case platform components (e.g. HTTP
+         stacks, {@code WebView}, {@code MediaPlayer}) will refuse applications's requests to use
+         cleartext traffic. Third-party libraries are encouraged to honor this flag as well. -->
     <attr name="usesCleartextTraffic" format="boolean" />
 
     <!-- Declare that code from this application will need to be loaded into other
@@ -1039,6 +1038,22 @@
          activity. -->
     <attr name="resizeableActivity" format="boolean" />
 
+    <!-- When set installer will extract native libraries. If set to false
+         libraries in the apk must be stored and page-aligned.  -->
+    <attr name="extractNativeLibs" format="boolean"/>
+
+    <!-- Specify whether an activity intent filter will need to be verified thru its set
+         of data URIs. This will only be used when the Intent's action is set to
+         {@link android.content.Intent#ACTION_VIEW Intent.ACTION_VIEW} and the Intent's category is
+         set to {@link android.content.Intent#CATEGORY_BROWSABLE Intent.CATEGORY_BROWSABLE} and the
+         intern filter data scheme is set to "http" or "https". When set to true, the intent filter
+         will need to use its data tag for getting the URIs to verify with.
+
+         For each URI, an HTTPS network request will be done to <code>/.well-known/associations.json</code>
+         host to verify that the web site is okay with the app intercepting the URI.
+         -->
+    <attr name="autoVerify" format="boolean" />
+
     <!-- The <code>manifest</code> tag is the root of an
          <code>AndroidManifest.xml</code> file,
          describing the contents of an Android package (.apk) file.  One
@@ -1160,17 +1175,17 @@
              "com.google". -->
         <attr name="requiredAccountType" format="string"/>
         <attr name="isGame" />
-        <!-- Declare that this application may use cleartext traffic (e.g., HTTP rather than HTTPS;
-             WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP without STARTTLS or TLS).
-             Defaults to true. If set to false {@code false}, the app declares that it does not
-             intend to use cleartext network traffic, in which case platform components (e.g.,
-             HTTP stacks, {@code WebView}, {@code MediaPlayer}) will refuse app's requests to use
-             cleartext traffic. Third-party libraries are encouraged to honor this flag as well.
-             @hide -->
+        <!-- Declare that this application may use cleartext traffic, such as HTTP rather than
+             HTTPS; WebSockets rather than WebSockets Secure; XMPP, IMAP, STMP without STARTTLS or
+             TLS). Defaults to true. If set to false {@code false}, the application declares that it
+             does not intend to use cleartext network traffic, in which case platform components
+             (e.g. HTTP stacks, {@code WebView}, {@code MediaPlayer}) will refuse applications's
+             requests to use cleartext traffic. Third-party libraries are encouraged to honor this
+             flag as well. -->
         <attr name="usesCleartextTraffic" />
         <attr name="multiArch" />
+        <attr name="extractNativeLibs" />
     </declare-styleable>
-    
     <!-- The <code>permission</code> tag declares a security permission that can be
          used to control access from other packages to specific components or
          features in your package (or other packages).  See the
@@ -1837,6 +1852,7 @@
         <attr name="banner" />
         <attr name="logo" />
         <attr name="priority" />
+        <attr name="autoVerify" />
     </declare-styleable>
     
     <!-- Attributes that can be supplied in an AndroidManifest.xml
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index da68c92..1cb39f0 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -16,16 +16,19 @@
 
 <!-- Colors specific to Material themes. -->
 <resources>
-    <color name="background_material_dark">#ff303030</color>
-    <color name="background_material_light">#fffafafa</color>
-    <color name="background_floating_material_dark">#ff424242</color>
-    <color name="background_floating_material_light">#ffffffff</color>
+    <color name="foreground_material_dark">@color/white</color>
+    <color name="foreground_material_light">@color/black</color>
 
-    <color name="primary_material_dark">#ff212121</color>
-    <color name="primary_material_light">#fff5f5f5</color>
-    <color name="primary_dark_material_dark">#ff000000</color>
-    <color name="primary_dark_material_light">#ff757575</color>
-    <color name="primary_dark_material_light_light_status_bar">#ffe0e0e0</color>
+    <color name="background_material_dark">@color/material_grey_850</color>
+    <color name="background_material_light">@color/material_grey_50</color>
+    <color name="background_floating_material_dark">@color/material_grey_800</color>
+    <color name="background_floating_material_light">@color/white</color>
+
+    <color name="primary_material_dark">@color/material_grey_900</color>
+    <color name="primary_material_light">@color/material_grey_100</color>
+    <color name="primary_dark_material_dark">@color/black</color>
+    <color name="primary_dark_material_light">@color/material_grey_600</color>
+    <color name="primary_dark_material_light_light_status_bar">@color/material_grey_300</color>
 
     <color name="accent_material_light">@color/material_deep_teal_500</color>
     <color name="accent_material_dark">@color/material_deep_teal_200</color>
@@ -38,19 +41,20 @@
     <color name="switch_thumb_disabled_material_dark">#ff616161</color>
     <color name="switch_thumb_disabled_material_light">#ffbdbdbd</color>
 
-    <color name="foreground_material_dark">@color/white</color>
-    <color name="foreground_material_light">@color/black</color>
-
-    <color name="link_text_material_dark">@color/material_deep_teal_200</color>
     <color name="link_text_material_light">@color/material_deep_teal_500</color>
+    <color name="link_text_material_dark">@color/material_deep_teal_200</color>
 
     <!-- Text & foreground colors -->
     <eat-comment />
 
+    <!-- 87% black -->
     <color name="primary_text_default_material_light">#de000000</color>
+    <!-- 54% black -->
     <color name="secondary_text_default_material_light">#8a000000</color>
 
+    <!-- 100% white -->
     <color name="primary_text_default_material_dark">#ffffffff</color>
+    <!-- 70% white -->
     <color name="secondary_text_default_material_dark">#b3ffffff</color>
 
     <item name="hint_alpha_material_dark" format="float" type="dimen">0.50</item>
@@ -66,6 +70,14 @@
     <!-- Primary & accent colors -->
     <eat-comment />
 
+    <color name="material_grey_900">#ff212121</color>
+    <color name="material_grey_850">#ff303030</color>
+    <color name="material_grey_800">#ff424242</color>
+    <color name="material_grey_600">#ff757575</color>
+    <color name="material_grey_300">#ffe0e0e0</color>
+    <color name="material_grey_100">#fff5f5f5</color>
+    <color name="material_grey_50">#fffafafa</color>
+
     <color name="material_deep_teal_200">#ff80cbc4</color>
     <color name="material_deep_teal_500">#ff009688</color>
 
@@ -98,16 +110,16 @@
     <color name="datepicker_default_disabled_text_color_material_light">#80999999</color>
     <color name="datepicker_default_disabled_text_color_material_dark">#80999999</color>
 
-    <color name="datepicker_default_selected_text_color_material_light">#33b5e5</color>
-    <color name="datepicker_default_selected_text_color_material_dark">#33b5e5</color>
+    <color name="datepicker_default_selected_text_color_material_light">#ff33b5e5</color>
+    <color name="datepicker_default_selected_text_color_material_dark">#ff33b5e5</color>
 
-    <color name="datepicker_default_pressed_text_color_material_light">#0099cc</color>
-    <color name="datepicker_default_pressed_text_color_material_dark">#0099cc</color>
+    <color name="datepicker_default_pressed_text_color_material_light">#ff0099cc</color>
+    <color name="datepicker_default_pressed_text_color_material_dark">#ff0099cc</color>
 
     <color name="datepicker_default_circle_background_color_material_light">@color/material_deep_teal_500</color>
     <color name="datepicker_default_circle_background_color_material_dark">@color/material_deep_teal_200</color>
 
-    <color name="datepicker_default_view_animator_color_material_light">#f2f2f2</color>
+    <color name="datepicker_default_view_animator_color_material_light">#fff2f2f2</color>
     <color name="datepicker_default_view_animator_color_material_dark">#ff303030</color>
 
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 37c9598..e879244 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -471,6 +471,18 @@
     <!-- Wifi driver supports batched scan -->
     <bool translatable="false" name="config_wifi_batched_scan_supported">false</bool>
 
+    <!-- Idle Receive current for wifi radio. 0 by default-->
+    <integer translatable="false" name="config_wifi_idle_receive_cur_ma">1</integer>
+
+    <!-- Rx current for wifi radio. 0 by default-->
+    <integer translatable="false" name="config_wifi_active_rx_cur_ma">2</integer>
+
+    <!-- Tx current for wifi radio. 0 by default-->
+    <integer translatable="false" name="config_wifi_tx_cur_ma">3</integer>
+
+    <!-- Operating volatage for wifi radio. 0 by default-->
+    <integer translatable="false" name="config_wifi_operating_voltage_mv">4</integer>
+
     <!-- Flag indicating whether the we should enable the automatic brightness in Settings.
          Software implementation will be used if config_hardware_auto_brightness_available is not set -->
     <bool name="config_automatic_brightness_available">false</bool>
@@ -1112,6 +1124,18 @@
          device does not support multiple advertisement-->
     <integer translatable="false" name="config_bluetooth_max_advertisers">0</integer>
 
+    <!-- Idle current for bluetooth controller. 0 by default-->
+    <integer translatable="false" name="config_bluetooth_idle_cur_ma">1</integer>
+
+    <!-- Rx current for bluetooth controller. 0 by default-->
+    <integer translatable="false" name="config_bluetooth_rx_cur_ma">2</integer>
+
+    <!-- Tx current for bluetooth controller. 0 by default-->
+    <integer translatable="false" name="config_bluetooth_tx_cur_ma">3</integer>
+
+    <!-- Operating volatage for bluetooth controller. 0 by default-->
+    <integer translatable="false" name="config_bluetooth_operating_voltage_mv">4</integer>
+
     <!-- The default data-use polling period. -->
     <integer name="config_datause_polling_period_sec">600</integer>
 
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 6c6d2cc..7d08e7f 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -364,26 +364,32 @@
 
     <dimen name="resolver_max_width">480dp</dimen>
 
-     <!-- Size of the offset applied to the position of the circular mask. This is only
-     used on circular displays. In the case where there is no "chin", this will default
-     to 0 -->
-     <dimen name="circular_display_mask_offset">0px</dimen>
+    <!-- Size of the offset applied to the position of the circular mask. This
+         is only used on circular displays. In the case where there is no
+         "chin", this will default to 0 -->
+    <dimen name="circular_display_mask_offset">0px</dimen>
+    <!-- Amount to reduce the size of the circular mask by (to compensate for
+         aliasing effects). This is only used on circular displays. -->
+    <dimen name="circular_display_mask_thickness">1px</dimen>
 
-     <!-- Amount to reduce the size of the circular mask by (to compensate for aliasing
-     effects). This is only used on circular displays. -->
-     <dimen name="circular_display_mask_thickness">1px</dimen>
+    <dimen name="lock_pattern_dot_line_width">3dp</dimen>
+    <dimen name="lock_pattern_dot_size">12dp</dimen>
+    <dimen name="lock_pattern_dot_size_activated">28dp</dimen>
 
-     <dimen name="lock_pattern_dot_line_width">3dp</dimen>
-     <dimen name="lock_pattern_dot_size">12dp</dimen>
-     <dimen name="lock_pattern_dot_size_activated">28dp</dimen>
+    <dimen name="text_handle_min_size">40dp</dimen>
 
-     <dimen name="text_handle_min_size">40dp</dimen>
+    <!-- Lighting and shadow properties -->
+    <dimen name="light_y">-200dp</dimen>
+    <dimen name="light_z">800dp</dimen>
+    <dimen name="light_radius">600dp</dimen>
+    <item type="dimen" format="float" name="ambient_shadow_alpha">0.075</item>
+    <item type="dimen" format="float" name="spot_shadow_alpha">0.15</item>
 
-     <!-- Lighting and shadow properties -->
-     <dimen name="light_y">-200dp</dimen>
-     <dimen name="light_z">800dp</dimen>
-     <dimen name="light_radius">600dp</dimen>
-     <item type="dimen" format="float" name="ambient_shadow_alpha">0.075</item>
-     <item type="dimen" format="float" name="spot_shadow_alpha">0.15</item>
-
+     <!-- Floating toolbar dimensions -->
+     <dimen name="floating_toolbar_height">48dp</dimen>
+     <dimen name="floating_toolbar_menu_button_side_padding">8dp</dimen>
+     <dimen name="floating_toolbar_text_size">14sp</dimen>
+     <dimen name="floating_toolbar_menu_button_minimum_width">48dp</dimen>
+     <dimen name="floating_toolbar_default_width">250dp</dimen>
+     <dimen name="floating_toolbar_minimum_overflow_height">192dp</dimen>
 </resources>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 8d2afde..6fd39f6 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -143,9 +143,7 @@
     <dimen name="timepicker_text_size_inner">12sp</dimen>
 
     <!-- Material date picker dimensions. -->
-    <dimen name="datepicker_year_picker_padding_top">8dp</dimen>
     <dimen name="datepicker_year_label_height">64dp</dimen>
-    <dimen name="datepicker_year_label_text_size">22dp</dimen>
     <dimen name="datepicker_component_width">260dp</dimen>
     <dimen name="datepicker_dialog_width">520dp</dimen>
     <dimen name="datepicker_selected_date_day_size">88dp</dimen>
@@ -154,10 +152,24 @@
     <dimen name="datepicker_header_height">30dp</dimen>
     <dimen name="datepicker_header_text_size">14dp</dimen>
 
+    <dimen name="datepicker_list_year_label_size">16sp</dimen>
+    <dimen name="datepicker_list_year_activated_label_size">26sp</dimen>
+
+    <dimen name="date_picker_year_label_size">16sp</dimen>
+    <dimen name="date_picker_date_label_size">34dp</dimen>
+
     <!-- Used by Material-style SimpleMonthView -->
-    <dimen name="datepicker_day_number_size">12sp</dimen>
-    <dimen name="datepicker_month_label_size">14sp</dimen>
-    <dimen name="datepicker_month_day_label_text_size">12sp</dimen>
-    <dimen name="datepicker_month_list_item_header_height">48dp</dimen>
+    <dimen name="date_picker_month_text_size">14sp</dimen>
+    <dimen name="date_picker_day_of_week_text_size">12sp</dimen>
+    <dimen name="date_picker_day_text_size">12sp</dimen>
+    <dimen name="date_picker_month_height">56dp</dimen>
+    <dimen name="date_picker_day_of_week_height">36dp</dimen>
+    <dimen name="date_picker_day_height">40dp</dimen>
+    <dimen name="date_picker_day_width">44dp</dimen>
+    <dimen name="date_picker_day_selector_radius">20dp</dimen>
+    <dimen name="day_picker_padding_horizontal">20dp</dimen>
+    <dimen name="day_picker_padding_top">6dp</dimen>
+    <dimen name="day_picker_button_margin_top">0dp</dimen>
+
     <dimen name="datepicker_view_animator_height">226dp</dimen>
 </resources>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 6108b27..7e963954 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -93,4 +93,5 @@
   <item type="id" name="undo" />
   <item type="id" name="redo" />
   <item type="id" name="replaceText" />
+  <item type="id" name="accessibility_action_show_on_screen" />
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e507b3d..f59a4d8 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2651,4 +2651,11 @@
 
   <public type="attr" name="colorBackgroundFloating" />
 
+  <public type="attr" name="extractNativeLibs" />
+  <public type="attr" name="usesCleartextTraffic" />
+
+  <!--IntentFilter auto verification -->
+  <public type="attr" name="autoVerify" />
+  <public type="attr" name="breakStrategy" />
+
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 19cae03..6cd3139 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -249,8 +249,20 @@
     <string name="wfcRegErrorTitle">Wi-Fi Calling</string>
     <!-- WFC Operator Error Codes -->
     <string-array name="wfcOperatorErrorCodes" translatable="false" />
-    <!-- WFC Operator Error Messages -->
-    <string-array name="wfcOperatorErrorMessages" />
+    <!-- WFC Operator Error Messages showed as alerts -->
+    <string-array name="wfcOperatorErrorAlertMessages" />
+    <!-- WFC Operator Error Messages showed as notifications -->
+    <string-array name="wfcOperatorErrorNotificationMessages" />
+    <!-- Template for showing cellular network operator name while WFC is active -->
+    <string name="wfcSpnFormat">%s</string>
+    <!-- WFC, summary for Disabled -->
+    <string name="wifi_calling_off_summary">Off</string>
+    <!-- WFC, summary for Wi-Fi Preferred -->
+    <string name="wfc_mode_wifi_preferred_summary">Wi-Fi preferred</string>
+    <!-- WFC, summary for Cellular Preferred -->
+    <string name="wfc_mode_cellular_preferred_summary">Cellular preferred</string>
+    <!-- WFC, summary for Wi-Fi Only -->
+    <string name="wfc_mode_wifi_only_summary">Wi-Fi only</string>
 
     <!--
         {0} is one of "bearerServiceCode*"
@@ -1756,6 +1768,8 @@
     <string name="permlab_cameraDisableTransmitLed">disable transmit indicator LED when camera is in use</string>
     <!-- Description of a camera app permission, listed so the user can choose whether or not they want to allow it to disable the may-transmit light indicator. -->
     <string name="permdesc_cameraDisableTransmitLed">Allows a pre-installed system application to disable the camera use indicator LED.</string>
+    <!-- Description of a camera app permission, listed so that the user can send the camera service notifications about system-wide events. -->
+    <string name="permdesc_cameraSendSystemEvent">Allows a pre-installed system application to send the camera service system events.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_brick" product="tablet">permanently disable tablet</string>
@@ -1885,6 +1899,10 @@
     <string name="permdesc_performCdmaProvisioning">Allows the app to start CDMA provisioning.
         Malicious apps may unnecessarily start CDMA provisioning.</string>
 
+    <string name="permlab_performSimActivation">start SIM card setup</string>
+    <string name="permdesc_performSimActivation">Allows the app to handle SIM activation requests.
+        The app may directly perform activation or may delegate to another app.</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_locationUpdates">control location update notifications</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -2226,6 +2244,36 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_useFingerprint">Allows the app to use fingerprint hardware for authentication</string>
 
+    <!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized -->
+    <string name="fingerprint_acquired_partial">Partial fingerprint detected. Please try again.</string>
+    <!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized -->
+    <string name="fingerprint_acquired_insufficient">Couldn\'t process fingerprint. Please try again.</string>
+    <!-- Message shown during fingerprint acquisision when the fingerprint sensor needs cleaning -->
+    <string name="fingerprint_acquired_imager_dirty">Fingerprint sensor is dirty. Please clean and try again.</string>
+    <!-- Message shown during fingerprint acquisision when the user removes their finger from the sensor too quickly -->
+    <string name="fingerprint_acquired_too_fast">Finger moved to fast. Please try again.</string>
+    <!-- Message shown during fingerprint acquisision when the user moves their finger too slowly -->
+    <string name="fingerprint_acquired_too_slow">Finger moved to slow. Please try again.</string>
+    <!-- Array containing custom messages shown during fingerprint acquisision from vendor.  Vendor is expected to add and translate these strings -->
+    <string-array name="fingerprint_acquired_vendor">
+        <item>Vendor-specific acquisition error message 0</item>
+    </string-array>
+
+    <!-- Generic error message shown when the fingerprint hardware can't recognize the fingerprint -->
+    <string name="fingerprint_error_unable_to_process">Unable to process. Try again.</string>
+    <!-- Error message shown when the fingerprint hardware can't be accessed -->
+    <string name="fingerprint_error_hw_not_available">Hardware not available.</string>
+    <!-- Error message shown when the fingerprint hardware has run out of room for storing fingerprints -->
+    <string name="fingerprint_error_no_space">Fingerprint can\'t be stored. Please remove an existing fingerprint.</string>
+    <!-- Error message shown when the fingerprint hardware timer has expired and the user needs to restart the operation. -->
+    <string name="fingerprint_error_timeout">Fingerprint time out reached. Try again.</string>
+    <!-- Error message shown when the fingerprint hardware timer has expired and the user needs to restart the operation. -->
+    <string name="fingerprint_error_vendor">Fingerprint time out reached. Try again.</string>
+    <!-- Array containing custom error messages from vendor.  Vendor is expected to add and translate these strings -->
+    <string-array name="fingerprint_error_vendor">
+        <item>Vendor-specifc error message.</item>
+    </string-array>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readSyncSettings">read sync settings</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -3335,6 +3383,22 @@
     <string name="permdesc_bindPackageVerifier">Allows the holder to make requests of
         package verifiers. Should never be needed for normal apps.</string>
 
+    <!-- Title of an application permission which allows the application to verify whether
+         a different intent filter is able to be verified by some internal logic. [CHAR LIMIT=40] -->
+    <string name="permlab_intentFilterVerificationAgent">verify intent filter</string>
+    <!-- Description of an application permission which allows the application to verify whether
+         a different intent filter is able to be verified by some internal heuristic. [CHAR LIMIT=NONE] -->
+    <string name="permdesc_intentFilterVerificationAgent">Allows the app to check if an intent filter
+        is verified or not.</string>
+
+    <!-- Title of an application permission which allows the application to verify whether
+         a different intent filter is able to be verified by some internal logic. [CHAR LIMIT=40] -->
+    <string name="permlab_bindIntentFilterVerifier">bind to an intent filter verifier</string>
+    <!-- Description of an application permission which allows the application to verify whether
+         a different intent filter is able to be verified by some internal logic. [CHAR LIMIT=NONE] -->
+    <string name="permdesc_bindIntentFilterVerifier">Allows the holder to make requests of
+        intent filter verifiers. Should never be needed for normal apps.</string>
+
     <!-- Title of an application permission which allows the application to access serial ports via the SerialManager. [CHAR LIMIT=40] -->
     <string name="permlab_serialPort">access serial ports</string>
     <!-- Description of an application permission which allows the application access serial ports via the SerialManager. [CHAR LIMIT=NONE] -->
@@ -4383,6 +4447,10 @@
     <string name="date_picker_increment_year_button">Increase year</string>
     <!-- Description of the button to decrease the DatePicker's year value. [CHAR LIMIT=NONE] -->
     <string name="date_picker_decrement_year_button">Decrease year</string>
+    <!-- Description of the button to move to the previous month. [CHAR LIMIT=NONE] -->
+    <string name="date_picker_prev_month_button">Previous month</string>
+    <!-- Description of the button to move to the next month. [CHAR LIMIT=NONE] -->
+    <string name="date_picker_next_month_button">Next month</string>
 
     <!-- KeyboardView - accessibility support -->
     <!-- Description of the Alt button in a KeyboardView. [CHAR LIMIT=NONE] -->
@@ -5042,24 +5110,10 @@
     <!-- Accessibility announcement for minute circular picker [CHAR LIMIT=NONE] -->
     <string name="select_minutes">Select minutes</string>
 
-    <!--
-        Content description for the month and day selector in the date picker, which displays
-        a selectable grid of days laid out by month.
-        [CHAR LIMIT=50]
-     -->
-    <string name="day_picker_description">Month grid of days</string>
-    <!--
-        Content description for the year selector in the date picker, which displays
-        a scrolling, vertical list of years.
-        [CHAR LIMIT=50]
-     -->
-    <string name="year_picker_description">Year list</string>
     <!-- Accessibility announcement for the day picker [CHAR LIMIT=NONE] -->
     <string name="select_day">Select month and day</string>
     <!-- Accessibility announcement for the year picker [CHAR LIMIT=NONE] -->
     <string name="select_year">Select year</string>
-    <!-- Accessibility description for the item that is currently selected. -->
-    <string name="item_is_selected"><xliff:g id="item" example="2013">%1$s</xliff:g> selected</string>
     <!-- Accessibility announcement when a number that had been typed in is deleted [CHAR_LIMIT=NONE] -->
     <string name="deleted_key"><xliff:g id="key" example="4">%1$s</xliff:g> deleted</string>
 
@@ -5079,7 +5133,11 @@
     <string name="sans_serif">sans-serif</string>
 
     <!-- DO NOT TRANSLATE -->
-    <string name="day_of_week_label_typeface">sans-serif</string>
+    <string name="date_picker_month_typeface">sans-serif-medium</string>
+    <!-- DO NOT TRANSLATE -->
+    <string name="date_picker_day_of_week_typeface">sans-serif-medium</string>
+    <!-- DO NOT TRANSLATE -->
+    <string name="date_picker_day_typeface">sans-serif-medium</string>
 
     <!-- Notify use that they are in Lock-to-app -->
     <string name="lock_to_app_toast">To unpin this screen, touch and hold Back and Overview at the same time.</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index cc64b43..3c3d286 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -497,6 +497,7 @@
         <item name="textEditSideNoPasteWindowLayout">?attr/textEditSideNoPasteWindowLayout</item>
         <item name="textEditSuggestionItemLayout">?attr/textEditSuggestionItemLayout</item>
         <item name="textCursorDrawable">?attr/textCursorDrawable</item>
+        <item name="breakStrategy">high_quality</item>
     </style>
 
     <style name="Widget.CheckedTextView">
@@ -527,6 +528,7 @@
         <item name="textAppearance">?attr/textAppearanceMediumInverse</item>
         <item name="textColor">?attr/editTextColor</item>
         <item name="gravity">center_vertical</item>
+        <item name="breakStrategy">simple</item>
     </style>
 
     <style name="Widget.ExpandableListView" parent="Widget.ListView">
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 223578d..23ac221 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -240,10 +240,6 @@
     <!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
     <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle.Inverse" parent="TextAppearance.Material.Widget.ActionMode.Subtitle.Inverse"/>
     <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Menu" parent="TextAppearance.Material.Widget.ActionBar.Menu"/>
-    <style name="TextAppearance.DeviceDefault.DatePicker.DayOfWeekLabel" parent="TextAppearance.Material.DatePicker.DayOfWeekLabel"/>
-    <style name="TextAppearance.DeviceDefault.DatePicker.MonthLabel" parent="TextAppearance.Material.DatePicker.MonthLabel"/>
-    <style name="TextAppearance.DeviceDefault.DatePicker.DayOfMonthLabel" parent="TextAppearance.Material.DatePicker.DayOfMonthLabel"/>
-    <style name="TextAppearance.DeviceDefault.DatePicker.YearLabel" parent="TextAppearance.Material.DatePicker.YearLabel"/>
 
     <!-- Preference Styles -->
     <style name="Preference.DeviceDefault" parent="Preference.Material"/>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index a8ab18d..9cf7884 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -372,48 +372,45 @@
     <style name="TextAppearance.Material.WindowTitle" parent="TextAppearance.Material.Title" />
     <style name="TextAppearance.Material.DialogWindowTitle" parent="TextAppearance.Material.Title" />
 
-    <style name="TextAppearance.Material.CalendarViewWeekDayView" parent="TextAppearance.Material.Small">
-        <item name="textStyle">bold</item>
-        <item name="textColor">#505050</item>
+    <style name="TextAppearance.Material.Widget.Calendar.Day" parent="TextAppearance.Material.Caption">
+        <item name="textColor">?attr/textColorPrimaryActivated</item>
     </style>
 
+    <style name="TextAppearance.Material.Widget.Calendar.DayOfWeek" parent="TextAppearance.Material.Caption" />
+    <style name="TextAppearance.Material.Widget.Calendar.Month" parent="TextAppearance.Material.Body2" />
+
     <style name="TextAppearance.Material.TimePicker.TimeLabel" parent="TextAppearance.Material">
         <item name="textSize">@dimen/timepicker_time_label_size</item>
-        <item name="textColor">@color/time_picker_header_text_material</item>
+        <item name="textColor">@color/primary_text_secondary_when_activated_material_inverse</item>
     </style>
 
     <style name="TextAppearance.Material.TimePicker.AmPmLabel" parent="TextAppearance.Material.Button">
         <item name="textSize">@dimen/timepicker_ampm_label_size</item>
-        <item name="textColor">@color/time_picker_header_text_material</item>
-    </style>
-
-    <style name="TextAppearance.Material.DatePicker.DayOfWeekLabel" parent="TextAppearance.Material">
-        <item name="includeFontPadding">false</item>
-        <item name="textColor">?attr/textColorPrimaryInverse</item>
-        <item name="textSize">@dimen/datepicker_header_text_size</item>
-    </style>
-
-    <style name="TextAppearance.Material.DatePicker.MonthLabel" parent="TextAppearance.Material">
-        <item name="includeFontPadding">false</item>
-        <item name="textColor">@color/date_picker_header_text_material</item>
-        <item name="textSize">@dimen/datepicker_selected_date_month_size</item>
-    </style>
-
-    <style name="TextAppearance.Material.DatePicker.DayOfMonthLabel" parent="TextAppearance.Material">
-        <item name="includeFontPadding">false</item>
-        <item name="textColor">@color/date_picker_header_text_material</item>
-        <item name="textSize">@dimen/datepicker_selected_date_day_size</item>
+        <item name="textColor">@color/primary_text_secondary_when_activated_material_inverse</item>
     </style>
 
     <style name="TextAppearance.Material.DatePicker.YearLabel" parent="TextAppearance.Material">
-        <item name="includeFontPadding">false</item>
-        <item name="textColor">@color/date_picker_header_text_material</item>
-        <item name="textSize">@dimen/datepicker_selected_date_year_size</item>
+        <item name="textColor">@color/primary_text_secondary_when_activated_material_inverse</item>
+        <item name="textSize">@dimen/date_picker_year_label_size</item>
+        <item name="fontFamily">sans-serif-medium</item>
+    </style>
+
+    <style name="TextAppearance.Material.DatePicker.DateLabel" parent="TextAppearance.Material">
+        <item name="textColor">@color/primary_text_secondary_when_activated_material_inverse</item>
+        <item name="textSize">@dimen/date_picker_date_label_size</item>
+        <item name="fontFamily">sans-serif-medium</item>
     </style>
 
     <style name="TextAppearance.Material.DatePicker.List.YearLabel" parent="TextAppearance.Material">
-        <item name="textColor">?attr/textColorSecondaryActivated</item>
-        <item name="textSize">@dimen/datepicker_year_label_text_size</item>
+        <item name="textColor">?attr/textColorPrimary</item>
+        <item name="textSize">@dimen/datepicker_list_year_label_size</item>
+        <item name="fontFamily">sans-serif</item>
+    </style>
+
+    <style name="TextAppearance.Material.DatePicker.List.YearLabel.Activated">
+        <item name="textColor">?attr/colorControlActivated</item>
+        <item name="textSize">@dimen/datepicker_list_year_activated_label_size</item>
+        <item name="fontFamily">sans-serif-medium</item>
     </style>
 
     <style name="TextAppearance.Material.Notification">
@@ -487,7 +484,8 @@
     <!-- Alert dialog button bar button -->
     <style name="Widget.Material.Button.ButtonBar.AlertDialog" parent="Widget.Material.Button.Borderless.Colored">
         <item name="minWidth">64dp</item>
-        <item name="maxLines">2</item>
+        <item name="singleLine">true</item>
+        <item name="ellipsize">none</item>
         <item name="minHeight">@dimen/alert_dialog_button_bar_height</item>
     </style>
 
@@ -615,14 +613,13 @@
     </style>
 
     <style name="Widget.Material.CalendarView" parent="Widget.CalendarView">
-        <item name="selectedWeekBackgroundColor">#330099FF</item>
-        <item name="focusedMonthDateColor">#FFFFFFFF</item>
-        <item name="unfocusedMonthDateColor">#66FFFFFF</item>
-        <item name="weekNumberColor">#33FFFFFF</item>
-        <item name="weekSeparatorLineColor">#19FFFFFF</item>
-        <item name="selectedDateVerticalBar">@drawable/day_picker_week_view_dayline_holo</item>
-        <item name="weekDayTextAppearance">@style/TextAppearance.Material.CalendarViewWeekDayView</item>
         <item name="calendarViewMode">material</item>
+
+        <item name="monthTextAppearance">@style/TextAppearance.Material.Widget.Calendar.Month</item>
+        <item name="weekDayTextAppearance">@style/TextAppearance.Material.Widget.Calendar.DayOfWeek</item>
+        <item name="dateTextAppearance">@style/TextAppearance.Material.Widget.Calendar.Day</item>
+        <item name="daySelectorColor">?attr/colorControlActivated</item>
+        <item name="dayHighlightColor">?attr/colorControlHighlight</item>
     </style>
 
     <style name="Widget.Material.ImageButton" parent="Widget.ImageButton">
@@ -645,13 +642,12 @@
         <item name="legacyLayout">@layout/time_picker_legacy_material</item>
         <!-- Attributes for new-style TimePicker. -->
         <item name="internalLayout">@layout/time_picker_material</item>
-        <item name="headerTimeTextAppearance">@style/TextAppearance.Material.TimePicker.TimeLabel</item>
-        <item name="headerAmPmTextAppearance">@style/TextAppearance.Material.TimePicker.AmPmLabel</item>
-        <item name="headerBackground">@drawable/time_picker_header_material</item>
+        <item name="headerTextColor">@color/primary_text_secondary_when_activated_material</item>
+        <item name="headerBackground">#ff555555</item>
         <item name="numbersTextColor">?attr/textColorPrimaryActivated</item>
         <item name="numbersInnerTextColor">?attr/textColorSecondaryActivated</item>
-        <item name="numbersBackgroundColor">#10ffffff</item>
         <item name="numbersSelectorColor">?attr/colorControlActivated</item>
+        <item name="numbersBackgroundColor">#ff555555</item>
         <item name="amPmTextColor">?attr/textColorSecondary</item>
     </style>
 
@@ -660,17 +656,9 @@
         <item name="legacyLayout">@layout/date_picker_legacy_holo</item>
         <item name="calendarViewShown">true</item>
         <!-- Attributes for new-style DatePicker. -->
-        <item name="internalLayout">@layout/date_picker_holo</item>
-        <item name="dayOfWeekBackground">#10000000</item>
-        <item name="dayOfWeekTextAppearance">@style/TextAppearance.Material.DatePicker.DayOfWeekLabel</item>
-        <item name="headerMonthTextAppearance">@style/TextAppearance.Material.DatePicker.MonthLabel</item>
-        <item name="headerDayOfMonthTextAppearance">@style/TextAppearance.Material.DatePicker.DayOfMonthLabel</item>
-        <item name="headerYearTextAppearance">@style/TextAppearance.Material.DatePicker.YearLabel</item>
-        <item name="headerBackground">?attr/colorAccent</item>
-        <item name="yearListItemTextAppearance">@style/TextAppearance.Material.DatePicker.List.YearLabel</item>
-        <item name="yearListSelectorColor">?attr/colorControlActivated</item>
-        <item name="calendarTextColor">?attr/textColorSecondaryActivated</item>
-        <item name="calendarDayBackgroundColor">?attr/colorControlActivated</item>
+        <item name="internalLayout">@layout/date_picker_material</item>
+        <item name="headerTextColor">@color/primary_text_secondary_when_activated_material</item>
+        <item name="headerBackground">#ff555555</item>
     </style>
 
     <style name="Widget.Material.ActivityChooserView" parent="Widget.ActivityChooserView">
@@ -1021,24 +1009,20 @@
     <style name="Widget.Material.Light.GestureOverlayView" parent="Widget.Material.GestureOverlayView"/>
     <style name="Widget.Material.Light.GridView" parent="Widget.Material.GridView"/>
     <style name="Widget.Material.Light.ImageButton" parent="Widget.Material.ImageButton"/>
-
-    <style name="Widget.Material.Light.CalendarView" parent="Widget.CalendarView">
-        <item name="selectedWeekBackgroundColor">#330066ff</item>
-        <item name="focusedMonthDateColor">#FF000000</item>
-        <item name="unfocusedMonthDateColor">#7F08002B</item>
-        <item name="weekNumberColor">#7F080021</item>
-        <item name="weekSeparatorLineColor">#7F08002A</item>
-        <item name="weekDayTextAppearance">@style/TextAppearance.Material.CalendarViewWeekDayView</item>
-        <item name="calendarViewMode">material</item>
-    </style>
-
+    <style name="Widget.Material.Light.CalendarView" parent="Widget.Material.CalendarView" />
     <style name="Widget.Material.Light.NumberPicker" parent="Widget.Material.NumberPicker"/>
 
     <style name="Widget.Material.Light.TimePicker" parent="Widget.Material.TimePicker">
-        <item name="numbersBackgroundColor">#10000000</item>
+        <item name="headerTextColor">@color/primary_text_secondary_when_activated_material_inverse</item>
+        <item name="headerBackground">?attr/colorAccent</item>
+        <item name="numbersBackgroundColor">#ffeeeeee</item>
     </style>
 
-    <style name="Widget.Material.Light.DatePicker" parent="Widget.Material.DatePicker" />
+    <style name="Widget.Material.Light.DatePicker" parent="Widget.Material.DatePicker">
+        <item name="headerTextColor">@color/primary_text_secondary_when_activated_material_inverse</item>
+        <item name="headerBackground">?attr/colorAccent</item>
+    </style>
+
     <style name="Widget.Material.Light.ActivityChooserView" parent="Widget.Material.ActivityChooserView" />
     <style name="Widget.Material.Light.ImageWell" parent="Widget.Material.ImageWell"/>
     <style name="Widget.Material.Light.ListView" parent="Widget.Material.ListView"/>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 203b017..220d5e7 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -328,6 +328,10 @@
   <java-symbol type="integer" name="config_wifi_framework_current_network_boost" />
   <java-symbol type="string"  name="config_wifi_random_mac_oui" />
   <java-symbol type="integer"  name="config_wifi_network_switching_blacklist_time" />
+  <java-symbol type="integer"  name="config_wifi_idle_receive_cur_ma" />
+  <java-symbol type="integer"  name="config_wifi_active_rx_cur_ma" />
+  <java-symbol type="integer"  name="config_wifi_tx_cur_ma" />
+  <java-symbol type="integer"  name="config_wifi_operating_voltage_mv" />
 
   <java-symbol type="bool" name="editable_voicemailnumber" />
 
@@ -349,6 +353,10 @@
   <java-symbol type="integer" name="config_burnInProtectionMinVerticalOffset" />
   <java-symbol type="integer" name="config_burnInProtectionMaxVerticalOffset" />
   <java-symbol type="integer" name="config_burnInProtectionMaxRadius" />
+  <java-symbol type="integer" name="config_bluetooth_idle_cur_ma" />
+  <java-symbol type="integer" name="config_bluetooth_rx_cur_ma" />
+  <java-symbol type="integer" name="config_bluetooth_tx_cur_ma" />
+  <java-symbol type="integer" name="config_bluetooth_operating_voltage_mv" />
   <java-symbol type="integer" name="config_cursorWindowSize" />
   <java-symbol type="integer" name="config_drawLockTimeoutMillis" />
   <java-symbol type="integer" name="config_doublePressOnPowerBehavior" />
@@ -755,7 +763,13 @@
   <java-symbol type="string" name="phoneTypeWorkPager" />
   <java-symbol type="string" name="wfcRegErrorTitle" />
   <java-symbol type="array" name="wfcOperatorErrorCodes" />
-  <java-symbol type="array" name="wfcOperatorErrorMessages" />
+  <java-symbol type="array" name="wfcOperatorErrorAlertMessages" />
+  <java-symbol type="array" name="wfcOperatorErrorNotificationMessages" />
+  <java-symbol type="string" name="wfcSpnFormat" />
+  <java-symbol type="string" name="wifi_calling_off_summary" />
+  <java-symbol type="string" name="wfc_mode_wifi_preferred_summary" />
+  <java-symbol type="string" name="wfc_mode_cellular_preferred_summary" />
+  <java-symbol type="string" name="wfc_mode_wifi_only_summary" />
   <java-symbol type="string" name="policydesc_disableCamera" />
   <java-symbol type="string" name="policydesc_encryptedStorage" />
   <java-symbol type="string" name="policydesc_expirePassword" />
@@ -1988,7 +2002,7 @@
   <java-symbol type="layout" name="time_picker_material" />
   <java-symbol type="layout" name="time_picker_header_material" />
   <java-symbol type="layout" name="year_label_text_view" />
-  <java-symbol type="layout" name="date_picker_holo" />
+  <java-symbol type="layout" name="date_picker_material" />
 
   <java-symbol type="id" name="time_header" />
   <java-symbol type="id" name="hours" />
@@ -1999,11 +2013,8 @@
   <java-symbol type="id" name="radial_picker" />
   <java-symbol type="id" name="separator" />
   <java-symbol type="id" name="date_picker_header" />
-  <java-symbol type="id" name="date_picker_month_and_day_layout" />
-  <java-symbol type="id" name="day_picker_selector_layout" />
-  <java-symbol type="id" name="date_picker_month" />
-  <java-symbol type="id" name="date_picker_day" />
-  <java-symbol type="id" name="date_picker_year" />
+  <java-symbol type="id" name="date_picker_header_year" />
+  <java-symbol type="id" name="date_picker_header_date" />
   <java-symbol type="id" name="animator" />
 
   <java-symbol type="string" name="done_label" />
@@ -2038,20 +2049,24 @@
   <java-symbol type="integer" name="config_downtime_condition_lookahead_threshold_hrs" />
   <java-symbol type="string" name="muted_by" />
 
-  <java-symbol type="string" name="item_is_selected" />
-  <java-symbol type="string" name="day_of_week_label_typeface" />
   <java-symbol type="string" name="select_day" />
-  <java-symbol type="string" name="day_picker_description" />
   <java-symbol type="string" name="select_year" />
-  <java-symbol type="string" name="year_picker_description" />
 
-  <java-symbol type="dimen" name="datepicker_day_number_size" />
-  <java-symbol type="dimen" name="datepicker_month_label_size" />
-  <java-symbol type="dimen" name="datepicker_month_day_label_text_size" />
-  <java-symbol type="dimen" name="datepicker_month_list_item_header_height" />
+  <java-symbol type="string" name="date_picker_month_typeface" />
+  <java-symbol type="string" name="date_picker_day_of_week_typeface" />
+  <java-symbol type="string" name="date_picker_day_typeface" />
+  <java-symbol type="dimen" name="date_picker_month_text_size" />
+  <java-symbol type="dimen" name="date_picker_day_of_week_text_size" />
+  <java-symbol type="dimen" name="date_picker_day_text_size" />
+  <java-symbol type="dimen" name="date_picker_month_height" />
+  <java-symbol type="dimen" name="date_picker_day_height" />
+  <java-symbol type="dimen" name="date_picker_day_width" />
+  <java-symbol type="dimen" name="date_picker_day_selector_radius" />
+  <java-symbol type="id" name="date_picker_day_picker" />
+  <java-symbol type="id" name="date_picker_year_picker" />
+
   <java-symbol type="dimen" name="datepicker_view_animator_height" />
   <java-symbol type="dimen" name="datepicker_year_label_height" />
-  <java-symbol type="dimen" name="datepicker_year_picker_padding_top" />
 
   <java-symbol type="array" name="config_clockTickVibePattern" />
   <java-symbol type="array" name="config_calendarDateVibePattern" />
@@ -2059,6 +2074,19 @@
   <!-- From KeyguardServiceDelegate -->
   <java-symbol type="string" name="config_keyguardComponent" />
 
+  <!-- Fingerprint messages -->
+  <java-symbol type="string" name="fingerprint_error_unable_to_process" />
+  <java-symbol type="string" name="fingerprint_error_hw_not_available" />
+  <java-symbol type="string" name="fingerprint_error_no_space" />
+  <java-symbol type="string" name="fingerprint_error_timeout" />
+  <java-symbol type="array" name="fingerprint_error_vendor" />
+  <java-symbol type="string" name="fingerprint_acquired_partial" />
+  <java-symbol type="string" name="fingerprint_acquired_insufficient" />
+  <java-symbol type="string" name="fingerprint_acquired_imager_dirty" />
+  <java-symbol type="string" name="fingerprint_acquired_too_slow" />
+  <java-symbol type="string" name="fingerprint_acquired_too_fast" />
+  <java-symbol type="array" name="fingerprint_acquired_vendor" />
+
   <!-- From various Material changes -->
   <java-symbol type="attr" name="titleTextAppearance" />
   <java-symbol type="attr" name="subtitleTextAppearance" />
@@ -2115,7 +2143,6 @@
   <java-symbol type="id" name="transitionTransform" />
   <java-symbol type="id" name="parentMatrix" />
   <java-symbol type="bool" name="config_auto_attach_data_on_creation" />
-  <java-symbol type="id" name="date_picker_month_day_year_layout" />
   <java-symbol type="attr" name="closeItemLayout" />
   <java-symbol type="layout" name="resolver_different_item_header" />
   <java-symbol type="array" name="config_default_vm_number" />
@@ -2167,4 +2194,36 @@
   <java-symbol type="integer" name="config_screen_magnification_multi_tap_adjustment" />
   <java-symbol type="dimen" name="config_screen_magnification_scaling_threshold" />
   <java-symbol type="dimen" name="timepicker_selector_stroke"/>
+
+  <java-symbol type="style" name="TextAppearance.Material.Widget.Calendar.Month" />
+  <java-symbol type="style" name="TextAppearance.Material.Widget.Calendar.DayOfWeek" />
+  <java-symbol type="style" name="TextAppearance.Material.Widget.Calendar.Day" />
+  <java-symbol type="style" name="TextAppearance.Material.DatePicker.List.YearLabel" />
+  <java-symbol type="style" name="TextAppearance.Material.DatePicker.List.YearLabel.Activated" />
+  <java-symbol type="dimen" name="day_picker_padding_top"/>
+  <java-symbol type="dimen" name="date_picker_day_of_week_height"/>
+
+  <java-symbol type="string" name="storage_internal" />
+  <java-symbol type="string" name="storage_sd_card" />
+  <java-symbol type="string" name="storage_usb" />
+
+  <java-symbol type="id" name="accessibility_action_show_on_screen" />
+
+  <!-- Floating toolbar -->
+  <java-symbol type="layout" name="floating_popup_container" />
+  <java-symbol type="layout" name="floating_popup_menu_button" />
+  <java-symbol type="layout" name="floating_popup_open_overflow_button" />
+  <java-symbol type="dimen" name="floating_toolbar_height" />
+  <java-symbol type="dimen" name="floating_toolbar_menu_button_side_padding" />
+  <java-symbol type="dimen" name="floating_toolbar_text_size" />
+  <java-symbol type="dimen" name="floating_toolbar_menu_button_minimum_width" />
+  <java-symbol type="dimen" name="floating_toolbar_default_width" />
+  <java-symbol type="dimen" name="floating_toolbar_minimum_overflow_height" />
+
+  <java-symbol type="drawable" name="ic_chevron_left" />
+  <java-symbol type="drawable" name="ic_chevron_right" />
+  <java-symbol type="string" name="date_picker_prev_month_button" />
+  <java-symbol type="string" name="date_picker_next_month_button" />
+  <java-symbol type="layout" name="date_picker_month_item_material" />
+  <java-symbol type="id" name="month_view" />
 </resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 38cfecd..9931d00 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -857,6 +857,7 @@
     <!-- Theme overlay that overrides window properties to display as a dialog. -->
     <style name="ThemeOverlay.Material.Dialog">
         <item name="colorBackgroundCacheHint">@null</item>
+        <item name="colorBackground">?attr/colorBackgroundFloating</item>
 
         <item name="windowFrame">@null</item>
         <item name="windowTitleStyle">@style/DialogWindowTitle.Material</item>
@@ -1271,7 +1272,6 @@
     <style name="Theme.Material.Settings" parent="Theme.Material.DayNight.DarkActionBar">
         <item name="colorPrimary">@color/material_blue_grey_900</item>
         <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
-        <item name="colorAccent">@color/material_deep_teal_500</item>
 
         <item name="presentationTheme">@style/Theme.Material.Settings.Dialog.Presentation</item>
         <item name="searchDialogTheme">@style/Theme.Material.Settings.SearchBar</item>
@@ -1281,7 +1281,6 @@
     <style name="Theme.Material.Settings.BaseDialog" parent="Theme.Material.DayNight.BaseDialog">
         <item name="colorPrimary">@color/material_blue_grey_900</item>
         <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
-        <item name="colorAccent">@color/material_deep_teal_500</item>
     </style>
 
     <style name="Theme.Material.Settings.Dialog" parent="Theme.Material.Settings.BaseDialog" />
@@ -1289,7 +1288,6 @@
     <style name="Theme.Material.Settings.Dialog.BaseAlert" parent="Theme.Material.DayNight.Dialog.BaseAlert">
         <item name="colorPrimary">@color/material_blue_grey_900</item>
         <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
-        <item name="colorAccent">@color/material_deep_teal_500</item>
     </style>
 
     <style name="Theme.Material.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.BaseAlert" />
@@ -1297,7 +1295,6 @@
     <style name="Theme.Material.Settings.Dialog.Presentation" parent="Theme.Material.DayNight.Dialog.Presentation">
         <item name="colorPrimary">@color/material_blue_grey_900</item>
         <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
-        <item name="colorAccent">@color/material_deep_teal_500</item>
     </style>
 
     <style name="Theme.Material.Settings.SearchBar" parent="Theme.Material.DayNight.SearchBar">
@@ -1309,6 +1306,5 @@
     <style name="Theme.Material.Settings.CompactMenu" parent="Theme.Material.DayNight.CompactMenu">
         <item name="colorPrimary">@color/material_blue_grey_900</item>
         <item name="colorPrimaryDark">@color/material_blue_grey_950</item>
-        <item name="colorAccent">@color/material_deep_teal_500</item>
     </style>
 </resources>
diff --git a/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
new file mode 100644
index 0000000..1a50432
--- /dev/null
+++ b/core/tests/benchmarks/src/android/net/NetworkStatsBenchmark.java
@@ -0,0 +1,82 @@
+/*
+ * 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 android.net;
+
+import com.google.caliper.Param;
+import com.google.caliper.SimpleBenchmark;
+
+public class NetworkStatsBenchmark extends SimpleBenchmark {
+    private static final String UNDERLYING_IFACE = "wlan0";
+    private static final String TUN_IFACE = "tun0";
+    private static final int TUN_UID = 999999999;
+
+    @Param({"100", "1000"})
+    private int mSize;
+    private NetworkStats mNetworkStats;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mNetworkStats = new NetworkStats(0, mSize + 2);
+        int uid = 0;
+        NetworkStats.Entry recycle = new NetworkStats.Entry();
+        for (int i = 0; i < mSize; i++) {
+            recycle.iface = (i < mSize / 2) ? TUN_IFACE : UNDERLYING_IFACE;
+            recycle.uid = uid;
+            recycle.set = i % 2;
+            recycle.tag = NetworkStats.TAG_NONE;
+            recycle.rxBytes = 60000;
+            recycle.rxPackets = 60;
+            recycle.txBytes = 150000;
+            recycle.txPackets = 1500;
+            recycle.operations = 0;
+            mNetworkStats.addValues(recycle);
+            if (recycle.set == 1) {
+                uid++;
+            }
+        }
+        recycle.iface = UNDERLYING_IFACE;
+        recycle.uid = TUN_UID;
+        recycle.set = NetworkStats.SET_FOREGROUND;
+        recycle.tag = NetworkStats.TAG_NONE;
+        recycle.rxBytes = 90000 * mSize;
+        recycle.rxPackets = 40 * mSize;
+        recycle.txBytes = 180000 * mSize;
+        recycle.txPackets = 1200 * mSize;
+        recycle.operations = 0;
+        mNetworkStats.addValues(recycle);
+    }
+
+    public void timeMigrateTun(int reps) {
+        for (int i = 0; i < reps; i++) {
+            NetworkStats stats = mNetworkStats.clone();
+            stats.migrateTun(TUN_UID, TUN_IFACE, UNDERLYING_IFACE);
+        }
+    }
+
+    /**
+     * Since timeMigrateTun() includes a clone() call on the NetworkStats object,
+     * we need to measure the cost of the clone() call itself in order to get more
+     * accurate measurement on the migrateTun() method.
+     */
+    public void timeClone(int reps) {
+        for (int i = 0; i < reps; i++) {
+            NetworkStats stats = mNetworkStats.clone();
+        }
+    }
+}
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index bfaea8f..b07d33833 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -113,6 +113,9 @@
     <application android:theme="@style/Theme">
         <uses-library android:name="android.test.runner" />
         <uses-library android:name="org.apache.http.legacy" android:required="false" />
+        <meta-data
+            android:name="android.content.APP_RESTRICTIONS"
+            android:resource="@xml/app_restrictions" />
         <activity android:name="android.view.ViewAttachTestActivity" android:label="View Attach Test">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/core/tests/coretests/apks/install_jni_lib/Android.mk b/core/tests/coretests/apks/install_jni_lib/Android.mk
index b61ea8e..7322e8d 100644
--- a/core/tests/coretests/apks/install_jni_lib/Android.mk
+++ b/core/tests/coretests/apks/install_jni_lib/Android.mk
@@ -23,6 +23,14 @@
     libnativehelper
 
 LOCAL_MODULE := libframeworks_coretests_jni
+
+# this does not prevent build system
+# from installing library to /system/lib
 LOCAL_MODULE_TAGS := tests
 
+# .. we want to avoid that... so we put it somewhere
+# bionic linker cant find it without outside help (nativetests):
+LOCAL_MODULE_PATH_32 := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
+LOCAL_MODULE_PATH_64 := $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
+
 include $(BUILD_SHARED_LIBRARY)
diff --git a/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp b/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
index 957fc4a..e0b616c 100644
--- a/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
+++ b/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
@@ -27,8 +27,8 @@
     { "checkFunction", "()I", (void*) checkFunction },
 };
 
-int register_com_android_framework_coretests_JNITests(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "com/android/framework/coretests/JNITests", sMethods,
+int register_com_android_frameworks_coretests_JNITests(JNIEnv* env) {
+    return jniRegisterNativeMethods(env, "com/android/frameworks/coretests/JNITests", sMethods,
             NELEM(sMethods));
 }
 
@@ -46,7 +46,7 @@
         return JNI_ERR;
     }
 
-    if ((status = android::register_com_android_framework_coretests_JNITests(e)) < 0) {
+    if ((status = android::register_com_android_frameworks_coretests_JNITests(e)) < 0) {
         return JNI_ERR;
     }
 
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
new file mode 100644
index 0000000..5fa2405
--- /dev/null
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := install_jni_lib_open_from_apk
+
+LOCAL_JNI_SHARED_LIBRARIES_ZIP_OPTIONS := -0
+LOCAL_PAGE_ALIGN_JNI_SHARED_LIBRARIES := true
+
+include $(FrameworkCoreTests_BUILD_PACKAGE)
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/AndroidManifest.xml b/core/tests/coretests/apks/install_jni_lib_open_from_apk/AndroidManifest.xml
new file mode 100644
index 0000000..190f894
--- /dev/null
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.frameworks.coretests.install_jni_lib_open_from_apk">
+
+    <application android:hasCode="true" android:label="@string/app_name" android:extractNativeLibs="false">
+        <activity android:name="com.android.frameworks.coretests.OpenFromApkActivity"
+           android:label="@string/app_name">
+          <intent-filter>
+            <action android:name="android.intent.action.MAIN" />
+            <category android:name="android.intent.category.LAUNCHER" />
+          </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/core/res/res/drawable/time_picker_header_material.xml b/core/tests/coretests/apks/install_jni_lib_open_from_apk/res/values/strings.xml
similarity index 71%
rename from core/res/res/drawable/time_picker_header_material.xml
rename to core/tests/coretests/apks/install_jni_lib_open_from_apk/res/values/strings.xml
index ef2068a..8c2a0bf 100644
--- a/core/res/res/drawable/time_picker_header_material.xml
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/res/values/strings.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- 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.
@@ -14,7 +14,6 @@
      limitations under the License.
 -->
 
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-        android:color="?attr/colorControlHighlight">
-    <item android:drawable="?attr/colorAccent" />
-</ripple>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string name="app_name">Load From Apk Test</string>
+</resources>
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/JNITests.java b/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/JNITests.java
new file mode 100644
index 0000000..4f9176c
--- /dev/null
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/JNITests.java
@@ -0,0 +1,26 @@
+/*
+ * 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.frameworks.coretests;
+
+public class JNITests {
+  static {
+    System.loadLibrary("frameworks_coretests_jni");
+  }
+
+  public static native int checkFunction();
+}
diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/OpenFromApkActivity.java b/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/OpenFromApkActivity.java
new file mode 100644
index 0000000..524cad7c
--- /dev/null
+++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/src/com/android/frameworks/coretests/OpenFromApkActivity.java
@@ -0,0 +1,38 @@
+/*
+ * 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.frameworks.coretests;
+
+import android.app.Activity;
+import android.widget.TextView;
+import android.os.Bundle;
+
+public class OpenFromApkActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        TextView  tv = new TextView(this);
+
+        int i = JNITests.checkFunction();
+
+        tv.setText("All is well: i=" + i);
+
+        setContentView(tv);
+    }
+
+}
diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index ce0d9a2..04b0478 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -135,4 +135,8 @@
     <string name="first">Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal.</string>
     <string name="actor">Abe Lincoln</string>
     <string name="caption">Lincoln adressing the crowd at Gettysburgh</string>
+
+    <!-- RestrictionsManagerTest -->
+    <string name="restrictionManager_title">Title</string>
+    <string name="restrictionManager_desc">Description</string>
 </resources>
diff --git a/core/tests/coretests/res/xml/app_restrictions.xml b/core/tests/coretests/res/xml/app_restrictions.xml
new file mode 100644
index 0000000..c84cabcc
--- /dev/null
+++ b/core/tests/coretests/res/xml/app_restrictions.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<restrictions xmlns:android="http://schemas.android.com/apk/res/android">
+    <restriction android:key="hidden_key"
+                 android:restrictionType="hidden"/>
+    <restriction
+            android:defaultValue="true"
+            android:description="@string/restrictionManager_desc"
+            android:key="bool_key"
+            android:restrictionType="bool"
+            android:title="@string/restrictionManager_title"/>
+    <restriction
+            android:defaultValue="test"
+            android:key="string_key"
+            android:restrictionType="string"/>
+
+    <restriction android:key="int_key"
+                 android:restrictionType="integer"
+                 android:defaultValue="15"/>
+    <restriction android:key="bundle_key"
+                 android:restrictionType="bundle">
+        <restriction
+                android:key="bundle_string_key"
+                android:restrictionType="string"/>
+        <restriction
+                android:defaultValue="true"
+                android:key="bundle_bool_key"
+                android:restrictionType="bool"/>
+
+    </restriction>
+    <restriction android:key="bundle_array_key"
+                 android:restrictionType="bundle_array">
+        <restriction android:key="bundle_array_int"
+                     android:restrictionType="integer"/>
+        <restriction android:key="bundle_array_bundle_key"
+                     android:restrictionType="bundle">
+            <restriction android:key="bundle_array_bundle_int_key"
+                         android:restrictionType="integer"/>
+        </restriction>
+    </restriction>
+</restrictions>
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/content/RestrictionsManagerTest.java b/core/tests/coretests/src/android/content/RestrictionsManagerTest.java
new file mode 100644
index 0000000..8921924
--- /dev/null
+++ b/core/tests/coretests/src/android/content/RestrictionsManagerTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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 android.content;
+
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.test.AndroidTestCase;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class RestrictionsManagerTest extends AndroidTestCase {
+    private RestrictionsManager mRm;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mRm = (RestrictionsManager) mContext.getSystemService(Context.RESTRICTIONS_SERVICE);
+    }
+
+    public void testGetManifestRestrictions() {
+        String packageName = getContext().getPackageName();
+        List<RestrictionEntry> manifestRestrictions = mRm.getManifestRestrictions(packageName);
+        assertEquals(6, manifestRestrictions.size());
+        Set<String> verifiedKeys = new HashSet<>(Arrays.asList("bundle_key", "bundle_array_key",
+                "bundle_array_bundle_key"));
+        for (RestrictionEntry entry : manifestRestrictions) {
+            if ("bundle_key".equals(entry.getKey())) {
+                assertEquals("bundle_key entry should have 2 children entries",
+                        2, entry.getRestrictions().length);
+                verifiedKeys.remove(entry.getKey());
+            } else if ("bundle_array_key".equals(entry.getKey())) {
+                assertEquals("bundle_array_key should have 2 children entries",
+                        2, entry.getRestrictions().length);
+                assertNotNull(entry.getRestrictions());
+                for (RestrictionEntry childEntry : entry.getRestrictions()) {
+                    if ("bundle_array_bundle_key".equals(childEntry.getKey())) {
+                        assertNotNull(childEntry.getRestrictions());
+                        assertEquals("bundle_array_bundle_key should have 1 child entry",
+                                1, childEntry.getRestrictions().length);
+                        verifiedKeys.remove(childEntry.getKey());
+                    }
+                }
+                verifiedKeys.remove(entry.getKey());
+            }
+        }
+        assertTrue("Entries" + verifiedKeys + " were not found", verifiedKeys.isEmpty());
+    }
+
+    public void testConvertRestrictionsToBundle() {
+        String packageName = getContext().getPackageName();
+        List<RestrictionEntry> manifestRestrictions = mRm.getManifestRestrictions(packageName);
+        Bundle bundle = RestrictionsManager.convertRestrictionsToBundle(manifestRestrictions);
+        assertEquals(6, bundle.size());
+        Bundle childBundle = bundle.getBundle("bundle_key");
+        assertNotNull(childBundle);
+        assertEquals(2, childBundle.size());
+        Parcelable[] childBundleArray = bundle.getParcelableArray("bundle_array_key");
+        assertEquals(2, childBundleArray.length);
+    }
+
+}
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index fd922a2..a470de1 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -18,6 +18,8 @@
 
 import static android.net.NetworkStats.SET_DEFAULT;
 import static android.net.NetworkStats.SET_FOREGROUND;
+import static android.net.NetworkStats.SET_DBG_VPN_IN;
+import static android.net.NetworkStats.SET_DBG_VPN_OUT;
 import static android.net.NetworkStats.SET_ALL;
 import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.TAG_NONE;
@@ -346,7 +348,7 @@
             .addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
 
         assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface));
-        assertEquals(17, delta.size());
+        assertEquals(21, delta.size());
 
         // tunIface and TEST_IFACE entries are not changed.
         assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE,
@@ -377,14 +379,33 @@
                 0L, 0L, 0L, 0L, 0L);
 
         // New entries are added for new application's underlying Iface traffic
-        assertValues(delta, 13, underlyingIface, 10120, SET_DEFAULT, TAG_NONE,
+        assertContains(delta, underlyingIface, 10120, SET_DEFAULT, TAG_NONE,
                 72667L, 197L, 41872l, 219L, 0L);
-        assertValues(delta, 14, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE,
+        assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE,
                 9297L, 17L, 3936, 19L, 0L);
-        assertValues(delta, 15, underlyingIface, 10120, SET_DEFAULT, testTag1,
+        assertContains(delta, underlyingIface, 10120, SET_DEFAULT, testTag1,
                 21691L, 41L, 13179L, 46L, 0L);
-        assertValues(delta, 16, underlyingIface, 10120, SET_FOREGROUND, testTag1,
+        assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, testTag1,
                 1281L, 2L, 634L, 1L, 0L);
+
+        // New entries are added for debug purpose
+        assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE,
+                39605L, 46L, 11690, 49, 0);
+        assertContains(delta, underlyingIface, 10120, SET_DBG_VPN_IN, TAG_NONE,
+                81964, 214, 45808, 238, 0);
+        assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_IN, TAG_NONE,
+                4983, 10, 1717, 10, 0);
+        assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE,
+                126552, 270, 59215, 297, 0);
+
+    }
+
+    private static void assertContains(NetworkStats stats,  String iface, int uid, int set,
+            int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
+        int index = stats.findIndex(iface, uid, set, tag);
+        assertTrue(index != -1);
+        assertValues(stats, index, iface, uid, set, tag,
+                rxBytes, rxPackets, txBytes, txPackets, operations);
     }
 
     private static void assertValues(NetworkStats stats, int index, String iface, int uid, int set,
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index cd45017..6fa28b1 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -804,4 +804,56 @@
         assertFalse(Uri.parse("content://com.example/path/path").isPathPrefixMatch(
                 Uri.parse("content://com.example/path%2Fpath")));
     }
+
+    public void testToSafeString() {
+        checkToSafeString("tel:xxxxxx", "tel:Google");
+        checkToSafeString("tel:xxxxxxxxxx", "tel:1234567890");
+        checkToSafeString("tEl:xxx.xxx-xxxx", "tEl:123.456-7890");
+
+        checkToSafeString("sms:xxxxxx", "sms:123abc");
+        checkToSafeString("smS:xxx.xxx-xxxx", "smS:123.456-7890");
+
+        checkToSafeString("smsto:xxxxxx", "smsto:123abc");
+        checkToSafeString("SMSTo:xxx.xxx-xxxx", "SMSTo:123.456-7890");
+
+        checkToSafeString("mailto:xxxxxxx@xxxxxxx.xxx", "mailto:android@android.com");
+        checkToSafeString("Mailto:xxxxxxx@xxxxxxx.xxxxxxxxxx",
+                "Mailto:android@android.com/secret");
+
+        checkToSafeString("sip:xxxxxxx@xxxxxxx.xxxxxxxx", "sip:android@android.com:1234");
+        checkToSafeString("sIp:xxxxxxx@xxxxxxx.xxx", "sIp:android@android.com");
+
+        checkToSafeString("http://www.android.com/...", "http://www.android.com");
+        checkToSafeString("HTTP://www.android.com/...", "HTTP://www.android.com");
+        checkToSafeString("http://www.android.com/...", "http://www.android.com/");
+        checkToSafeString("http://www.android.com/...", "http://www.android.com/secretUrl?param");
+        checkToSafeString("http://www.android.com/...",
+                "http://user:pwd@www.android.com/secretUrl?param");
+        checkToSafeString("http://www.android.com/...",
+                "http://user@www.android.com/secretUrl?param");
+        checkToSafeString("http://www.android.com/...", "http://www.android.com/secretUrl?param");
+        checkToSafeString("http:///...", "http:///path?param");
+        checkToSafeString("http:///...", "http://");
+        checkToSafeString("http://:12345/...", "http://:12345/");
+
+        checkToSafeString("https://www.android.com/...", "https://www.android.com/secretUrl?param");
+        checkToSafeString("https://www.android.com:8443/...",
+                "https://user:pwd@www.android.com:8443/secretUrl?param");
+        checkToSafeString("https://www.android.com/...", "https://user:pwd@www.android.com");
+        checkToSafeString("Https://www.android.com/...", "Https://user:pwd@www.android.com");
+
+        checkToSafeString("ftp://ftp.android.com/...", "ftp://ftp.android.com/");
+        checkToSafeString("ftP://ftp.android.com/...", "ftP://anonymous@ftp.android.com/");
+        checkToSafeString("ftp://ftp.android.com:2121/...",
+                "ftp://root:love@ftp.android.com:2121/");
+
+        checkToSafeString("unsupported://ajkakjah/askdha/secret?secret",
+                "unsupported://ajkakjah/askdha/secret?secret");
+        checkToSafeString("unsupported:ajkakjah/askdha/secret?secret",
+                "unsupported:ajkakjah/askdha/secret?secret");
+    }
+
+    private void checkToSafeString(String expectedSafeString, String original) {
+        assertEquals(expectedSafeString, Uri.parse(original).toSafeString());
+    }
 }
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 6659769..c517201 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -58,11 +58,11 @@
         <group gid="log" />
     </permission>
 
-    <permission name="android.permission.READ_EXTERNAL_STORAGE" >
+    <permission name="android.permission.READ_EXTERNAL_STORAGE" perUser="true" >
         <group gid="sdcard_r" />
     </permission>
 
-    <permission name="android.permission.WRITE_EXTERNAL_STORAGE" >
+    <permission name="android.permission.WRITE_EXTERNAL_STORAGE" perUser="true" >
         <group gid="sdcard_r" />
         <group gid="sdcard_rw" />
     </permission>
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index 38321a3..a0b26f3 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -49,6 +49,8 @@
 ################################
 # Do not include Motoya on space-constrained devices
 ifneq ($(SMALLER_FONT_FOOTPRINT),true)
+# Do not include Motoya if we are including full NotoSans
+ifneq ($(FONT_NOTOSANS_FULL),true)
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := MTLmr3m.ttf
@@ -59,6 +61,7 @@
 include $(BUILD_PREBUILT)
 extra_font_files += MTLmr3m.ttf
 
+endif  # !FONT_NOTOSANS_FULL
 endif  # !SMALLER_FONT_FOOTPRINT
 
 ################################
diff --git a/docs/html/google/auth/http-auth.jd b/docs/html/google/auth/http-auth.jd
index 804ba12..7d34d89 100644
--- a/docs/html/google/auth/http-auth.jd
+++ b/docs/html/google/auth/http-auth.jd
@@ -95,7 +95,7 @@
 </pre>
 <p>For example, you're using a debug-key with Eclipse, then the command looks like this:</p>
 <pre class="no-pretty-print">
-keytool -exportcert -alias androiddebugkey-keystore ~/.android/debug.keystore -list -v
+keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v
 </pre>
 <p>Then the keystore password is "android".</p>
 </li>
diff --git a/docs/html/google/gcm/c2dm.jd b/docs/html/google/gcm/c2dm.jd
index 6ae7c1a..d0f8c711 100644
--- a/docs/html/google/gcm/c2dm.jd
+++ b/docs/html/google/gcm/c2dm.jd
@@ -33,7 +33,10 @@
 </div>
 </div>
 
-<p>Android Cloud to Device Messaging (C2DM) is deprecated. The C2DM service will continue to be maintained in the short term, but C2DM will accept no new users, and it will grant no new quotas. <strong>C2DM developers are strongly encouraged to move to Google Cloud Messaging (GCM)</strong>. GCM is the next generation of C2DM.</p>
+<p>Android Cloud to Device Messaging (C2DM) was officially deprecated on June 26, 2012, and has been
+ shut down completely as of April 1, 2015.  <strong>C2DM developers are strongly encouraged to move
+  to Google Cloud Messaging (GCM)</strong>. GCM is the next generation of C2DM.</p>
+
 <p>This document is addressed to  C2DM developers who are moving to GCM. It describes the differences between GCM and C2DM, and explains how to migrate existing C2DM apps to GCM.</p>
 
 
diff --git a/docs/html/guide/index.jd b/docs/html/guide/index.jd
index d78a1b1..cb4f65c 100644
--- a/docs/html/guide/index.jd
+++ b/docs/html/guide/index.jd
@@ -30,7 +30,7 @@
 work in the background.</p>
 
 <p>From one component you can start another component using an <em>intent</em>. You can even start
-a component in a different app, such an activity in a maps app to show an address. This model
+a component in a different app, such as an activity in a maps app to show an address. This model
 provides multiple entry points for a single app and allows any app to behave as a user's "default"
 for an action that other apps may invoke.</p>
 
diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd
index c1256f9..99e64d9 100644
--- a/docs/html/guide/topics/manifest/activity-element.jd
+++ b/docs/html/guide/topics/manifest/activity-element.jd
@@ -13,10 +13,11 @@
           android:<a href="#clear">clearTaskOnLaunch</a>=["true" | "false"]
           android:<a href="#config">configChanges</a>=["mcc", "mnc", "locale",
                                  "touchscreen", "keyboard", "keyboardHidden",
-                                 "navigation", "screenLayout", "fontScale", "uiMode",
-                                 "orientation", "screenSize", "smallestScreenSize"]
-          android:<a href="#dlmode">documentLaunchMode</a>=["intoExisting", "always",
-                                  "none", "never"]
+                                 "navigation", "screenLayout", "fontScale",
+                                 "uiMode", "orientation", "screenSize",
+                                 "smallestScreenSize"]
+          android:<a href="#dlmode">documentLaunchMode</a>=["intoExisting" | "always" |
+                                  "none" | "never"]
           android:<a href="#enabled">enabled</a>=["true" | "false"]
           android:<a href="#exclude">excludeFromRecents</a>=["true" | "false"]
           android:<a href="#exported">exported</a>=["true" | "false"]
diff --git a/docs/html/images/tv/channel-info.png b/docs/html/images/tv/channel-info.png
new file mode 100644
index 0000000..5a48078
--- /dev/null
+++ b/docs/html/images/tv/channel-info.png
Binary files differ
diff --git a/docs/html/images/tv/do-not-attempt.png b/docs/html/images/tv/do-not-attempt.png
new file mode 100644
index 0000000..18a8775
--- /dev/null
+++ b/docs/html/images/tv/do-not-attempt.png
Binary files differ
diff --git a/docs/html/images/tv/prog-guide.png b/docs/html/images/tv/prog-guide.png
new file mode 100644
index 0000000..caa2278
--- /dev/null
+++ b/docs/html/images/tv/prog-guide.png
Binary files differ
diff --git a/docs/html/images/tv/tvinput-life.png b/docs/html/images/tv/tvinput-life.png
new file mode 100644
index 0000000..fc53f89
--- /dev/null
+++ b/docs/html/images/tv/tvinput-life.png
Binary files differ
diff --git a/docs/html/tools/revisions/build-tools.jd b/docs/html/tools/revisions/build-tools.jd
index b08fbcf..e8706c1 100644
--- a/docs/html/tools/revisions/build-tools.jd
+++ b/docs/html/tools/revisions/build-tools.jd
@@ -78,6 +78,19 @@
 <div class="toggle-content opened">
   <p><a href="#" onclick="return toggleContent(this)">
     <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+      alt=""/>Build Tools, Revision 22.0.1</a> <em>(March 2015)</em>
+  </p>
+  <div class="toggle-content-toggleme">
+    <p>Fixed compatibility issues with
+    <a href="{@docRoot}guide/topics/renderscript/compute.html">RenderScript</a> kernels on
+    Android 4.4 (API level 19) to Android 4.1 (API level 16) devices.</p>
+  </div>
+</div>
+
+
+<div class="toggle-content closed">
+  <p><a href="#" onclick="return toggleContent(this)">
+    <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
       alt=""/>Build Tools, Revision 22.0.0</a> <em>(March 2015)</em>
   </p>
   <div class="toggle-content-toggleme">
diff --git a/docs/html/training/custom-views/optimizing-view.jd b/docs/html/training/custom-views/optimizing-view.jd
index 7f2e762..022618b 100644
--- a/docs/html/training/custom-views/optimizing-view.jd
+++ b/docs/html/training/custom-views/optimizing-view.jd
@@ -12,33 +12,21 @@
     <div id="tb">
 
         <h2>This lesson teaches you to</h2>
-        <ol>
-            <li><a href="#less">Do Less, Less Frequently</a></li>
-            <li><a href="#accelerate">Use Hardware Acceleration</a></li>
-        </ol>
-
-        <h2>You should also read</h2>
         <ul>
-            <li><a href="{@docRoot}guide/topics/graphics/hardware-accel.html">
-                Hardware Acceleration
-            </a>
-        </li>
-    </ul>
-<h2>Try it out</h2>
-<div class="download-box">
-<a href="{@docRoot}shareables/training/CustomView.zip"
-class="button">Download the sample</a>
-<p class="filename">CustomView.zip</p>
-</div>
-</div>
+            <li><a href="#less">Do Less, Less Frequently</a></li>
+        </ul>
+        <h2>Try it out</h2>
+        <div class="download-box">
+            <a href="{@docRoot}shareables/training/CustomView.zip"
+                class="button">Download the sample</a>
+            <p class="filename">CustomView.zip</p>
         </div>
-
+    </div>
+</div>
 
 <p>Now that you have a well-designed view that responds to gestures and transitions between states,
-you need to ensure
-that the view runs fast. To avoid a UI that feels sluggish or stutters during playback, you must
-ensure that your
-animations consistently run at 60 frames per second.</p>
+ensure that the view runs fast. To avoid a UI that feels sluggish or stutters during playback,
+ensure that animations consistently run at 60 frames per second.</p>
 
 <h2 id="less">Do Less, Less Frequently</h2>
 
@@ -52,19 +40,13 @@
 allocation while an
 animation is running.</p>
 
-<p>In addition to making {@link android.view.View#onDraw onDraw()} leaner, you should also make sure
+<p>In addition to making {@link android.view.View#onDraw onDraw()} leaner, also make sure
 it's called as
 infrequently as possible. Most calls to {@link android.view.View#onDraw onDraw()} are the result of
 a call to {@link
 android.view.View#invalidate() invalidate()}, so eliminate unnecessary calls to {@link
 android.view.View#invalidate()
-invalidate()}. When possible, call the four-parameter variant of {@link
-android.view.View#invalidate() invalidate()}
-rather than the version that takes no parameters. The no-parameter variant invalidates the entire
-view, while the
-four-parameter variant invalidates only a specified portion of the view. This approach allows draw calls to
-be more efficient and
-can eliminate unnecessary invalidation of views that fall outside the invalid rectangle.</p>
+invalidate()}.</p>
 
 <p>Another very expensive operation is traversing layouts. Any time a view calls {@link
 android.view.View#requestLayout()
@@ -78,7 +60,7 @@
 as shallow as
 possible.</p>
 
-<p>If you have a complex UI, you should consider writing a custom {@link android.view.ViewGroup
+<p>If you have a complex UI, consider writing a custom {@link android.view.ViewGroup
 ViewGroup} to perform
 its layout. Unlike the built-in views, your custom view can make application-specific assumptions
 about the size and
@@ -88,89 +70,3 @@
 views, but it never
 measures them. Instead, it sets their sizes directly according to its own custom layout
 algorithm.</p>
-
-<h2 id="accelerate">Use Hardware Acceleration</h2>
-
-<p>As of Android 3.0, the Android 2D graphics system can be accelerated by the GPU (Graphics
-Processing Unit) hardware
-found in most newer Android devices. GPU hardware acceleration can result in a tremendous
-performance increase for many
-applications, but it isn't the right choice for every application. The Android framework
-gives you the ability to finely control which parts of your application are or are not
-hardware accelerated.</p>
-
-<p>See <a href="{@docRoot}guide/topics/graphics/hardware-accel.html">Hardware Acceleration</a>
-        in the Android Developers Guide for directions on how to enable acceleration at the
-        application, activity, or window level. Notice  that in addition to the directions in
-        the developer guide, you must also set your application's target API to 11 or higher by
-        specifying {@code &lt;uses-sdk
-        android:targetSdkVersion="11"/&gt;} in your {@code AndroidManifest.xml} file.</p>
-
-<p>Once you've enabled hardware acceleration, you may or may not see a performance increase.
-Mobile GPUs are very good at certain tasks, such as scaling, rotating, and translating
-bitmapped images. They are not particularly good at other tasks, such as drawing lines or curves. To
-get the most out of GPU acceleration, you should maximize the number of operations that the GPU is
-good at, and minimize the number of operations that the GPU isn't good at.</p>
-
-<p>In the PieChart example, for instance, drawing the pie is relatively expensive. Redrawing the pie
-each time it's
-rotated causes the UI to feel sluggish. The solution is to place the pie chart into a child
-{@link android.view.View} and set that
-{@link android.view.View}'s
-<a href="{@docRoot}reference/android/view/View.html#setLayerType(int, android.graphics.Paint)">
-    layer type</a> to {@link android.view.View#LAYER_TYPE_HARDWARE}, so that the GPU can cache it as
-a static
-image. The sample
-defines the child view as an inner class of {@code PieChart}, which minimizes the amount of code
-changes that are needed
-to implement this solution.</p>
-
-<pre>
-   private class PieView extends View {
-
-       public PieView(Context context) {
-           super(context);
-           if (!isInEditMode()) {
-               setLayerType(View.LAYER_TYPE_HARDWARE, null);
-           }
-       }
-       
-       &#64;Override
-       protected void onDraw(Canvas canvas) {
-           super.onDraw(canvas);
-
-           for (Item it : mData) {
-               mPiePaint.setShader(it.mShader);
-               canvas.drawArc(mBounds,
-                       360 - it.mEndAngle,
-                       it.mEndAngle - it.mStartAngle,
-                       true, mPiePaint);
-           }
-       }
-
-       &#64;Override
-       protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-           mBounds = new RectF(0, 0, w, h);
-       }
-
-       RectF mBounds;
-   }
-</pre>
-
-<p>After this code change, {@code PieChart.PieView.onDraw()} is called only when the view is first
-shown. During the rest
-of the application's lifetime, the pie chart is cached as an image, and redrawn at different
-rotation angles by the GPU.
-GPU hardware is particularly good at this sort of thing, and the performance difference is
-immediately noticeable.</p>
-
-<p>There is a tradeoff, though. Caching images as hardware layers consumes video memory, which is a
-limited resource.
-For this reason, the final version of {@code PieChart.PieView} only sets its layer type to
-{@link android.view.View#LAYER_TYPE_HARDWARE}
-while the user is actively scrolling. At all other times, it sets its layer type to
-{@link android.view.View#LAYER_TYPE_NONE}, which
-allows the GPU to stop caching the image.</p>
-
-<p>Finally, don't forget to profile your code. Techniques that improve performance on one view
-might negatively affect performance on another.</p>
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 11ae1a6..2873b5b3 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -998,10 +998,25 @@
            Building TV Games</a>
       </li>
 
-      <li>
-        <a href="<?cs var:toroot ?>training/tv/tif/index.html"
+      <li class="nav-section">
+        <div class="nav-section-header">
+          <a href="<?cs var:toroot ?>training/tv/tif/index.html"
            description="How to build Live TV apps.">
            Building Live TV Apps</a>
+        </div>
+        <ul>
+          <li>
+            <a href="<?cs var:toroot ?>training/tv/tif/tvinput.html">
+              Developing a TV Input Service</a>
+          <li>
+            <a href="<?cs var:toroot ?>training/tv/tif/channel.html">
+              Working with Channel Data</a>
+          </li>
+          <li>
+            <a href="<?cs var:toroot ?>training/tv/tif/ui.html">
+              Managing User Interaction</a>
+          </li>
+        </ul>
       </li>
 
       <li>
diff --git a/docs/html/training/tv/tif/channel.jd b/docs/html/training/tv/tif/channel.jd
new file mode 100644
index 0000000..999f1ca
--- /dev/null
+++ b/docs/html/training/tv/tif/channel.jd
@@ -0,0 +1,239 @@
+page.title=Working with Channel Data
+page.tags=tv, tif
+helpoutsWidget=true
+
+trainingnavtop=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+  <h2>This lesson teaches you to</h2>
+  <ol>
+    <li><a href="#permission">Get Permission</a></li>
+    <li><a href="#register">Register Channels in the Database</a></li>
+    <li><a href="#update">Update Channel Data</a></li>
+  </ol>
+  <h2>Try It Out</h2>
+  <ul>
+    <li><a class="external-link" href="https://github.com/googlesamples/androidtv-sample-inputs">
+      TV Input Service sample app</a></li>
+  </ul>
+</div>
+</div>
+
+<p>Your TV input must provide Electronic Program Guide (EPG) data for at least one channel in its
+setup activity. You should also periodically update that data, with consideration for the size of
+the update and the processing thread that handles it. This lesson discusses creating and updating
+channel and program data on the system database with these considerations in mind.</p>
+
+<p>&nbsp;</p>
+
+<h2 id="permission">Get Permission</h2>
+
+<p>In order for your TV input to work with EPG data, it must declare the
+read and write permissions in its Android manifest file as follows:</p>
+
+<pre>
+&lt;uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" /&gt;
+&lt;uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" /&gt;
+</pre>
+
+<h2 id="register">Register Channels in the Database</h2>
+
+<p>The Android TV system database maintains records of channel data for TV inputs. In your setup
+activity, for each of your channels, you must map your channel data to the following fields of the
+{@link android.media.tv.TvContract.Channels} class:</p>
+
+<ul>
+  <li>{@link android.media.tv.TvContract.Channels#COLUMN_DISPLAY_NAME} - the displayed name of the
+  channel</li>
+  <li>{@link android.media.tv.TvContract.Channels#COLUMN_DISPLAY_NUMBER} - the displayed channel
+  number</li>
+  <li>{@link android.media.tv.TvContract.Channels#COLUMN_INPUT_ID} - the ID of the TV input service</li>
+  <li>{@link android.media.tv.TvContract.Channels#COLUMN_SERVICE_TYPE} - the channel's service type</li>
+  <li>{@link android.media.tv.TvContract.Channels#COLUMN_TYPE} - the channel's broadcast standard
+  type</li>
+  <li>{@link android.media.tv.TvContract.Channels#COLUMN_VIDEO_FORMAT} - the default video format
+  for the channel</li>
+</ul>
+
+<p>Although the TV input framework is generic enough to handle both traditional broadcast and
+over-the-top (OTT) content without any distinction, you may want to define the following columns in
+addition to those above to better identify traditional broadcast channels:</p>
+
+<ul>
+  <li>{@link android.media.tv.TvContract.Channels#COLUMN_ORIGINAL_NETWORK_ID} - the television
+  network ID</li>
+  <li>{@link android.media.tv.TvContract.Channels#COLUMN_SERVICE_ID} - the service ID</li>
+  <li>{@link android.media.tv.TvContract.Channels#COLUMN_TRANSPORT_STREAM_ID} - the transport stream
+  ID</li>
+</ul>
+
+<p>For internet streaming based TV inputs, assign your own values to the above accordingly so that
+each channel can be identified uniquely.</p>
+
+<p>Pull your channel metadata (in XML, JSON, or whatever) from your backend server, and in your setup
+activity map the values to the system database as follows:</p>
+
+<pre>
+ContentValues values = new ContentValues();
+
+values.put(Channels.COLUMN_DISPLAY_NUMBER, channel.mNumber);
+values.put(Channels.COLUMN_DISPLAY_NAME, channel.mName);
+values.put(Channels.COLUMN_ORIGINAL_NETWORK_ID, channel.mOriginalNetworkId);
+values.put(Channels.COLUMN_TRANSPORT_STREAM_ID, channel.mTransportStreamId);
+values.put(Channels.COLUMN_SERVICE_ID, channel.mServiceId);
+values.put(Channels.COLUMN_VIDEO_FORMAT, channel.mVideoFormat);
+
+Uri uri = context.getContentResolver().insert(TvContract.Channels.CONTENT_URI, values);
+</pre>
+
+<p>In the example above, <code>channel</code> is an object which holds channel metadata from the
+backend server.</p>
+
+<h3 id="art">Present Channel and Program Information</h2>
+
+<p>The system TV app presents channel and program information to users as they flip through channels,
+as shown in figure 1. To make sure the channel and program information works with the system TV app's
+channel and program information presenter, follow the guidelines below.</p>
+
+<ol>
+<li><strong>Channel number</strong> ({@link android.media.tv.TvContract.Channels#COLUMN_DISPLAY_NUMBER})
+<li><strong>Icon</strong>
+(<a href="guide/topics/manifest/application-element.html#icon"><code>android:icon</code></a> in the
+TV input's manifest)</li>
+<li><strong>Program description</strong> ({@link android.media.tv.TvContract.Programs#COLUMN_SHORT_DESCRIPTION})
+<li><strong>Program title</strong> ({@link android.media.tv.TvContract.Programs#COLUMN_TITLE})</li>
+<li><strong>Channel logo</strong> ({@link android.media.tv.TvContract.Channels.Logo})
+  <ul>
+    <li>Use the color #EEEEEE to match the surrounding text</li>
+    <li>Don't include padding
+  </ul></li>
+<li><strong>Poster art</strong> ({@link android.media.tv.TvContract.Programs#COLUMN_POSTER_ART_URI})
+  <ul>
+    <li>Aspect ratio between 16:9 and 4:3</li>
+  </ul>
+</ol>
+
+<img src="{@docRoot}images/tv/channel-info.png" id="figure1">
+<p class="img-caption">
+  <strong>Figure 1.</strong> The system TV app channel and program information presenter.
+</p>
+
+<p>The system TV app provides the same information through the program guide, including poster art,
+as shown in figure 2.</p>
+
+<img src="{@docRoot}images/tv/prog-guide.png" id="figure2">
+<p class="img-caption">
+  <strong>Figure 2.</strong> The system TV app program guide.
+</p>
+
+<h2 id="update">Update Channel Data</h2>
+
+<p>When updating existing channel data, use the
+{@link android.content.ContentProvider#update(android.net.Uri, android.content.ContentValues,
+java.lang.String, java.lang.String[]) update()}
+method instead of deleting and re-adding the data. You can identify the current version of the data
+by using {@link android.media.tv.TvContract.Channels#COLUMN_VERSION_NUMBER Channels.COLUMN_VERSION_NUMBER}
+and {@link android.media.tv.TvContract.Programs#COLUMN_VERSION_NUMBER Programs.COLUMN_VERSION_NUMBER}
+when choosing the records to update.</p>
+
+<p class="note"><strong>Note:</strong> Adding channel data to the {@link android.content.ContentProvider}
+can take time. Only add current programs (those within two hours of the current time) when you update,
+and use a <a href="{@docRoot}training/sync-adapters/creating-sync-adapter.html">Sync Adapter</a> to
+update the rest of the channel data in the background. See the <a class="external-link" href="https://github.com/googlesamples/androidtv-sample-inputs/blob/master/app/src/main/java/com/example/android/sampletvinput/syncadapter/SyncAdapter.java">
+Android TV Live TV Sample App</a> for an example.</p>
+
+<h3 id="batch">Batch Loading Channel Data</h3>
+
+<p>When updating the system database with a large amount of channel data, use the {@link android.content.ContentResolver}
+{@link android.content.ContentResolver#applyBatch applyBatch()}
+or
+{@link android.content.ContentResolver#bulkInsert(android.net.Uri, android.content.ContentValues[]) bulkInsert()}
+method. Here's an example using {@link android.content.ContentResolver#applyBatch applyBatch()}:<p>
+
+<pre>
+ArrayList&lt;ContentProviderOperation&gt; ops = new ArrayList&lt;&gt;();
+int programsCount = mChannelInfo.mPrograms.size();
+for (int j = 0; j &lt; programsCount; ++j) {
+    ProgramInfo program = mChannelInfo.mPrograms.get(j);
+    ops.add(ContentProviderOperation.newInsert(
+            TvContract.Programs.CONTENT_URI)
+            .withValues(programs.get(j))
+            .withValue(Programs.COLUMN_START_TIME_UTC_MILLIS,
+                    programStartSec * 1000)
+            .withValue(Programs.COLUMN_END_TIME_UTC_MILLIS,
+                    (programStartSec + program.mDurationSec) * 1000)
+            .build());
+    programStartSec = programStartSec + program.mDurationSec;
+    if (j % 100 == 99 || j == programsCount - 1) {
+        try {
+            <strong>getContentResolver().applyBatch(TvContract.AUTHORITY, ops);</strong>
+        } catch (RemoteException | OperationApplicationException e) {
+            Log.e(TAG, "Failed to insert programs.", e);
+            return;
+        }
+        ops.clear();
+    }
+}
+</pre>
+
+<h3 id="async">Processing Channel Data Asynchronously</h3>
+
+<p>Data manipulation, such as fetching a stream from the server or accessing the database, should
+not block the UI thread. Using an {@link android.os.AsyncTask} is one
+way to perform updates asynchronously.  For example, when loading channel info from a backend server,
+you can use {@link android.os.AsyncTask} as follows:</p>
+
+<pre>
+private static class LoadTvInputTask extends AsyncTask&lt;Uri, Void, Void>&gt; {
+
+    private Context mContext;
+
+    public LoadTvInputTask(Context context) {
+        mContext = context;
+    }
+
+    &#64;Override
+    protected Void doInBackground(Uri... uris) {
+        try {
+            fetchUri(uris[0]);
+        } catch (IOException e) {
+          Log.d(“LoadTvInputTask”, “fetchUri error”);
+        }
+        return null;
+    }
+
+    private void fetchUri(Uri videoUri) throws IOException {
+        InputStream inputStream = null;
+        try {
+            inputStream = mContext.getContentResolver().openInputStream(videoUri);
+            XmlPullParser parser = Xml.newPullParser();
+            try {
+                parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
+                parser.setInput(inputStream, null);
+                sTvInput = ChannelXMLParser.parseTvInput(parser);
+                sSampleChannels = ChannelXMLParser.parseChannelXML(parser);
+            } catch (XmlPullParserException e) {
+                e.printStackTrace();
+            }
+        } finally {
+            if (inputStream != null) {
+                inputStream.close();
+            }
+        }
+    }
+}
+</pre>
+
+<p>If you need to update EPG data on a regular basis, consider using
+a <a href="{@docRoot}training/sync-adapters/creating-sync-adapter.html">
+Sync Adapter</a> or {@link android.app.job.JobScheduler} to run the update process during idle time,
+such as every day at 3:00 a.m. See the <a class="external-link" href="https://github.com/googlesamples/androidtv-sample-inputs/blob/master/app/src/main/java/com/example/android/sampletvinput/syncadapter/SyncAdapter.java">
+Android TV live TV sample app</a> for an example.</p>
+
+<p>Other techniques to separate the data update tasks from the UI thread include using the
+{@link android.os.HandlerThread} class, or you may implement your own using {@link android.os.Looper}
+and {@link android.os.Handler} classes.  See <a href="{@docRoot}guide/components/processes-and-threads.html">
+Processes and Threads</a> for more information.</p>
\ No newline at end of file
diff --git a/docs/html/training/tv/tif/index.jd b/docs/html/training/tv/tif/index.jd
index 9c10850..5739294 100644
--- a/docs/html/training/tv/tif/index.jd
+++ b/docs/html/training/tv/tif/index.jd
@@ -1,17 +1,26 @@
 page.title=Building Live TV Apps
 page.tags=tv, tif
 helpoutsWidget=true
-page.article=true
+startpage=true
 
 @jd:body
 
 <div id="tb-wrapper">
 <div id="tb">
+  <h2>Dependencies and Prerequisites</h2>
+  <ul>
+    <li>Android 5.0 (API level 21) or higher</li>
+  </ul>
   <h2>You should also read</h2>
   <ul>
     <li><a href="{@docRoot}reference/android/media/tv/package-summary.html">
       android.media.tv</a></li>
   </ul>
+  <h2>Try It Out</h2>
+  <ul>
+    <li><a class="external-link" href="https://github.com/googlesamples/androidtv-sample-inputs">
+      TV Input Service sample app</a></li>
+  </ul>
 </div>
 </div>
 
@@ -44,6 +53,17 @@
   Building a TV input service for your content can help make it more accessible on TV devices.
 </p>
 
-<p>For more information about TV Input Framework, see the
-<a href="{@docRoot}reference/android/media/tv/package-summary.html">android.media.tv</a>
-reference.</p>
+<h2>Topics</h2>
+
+<dl>
+  <dt><b><a href="tvinput.html">Developing a TV Input Service</a></b></dt>
+    <dd>Learn how to develop a TV input service, which works with the system TV app.</dd>
+
+  <dt><b><a href="channel.html">Working with Channel Data</a></b></dt>
+    <dd>Learn how to describe channel and program data for the system.</dd>
+
+  <dt><b><a href="ui.html">Managing User Interaction</a></b></dt>
+    <dd>Learn how to present overlays, manage content availability, and handle content selection.</dd>
+</dl>
+
+
diff --git a/docs/html/training/tv/tif/tvinput.jd b/docs/html/training/tv/tif/tvinput.jd
new file mode 100644
index 0000000..91f8ded
--- /dev/null
+++ b/docs/html/training/tv/tif/tvinput.jd
@@ -0,0 +1,177 @@
+page.title=Developing a TV Input Service
+page.tags=tv, tif
+helpoutsWidget=true
+
+trainingnavtop=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+  <h2>This lesson teaches you to</h2>
+  <ol>
+    <li><a href="#manifest">Declare Your TV Input Service in the Manifest</a></li>
+    <li><a href="#tvinput">Define Your TV Input Service</a></li>
+    <li><a href="#setup">Define Setup and Settings Activities</a></li>
+  </ol>
+  <h2>You should also read</h2>
+  <ul>
+    <li><a href="{@docRoot}reference/android/media/tv/package-summary.html">
+      android.media.tv</a></li>
+    <li><a class="external-lin" href="http://source.android.com/devices/tv/index.html">
+      TV Input Framework</a></li>
+  </ul>
+  <h2>Try It Out</h2>
+  <ul>
+    <li><a class="external-link" href="https://github.com/googlesamples/androidtv-sample-inputs">
+      TV Input Service sample app</a></li>
+  </ul>
+</div>
+</div>
+
+<p>A TV input service represents a media stream source, and lets you present your media content in a
+linear, broadcast TV fashion as channels and programs. With the TV input service, you can provide
+parental controls, program guide information, and content ratings. The TV input service works
+with the Android system TV app, developed for the device and immutable by third-party apps, which
+ultimately controls and presents content on the TV. See
+<a class="external-link" href="http://source.android.com/devices/tv/index.html">
+TV Input Framework</a> for more information about the framework architecture and its components.</p>
+
+<p>To develop a TV input service, you implement the following components:</p>
+
+<ul>
+  <li>{@link android.media.tv.TvInputService} provides long-running and background availability for
+  the TV input</li>
+  <li>{@link android.media.tv.TvInputService.Session} maintains the TV input state and communicates
+  with the hosting app</li>
+  <li>{@link android.media.tv.TvContract} describes the channels and programs available to the TV
+  input</li>
+  <li>{@link android.media.tv.TvContract.Channels} represents information about a TV channel</li>
+  <li>{@link android.media.tv.TvContract.Programs} describes a TV program with data such as program
+  title and start time</li>
+  <li>{@link android.media.tv.TvTrackInfo} represents an audio, video, or subtitle track</li>
+  <li>{@link android.media.tv.TvContentRating} describes a content rating, allows for custom content
+  rating schemes</li>
+  <li>{@link android.media.tv.TvInputManager} provides an API to the system TV app and manages
+  the interaction with TV inputs and apps</li>
+</ul>
+
+<h2 id="manifest">Declare Your TV Input Service in the Manifest</h2>
+
+<p>Your app manifest must declare your {@link android.media.tv.TvInputService}. Within that
+declaration, specify the {@link android.Manifest.permission#BIND_TV_INPUT} permission to allow the
+service to connect the TV input to the system. A system service (<code>TvInputManagerService</code>)
+performs the binding and has that permission. The system TV app sends requests to TV input services
+via the {@link android.media.tv.TvInputManager} interface. The service declaration must also
+include an intent filter that specifies the {@link android.media.tv.TvInputService}
+as the action to perform with the intent. Also within the service declaration, declare the service
+meta data in a separate XML resource. The service declaration, the intent filter and the service
+meta data are described in the following example.</p>
+
+<pre>
+&lt;service android:name="com.example.sampletvinput.SampleTvInput"
+    android:label="@string/sample_tv_input_label"
+    android:permission="android.permission.BIND_TV_INPUT"&gt;
+    &lt;intent-filter&gt;
+        &lt;action android:name="android.media.tv.TvInputService" /&gt;
+    &lt;/intent-filter&gt;
+    &lt;meta-data android:name="android.media.tv.input"
+      android:resource="@xml/sample_tv_input" /&gt;
+&lt;/service&gt;
+</pre>
+
+<p>Define the service meta data in separate XML file, as shown in the following example. The service
+meta data must include a setup interface that describes the TV input's initial configuration and
+channel scan. Also, the service meta data may (optionally) describe a settings activity for users to
+modify the TV input's behavior. The service meta data file is located in the XML resources directory
+for your application and must match the name of the resource in the manifest. Using the example
+manifest entries above, you would create an XML file in the location
+<code>res/xml/sample_tv_input.xml</code>, with the following contents:</p>
+
+<pre>
+&lt;tv-input xmlns:android="http://schemas.android.com/apk/res/android"
+  &lt;!-- Required: activity for setting up the input --&gt;
+  android:setupActivity="com.example.sampletvinput.SampleTvInputSetupActivity"
+  &lt;!-- Optional: activity for controlling the settings --&gt;
+  android:settingsActivity="com.example.sampletvinput.SampleTvInputSettingsActivity" /&gt;
+</pre>
+
+<h2 id="tvinput">Define Your TV Input Service</h2>
+
+<div class="figure">
+<img id="tvinputlife" src="{@docRoot}images/tv/tvinput-life.png" alt=""/>
+<p class="img-caption"><strong>Figure 1.</strong>TvInputService lifecycle.</p>
+</div>
+
+<p>For your service, you extend the {@link android.media.tv.TvInputService} class. A
+{@link android.media.tv.TvInputService} implementation is a
+<a href="{@docRoot}guide/components/bound-services.html">bound service</a> where the system service
+(<code>TvInputManagerService</code>) is the client that binds to it. The service life cycle methods
+you need to implement are illustrated in figure 1.</p>
+
+<p>The {@link android.app.Service#onCreate()} method initializes and starts the
+{@link android.os.HandlerThread} which provides a process thread separate from the UI thread to
+handle system-driven actions. In the following example, the {@link android.app.Service#onCreate()}
+method initializes the {@link android.view.accessibility.CaptioningManager} and prepares to handle
+the {@link android.media.tv.TvInputManager#ACTION_BLOCKED_RATINGS_CHANGED}
+and {@link android.media.tv.TvInputManager#ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED} actions. These
+actions describe system intents fired when the user changes the parental control settings, and when
+there is a change on the list of blocked ratings.</p>
+
+<pre>
+&#64;Override
+public void onCreate() {
+    super.onCreate();
+    mHandlerThread = new HandlerThread(getClass()
+      .getSimpleName());
+    mHandlerThread.start();
+    mDbHandler = new Handler(mHandlerThread.getLooper());
+    mHandler = new Handler();
+    mCaptioningManager = (CaptioningManager)
+      getSystemService(Context.CAPTIONING_SERVICE);
+
+    setTheme(android.R.style.Theme_Holo_Light_NoActionBar);
+
+    mSessions = new ArrayList&lt;BaseTvInputSessionImpl&gt;();
+    IntentFilter intentFilter = new IntentFilter();
+    intentFilter.addAction(TvInputManager
+      .ACTION_BLOCKED_RATINGS_CHANGED);
+    intentFilter.addAction(TvInputManager
+      .ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED);
+    registerReceiver(mBroadcastReceiver, intentFilter);
+}
+</pre>
+
+<p> See <a href="{@docRoot}training/tv/tif/ui.html#control">
+Control Content</a> for more information about working with blocked content and providing
+parental control. See {@link android.media.tv.TvInputManager} for more system-driven actions that
+you may want to handle in your TV input service.</p>
+
+<p>The {@link android.media.tv.TvInputService} creates a
+{@link android.media.tv.TvInputService.Session} that implements {@link android.os.Handler.Callback}
+to handle player state changes. With {@link android.media.tv.TvInputService.Session#onSetSurface(android.view.Surface) onSetSurface()},
+the {@link android.media.tv.TvInputService.Session} sets the {@link android.view.Surface} with the
+video content. See <a href="{@docRoot}training/tv/tif/ui.html#surface">Integrate Player with Surface</a>
+for more information about working with {@link android.view.Surface} to render video.</p>
+
+<p>The {@link android.media.tv.TvInputService.Session} handles the
+{@link android.media.tv.TvInputService.Session#onTune(android.net.Uri) onTune()}
+event when the user selects a channel, and notifies the system TV app for changes in the content and
+content meta data. These <code>notify()</code>code> methods are described in
+<a href="{@docRoot}training/tv/tif/ui.html#control">
+Control Content</a> and <a href="training/tv/tif/ui.html#track">Handle Track Selection</a> further
+in this training.</p>
+
+<h2 id="setup">Define Setup and Settings Activities</h2>
+
+<p>The system TV app works with the setup and settings activities you define for your TV input. The
+setup activity is required and must provide at least one channel record for the system database. The
+system TV app will invoke the setup activity when it cannot find a channel for the TV input.
+<p>The setup activity describes to the system TV app the channels made available through the TV
+input, as demonstrated in the next lesson, <a href="{@docRoot}training/tv/tif/channel.html">Creating
+and Updating Channel Data</a>.</p>
+
+<p>The settings activity is optional. You can define a settings activity to turn on parental
+controls, enable closed captions, set the display attributes, and so forth.</p>
+
+
diff --git a/docs/html/training/tv/tif/ui.jd b/docs/html/training/tv/tif/ui.jd
new file mode 100644
index 0000000..6ead3db
--- /dev/null
+++ b/docs/html/training/tv/tif/ui.jd
@@ -0,0 +1,304 @@
+page.title=Managing User Interaction
+page.tags=tv, tif
+helpoutsWidget=true
+
+trainingnavtop=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+  <h2>This lesson teaches you to</h2>
+  <ol>
+    <li><a href="#surface">Integrate Player with Surface</a></li>
+    <li><a href="#overlay">Use an Overlay</a></li>
+    <li><a href="#control">Control Content</a></li>
+    <li><a href="#track">Handle Track Selection</a></li>
+  </ol>
+  <h2>Try It Out</h2>
+  <ul>
+    <li><a class="external-link" href="https://github.com/googlesamples/androidtv-sample-inputs">
+      TV Input Service sample app</a></li>
+  </ul>
+</div>
+</div>
+
+<p>In the live TV experience the user changes channels and is presented with
+channel and program information briefly before the information disappears. Other types of information,
+such as messages ("DO NOT ATTEMPT AT HOME"), subtitles, or ads may need to persist. As with any TV
+app, such information should not interfere with the program content playing on the screen.</p>
+
+<img src="{@docRoot}images/tv/do-not-attempt.png" id="figure1">
+<p class="img-caption">
+  <strong>Figure 1.</strong> An overlay message in a live TV app.
+</p>
+
+<p>Also consider whether certain program content should be presented, given the
+content's rating and parental control settings, and how your app behaves and informs the user when
+content is blocked or unavailable. This lesson describes how to develop your TV input's user
+experience for these considerations.</p>
+
+<h2 id="surface">Integrate Player with Surface</h2>
+
+<p>Your TV input must render video onto a {@link android.view.Surface} object, which is passed by
+the {@link android.media.tv.TvInputService.Session#onSetSurface(android.view.Surface) TvInputService.Session.onSetSurface()}
+method. Here's an example of how to use a {@link android.media.MediaPlayer} instance for playing
+content in the {@link android.view.Surface} object:</p>
+
+<pre>
+&#64;Override
+public boolean onSetSurface(Surface surface) {
+    if (mPlayer != null) {
+        mPlayer.setSurface(surface);
+    }
+    mSurface = surface;
+    return true;
+}
+
+&#64;Override
+public void onSetStreamVolume(float volume) {
+    if (mPlayer != null) {
+        mPlayer.setVolume(volume, volume);
+    }
+    mVolume = volume;
+}
+</pre>
+
+<p>Similarly, here's how to do it using <a href="{@docRoot}guide/topics/media/exoplayer.html">
+ExoPlayer</a>:</p>
+
+<pre>
+&#64;Override
+public boolean onSetSurface(Surface surface) {
+    if (mPlayer != null) {
+        mPlayer.sendMessage(mVideoRenderer,
+                MediaCodecVideoTrackRenderer.MSG_SET_SURFACE,
+                surface);
+    }
+    mSurface = surface;
+    return true;
+}
+
+&#64;Override
+public void onSetStreamVolume(float volume) {
+    if (mPlayer != null) {
+        mPlayer.sendMessage(mAudioRenderer,
+                MediaCodecAudioTrackRenderer.MSG_SET_VOLUME,
+                volume);
+    }
+    mVolume = volume;
+}
+</pre>
+
+<h2 id="overlay">Use an Overlay</h2>
+
+<p>Use an overlay to display subtitles, messages, ads or MHEG-5 data broadcasts. By default, the
+overlay is disabled. You can enable it when you create the session by calling
+{@link android.media.tv.TvInputService.Session#setOverlayViewEnabled(boolean) TvInputService.Session.setOverlayViewEnabled(true)},
+as in the following example:</p>
+
+<pre>
+&#64;Override
+public final Session onCreateSession(String inputId) {
+    BaseTvInputSessionImpl session = onCreateSessionInternal(inputId);
+    session.setOverlayViewEnabled(true);
+    mSessions.add(session);
+    return session;
+}
+</pre>
+
+<p>Use a {@link android.view.View} object for the overlay, returned from {@link android.media.tv.TvInputService.Session#onCreateOverlayView() TvInputService.Session.onCreateOverlayView()}, as shown here:</p>
+
+<pre>
+&#64;Override
+public View onCreateOverlayView() {
+    LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
+    View view = inflater.inflate(R.layout.overlayview, null);
+    mSubtitleView = (SubtitleView) view.findViewById(R.id.subtitles);
+
+    // Configure the subtitle view.
+    CaptionStyleCompat captionStyle;
+    float captionTextSize = getCaptionFontSize();
+    captionStyle = CaptionStyleCompat.createFromCaptionStyle(
+            mCaptioningManager.getUserStyle());
+    captionTextSize *= mCaptioningManager.getFontScale();
+    mSubtitleView.setStyle(captionStyle);
+    mSubtitleView.setTextSize(captionTextSize);
+    return view;
+}
+</pre>
+
+<p>The layout definition for the overlay might look something like this:</p>
+
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"&gt;
+
+    &lt;com.google.android.exoplayer.text.SubtitleView
+        android:id="@+id/subtitles"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom|center_horizontal"
+        android:layout_marginLeft="16dp"
+        android:layout_marginRight="16dp"
+        android:layout_marginBottom="32dp"
+        android:visibility="invisible"/&gt;
+&lt;/FrameLayout&gt;
+</pre>
+
+<h2 id="control">Control Content</h2>
+
+<p>When the user selects a channel, your TV input handles the {@link android.media.tv.TvInputService.Session#onTune(android.net.Uri)
+onTune()} callback in the {@link android.media.tv.TvInputService.Session} object. The system TV
+app's parental controls determine what content displays, given the content rating.
+The following sections describe how to manage channel and program selection using the
+{@link android.media.tv.TvInputService.Session} <code>notify</code> methods that
+communicate with the system TV app.</p>
+
+<h3 id="unavailable">Make Video Unavailable</h3>
+
+<p>When the user changes the channel, you want to make sure the screen doesn't display any stray
+video artifacts before your TV input renders the content. When you call {@link android.media.tv.TvInputService.Session#onTune(android.net.Uri) TvInputService.Session.onTune()},
+you can prevent the video from being presented by calling {@link android.media.tv.TvInputService.Session#notifyVideoUnavailable(int) TvInputService.Session.notifyVideoUnavailable()}
+and passing the {@link android.media.tv.TvInputManager#VIDEO_UNAVAILABLE_REASON_TUNING} constant, as
+shown in the following example.</p>
+
+<pre>
+&#64;Override
+public boolean onTune(Uri channelUri) {
+    if (mSubtitleView != null) {
+        mSubtitleView.setVisibility(View.INVISIBLE);
+    }
+    notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING);
+    mUnblockedRatingSet.clear();
+
+    mDbHandler.removeCallbacks(mPlayCurrentProgramRunnable);
+    mPlayCurrentProgramRunnable = new PlayCurrentProgramRunnable(channelUri);
+    mDbHandler.post(mPlayCurrentProgramRunnable);
+    return true;
+}
+</pre>
+
+<p>Then, when the content is rendered to the {@link android.view.Surface}, you call
+{@link android.media.tv.TvInputService.Session#notifyVideoAvailable() TvInputService.Session.notifyVideoAvailable()}
+to allow the video to display, like so:</p>
+
+<pre>
+&#64;Override
+public void onDrawnToSurface(Surface surface) {
+    mFirstFrameDrawn = true;
+    notifyVideoAvailable();
+}
+</pre>
+
+<p>This transition lasts only for fractions of a second, but presenting a blank screen is
+visually better than allowing the picture to flash odd blips and jitters.</p>
+
+<p>See also, <a href="#surface">Integrate Player with Surface</a> for more information about working
+with {@link android.view.Surface} to render video.</p>
+
+<h3 id="parental">Provide Parental Control</h3>
+
+<p>To determine if a given content is blocked by parental controls and content rating, you check the
+{@link android.media.tv.TvInputManager} class methods, {@link android.media.tv.TvInputManager#isParentalControlsEnabled()}
+and {@link android.media.tv.TvInputManager#isRatingBlocked(android.media.tv.TvContentRating)}. You
+might also want to make sure the content's {@link android.media.tv.TvContentRating} is included in a
+set of currently allowed content ratings. These considerations are shown in the following sample.</p>
+
+<pre>
+private void checkContentBlockNeeded() {
+    if (mCurrentContentRating == null || !mTvInputManager.isParentalControlsEnabled()
+            || !mTvInputManager.isRatingBlocked(mCurrentContentRating)
+            || mUnblockedRatingSet.contains(mCurrentContentRating)) {
+        // Content rating is changed so we don't need to block anymore.
+        // Unblock content here explicitly to resume playback.
+        unblockContent(null);
+        return;
+    }
+
+    mLastBlockedRating = mCurrentContentRating;
+    if (mPlayer != null) {
+        // Children restricted content might be blocked by TV app as well,
+        // but TIF should do its best not to show any single frame of blocked content.
+        releasePlayer();
+    }
+
+    notifyContentBlocked(mCurrentContentRating);
+}
+</pre>
+
+<p>Once you have determined if the content should or should not be blocked, notify the system TV
+app by calling the
+{@link android.media.tv.TvInputService.Session} method {@link android.media.tv.TvInputService.Session#notifyContentAllowed() notifyContentAllowed()}
+or
+{@link android.media.tv.TvInputService.Session#notifyContentBlocked(android.media.tv.TvContentRating) notifyContentBlocked()}
+, as shown in the previous example.</p>
+
+<p>Use the {@link android.media.tv.TvContentRating} class to generate the system-defined string for
+the {@link android.media.tv.TvContract.Programs#COLUMN_CONTENT_RATING} with the
+<code><a href="{@docRoot}reference/android/media/tv/TvContentRating.html#createRating(java.lang.String, java.lang.String, java.lang.String, java.lang.String...)">TvContentRating.createRating()</a></code>
+method, as shown here:</p>
+
+<pre>
+TvContentRating rating = TvContentRating.createRating(
+    "com.android.tv",
+    "US_TV",
+    "US_TV_PG",
+    "US_TV_D", "US_TV_L");
+</pre>
+
+<h2 id="track">Handle Track Selection</h2>
+
+<p>The {@link android.media.tv.TvTrackInfo} class holds information about media tracks such
+as the track type (video, audio, or subtitle) and so forth. </p>
+
+<p>The first time your TV input session is able to get track information, it should call
+<code><a href="{@docRoot}reference/android/media/tv/TvInputService.Session.html#notifyTracksChanged(java.util.List<android.media.tv.TvTrackInfo>)">TvInputService.Session.notifyTracksChanged()</a></code> with a list of all tracks to update the system TV app.  When there
+is a change in track information, call
+<code><a href="{@docRoot}reference/android/media/tv/TvInputService.Session.html#notifyTracksChanged(java.util.List<android.media.tv.TvTrackInfo>)">notifyTracksChanged()</a></code>
+again to update the system.
+
+</p>
+
+<p>The system TV app provides an interface for the user to select a specific track if more than one
+track is available for a given track type; for example, subtitles in different languages. Your TV
+input responds to the
+{@link android.media.tv.TvInputService.Session#onSelectTrack(int, java.lang.String) onSelectTrack()}
+call from the system TV app by calling
+{@link android.media.tv.TvInputService.Session#notifyTrackSelected(int, java.lang.String) notifyTrackSelected()}
+, as shown in the following example. Note that when <code>null</code>
+is passed as the track ID, this <em>deselects</em> the track.</p>
+
+<pre>
+&#64;Override
+public boolean onSelectTrack(int type, String trackId) {
+    if (mPlayer != null) {
+        if (type == TvTrackInfo.TYPE_SUBTITLE) {
+            if (!mCaptionEnabled && trackId != null) {
+                return false;
+            }
+            mSelectedSubtitleTrackId = trackId;
+            if (trackId == null) {
+                mSubtitleView.setVisibility(View.INVISIBLE);
+            }
+        }
+        if (mPlayer.selectTrack(type, trackId)) {
+            notifyTrackSelected(type, trackId);
+            return true;
+        }
+    }
+    return false;
+}
+</pre>
+
+
+
+
+
+
+
diff --git a/docs/html/training/wearables/apps/voice.jd b/docs/html/training/wearables/apps/voice.jd
index 6d49319..3aef3c4 100644
--- a/docs/html/training/wearables/apps/voice.jd
+++ b/docs/html/training/wearables/apps/voice.jd
@@ -131,6 +131,17 @@
   </tr>
 
   <tr>
+    <td>Start stopwatch</td>
+    <td>"Ok Google, start stopwatch"</td>
+    <td>
+      <dl>
+        <dt>Action</dt>
+        <dd><code>com.google.android.wearable.action.STOPWATCH</code></dd>
+      </dl>
+   </td>
+  </tr>
+
+  <tr>
     <td>Start/Stop a bike ride</td>
     <td>"OK Google, start cycling"<br/><br/>"OK Google, start my bike ride"<br/><br/>"OK Google, stop cycling"</td>
     <td>
diff --git a/docs/html/training/wearables/watch-faces/drawing.jd b/docs/html/training/wearables/watch-faces/drawing.jd
index 3c5da34..60da5d5 100644
--- a/docs/html/training/wearables/watch-faces/drawing.jd
+++ b/docs/html/training/wearables/watch-faces/drawing.jd
@@ -377,7 +377,8 @@
 
 <ul>
 <li>For devices that use low-bit ambient mode, the screen supports fewer bits for each color
-in ambient mode, so you should disable anti-aliasing.</li>
+in ambient mode, so you should disable anti-aliasing and bitmap filtering when the device switches
+to ambient mode.</li>
 <li>For devices that require burn-in protection, avoid using large blocks of white pixels in
 ambient mode and do not place content within 10 pixels of the edge of the screen, since the
 system shifts the content periodically to avoid pixel burn-in.</li>
@@ -385,7 +386,9 @@
 
 <p>For more information about low-bit ambient mode and burn-in protection, see
 <a href="{@docRoot}design/wear/watchfaces.html#SpecialScreens">Optimize for Special
-Screens</a>.</p>
+Screens</a>. For more information on how to disable bitmap filtering, see
+<a href="{@docRoot}training/wearables/watch-faces/performance.html#BitmapFiltering">Bitmap
+Filtering</a>.</p>
 
 
 <h2 id="Modes">Respond to Changes Between Modes</h2>
diff --git a/docs/html/training/wearables/watch-faces/performance.jd b/docs/html/training/wearables/watch-faces/performance.jd
index 68438fe..118bc6a0 100644
--- a/docs/html/training/wearables/watch-faces/performance.jd
+++ b/docs/html/training/wearables/watch-faces/performance.jd
@@ -99,7 +99,9 @@
 setFilterBitmap()} method. <a href="#fig2">Figure 2</a> shows a magnified view of a clock hand with
 and without bitmap filtering.</p>
 
-
+<p class="note"><strong>Note:</strong> In low-bit ambient mode, the system does not reliably
+render the colors in the image for bitmap filtering to process successfully. When ambient mode is
+active, disable bitmap filtering.</p>
 
 <h2 id="OutDrawing">Move Expensive Operations Outside the Drawing Method</h2>
 
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 150f195..48afcbf 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -45,6 +45,8 @@
  * Canvas and Drawables</a> developer guide.</p></div>
  */
 public class Canvas {
+    /** @hide */
+    public static boolean sCompatibilityRestore = false;
 
     /**
      * Should only be assigned in constructors (or setBitmap if software canvas),
@@ -557,7 +559,8 @@
      * an error to call restore() more times than save() was called.
      */
     public void restore() {
-        native_restore(mNativeCanvasWrapper);
+        boolean throwOnUnderflow = !sCompatibilityRestore || !isHardwareAccelerated();
+        native_restore(mNativeCanvasWrapper, throwOnUnderflow);
     }
 
     /**
@@ -582,7 +585,8 @@
      * @param saveCount The save level to restore to.
      */
     public void restoreToCount(int saveCount) {
-        native_restoreToCount(mNativeCanvasWrapper, saveCount);
+        boolean throwOnUnderflow = !sCompatibilityRestore || !isHardwareAccelerated();
+        native_restoreToCount(mNativeCanvasWrapper, saveCount, throwOnUnderflow);
     }
 
     /**
@@ -1988,9 +1992,10 @@
     private static native int native_saveLayerAlpha(long nativeCanvas, float l,
                                                     float t, float r, float b,
                                                     int alpha, int layerFlags);
-    private static native void native_restore(long canvasHandle);
+    private static native void native_restore(long canvasHandle, boolean tolerateUnderflow);
     private static native void native_restoreToCount(long canvasHandle,
-                                                     int saveCount);
+                                                     int saveCount,
+                                                     boolean tolerateUnderflow);
     private static native int native_getSaveCount(long canvasHandle);
 
     private static native void native_translate(long canvasHandle,
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 49c4247..c63c8ba 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -356,6 +356,108 @@
     public static final int RAW10 = 0x25;
 
     /**
+     * <p>
+     * Android 12-bit raw format
+     * </p>
+     * <p>
+     * This is a single-plane, 12-bit per pixel, densely packed (in each row),
+     * unprocessed format, usually representing raw Bayer-pattern images coming
+     * from an image sensor.
+     * </p>
+     * <p>
+     * In an image buffer with this format, starting from the first pixel of each
+     * row, each two consecutive pixels are packed into 3 bytes (24 bits). The first
+     * and second byte contains the top 8 bits of first and second pixel. The third
+     * byte contains the 4 least significant bits of the two pixels, the exact layout
+     * data for each two consecutive pixels is illustrated below (Pi[j] stands for
+     * the jth bit of the ith pixel):
+     * </p>
+     * <table>
+     * <thead>
+     * <tr>
+     * <th align="center"></th>
+     * <th align="center">bit 7</th>
+     * <th align="center">bit 6</th>
+     * <th align="center">bit 5</th>
+     * <th align="center">bit 4</th>
+     * <th align="center">bit 3</th>
+     * <th align="center">bit 2</th>
+     * <th align="center">bit 1</th>
+     * <th align="center">bit 0</th>
+     * </tr>
+     * </thead> <tbody>
+     * <tr>
+     * <td align="center">Byte 0:</td>
+     * <td align="center">P0[11]</td>
+     * <td align="center">P0[10]</td>
+     * <td align="center">P0[ 9]</td>
+     * <td align="center">P0[ 8]</td>
+     * <td align="center">P0[ 7]</td>
+     * <td align="center">P0[ 6]</td>
+     * <td align="center">P0[ 5]</td>
+     * <td align="center">P0[ 4]</td>
+     * </tr>
+     * <tr>
+     * <td align="center">Byte 1:</td>
+     * <td align="center">P1[11]</td>
+     * <td align="center">P1[10]</td>
+     * <td align="center">P1[ 9]</td>
+     * <td align="center">P1[ 8]</td>
+     * <td align="center">P1[ 7]</td>
+     * <td align="center">P1[ 6]</td>
+     * <td align="center">P1[ 5]</td>
+     * <td align="center">P1[ 4]</td>
+     * </tr>
+     * <tr>
+     * <td align="center">Byte 2:</td>
+     * <td align="center">P1[ 3]</td>
+     * <td align="center">P1[ 2]</td>
+     * <td align="center">P1[ 1]</td>
+     * <td align="center">P1[ 0]</td>
+     * <td align="center">P0[ 3]</td>
+     * <td align="center">P0[ 2]</td>
+     * <td align="center">P0[ 1]</td>
+     * <td align="center">P0[ 0]</td>
+     * </tr>
+     * </tbody>
+     * </table>
+     * <p>
+     * This format assumes
+     * <ul>
+     * <li>a width multiple of 4 pixels</li>
+     * <li>an even height</li>
+     * </ul>
+     * </p>
+     *
+     * <pre>size = row stride * height</pre> where the row stride is in <em>bytes</em>,
+     * not pixels.
+     *
+     * <p>
+     * Since this is a densely packed format, the pixel stride is always 0. The
+     * application must use the pixel data layout defined in above table to
+     * access each row data. When row stride is equal to {@code width * (12 / 8)}, there
+     * will be no padding bytes at the end of each row, the entire image data is
+     * densely packed. When stride is larger than {@code width * (12 / 8)}, padding
+     * bytes will be present at the end of each row.
+     * </p>
+     * <p>
+     * For example, the {@link android.media.Image} object can provide data in
+     * this format from a {@link android.hardware.camera2.CameraDevice} (if
+     * supported) through a {@link android.media.ImageReader} object. The
+     * {@link android.media.Image#getPlanes() Image#getPlanes()} will return a
+     * single plane containing the pixel data. The pixel stride is always 0 in
+     * {@link android.media.Image.Plane#getPixelStride()}, and the
+     * {@link android.media.Image.Plane#getRowStride()} describes the vertical
+     * neighboring pixel distance (in bytes) between adjacent rows.
+     * </p>
+     *
+     * @see android.media.Image
+     * @see android.media.ImageReader
+     * @see android.hardware.camera2.CameraDevice
+     */
+    public static final int RAW12 = 0x26;
+
+    /**
      * Android dense depth image format.
      *
      * Each pixel is 16 bits, representing a depth ranging measurement from
@@ -388,6 +490,33 @@
     public static final int DEPTH_POINT_CLOUD = 0x101;
 
     /**
+     * Android private opaque image format.
+     * <p>
+     * The choices of the actual format and pixel data layout are entirely up to
+     * the device-specific and framework internal implementations, and may vary
+     * depending on use cases even for the same device. The buffers of this
+     * format can be produced by components like
+     * {@link android.media.ImageWriter ImageWriter} , and interpreted correctly
+     * by consumers like {@link android.hardware.camera2.CameraDevice
+     * CameraDevice} based on the device/framework private information. However,
+     * these buffers are not directly accessible to the application.
+     * </p>
+     * <p>
+     * When an {@link android.media.Image Image} of this format is obtained from
+     * an {@link android.media.ImageReader ImageReader} or
+     * {@link android.media.ImageWriter ImageWriter}, the
+     * {@link android.media.Image#getPlanes() getPlanes()} method will return an
+     * empty {@link android.media.Image.Plane Plane} array.
+     * </p>
+     * <p>
+     * If a buffer of this format is to be used as an OpenGL ES texture, the
+     * framework will assume that sampling the texture will always return an
+     * alpha value of 1.0 (i.e. the buffer contains only opaque pixel values).
+     * </p>
+     */
+    public static final int PRIVATE = 0x22;
+
+    /**
      * Use this function to retrieve the number of bits per pixel of an
      * ImageFormat.
      *
@@ -418,6 +547,8 @@
                 return 16;
             case RAW10:
                 return 10;
+            case RAW12:
+                return 12;
         }
         return -1;
     }
@@ -445,8 +576,10 @@
             case YUV_420_888:
             case RAW_SENSOR:
             case RAW10:
+            case RAW12:
             case DEPTH16:
             case DEPTH_POINT_CLOUD:
+            case PRIVATE:
                 return true;
         }
 
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 1da198c..cd5f59d 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -2120,9 +2120,9 @@
         int contextLen = contextEnd - contextStart;
         char[] buf = TemporaryBuffer.obtain(contextLen);
         TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
-        int result = getTextRunCursor(buf, 0, contextLen, dir, offset - contextStart, cursorOpt);
+        int relPos = getTextRunCursor(buf, 0, contextLen, dir, offset - contextStart, cursorOpt);
         TemporaryBuffer.recycle(buf);
-        return result;
+        return (relPos == -1) ? -1 : relPos + contextStart;
     }
 
     /**
@@ -2249,6 +2249,26 @@
             bounds);
     }
 
+    /**
+     * Determine whether the typeface set on the paint has a glyph supporting the string. The
+     * simplest case is when the string contains a single character, in which this method
+     * determines whether the font has the character. In the case of multiple characters, the
+     * method returns true if there is a single glyph representing the ligature. For example, if
+     * the input is a pair of regional indicator symbols, determine whether there is an emoji flag
+     * for the pair.
+     *
+     * Finally, if the string contains a variation selector, the method only returns true if
+     * the fonts contains a glyph specific to that variation.
+     *
+     * Checking is done on the entire fallback chain, not just the immediate font referenced.
+     *
+     * @param string the string to test whether there is glyph support
+     * @return true if the typeface has a glyph for the string
+     */
+    public boolean hasGlyph(String string) {
+        return native_hasGlyph(mNativePaint, mNativeTypeface, mBidiFlags, string);
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -2334,4 +2354,6 @@
                                                              String settings);
     private static native int native_getHyphenEdit(long native_object);
     private static native void native_setHyphenEdit(long native_object, int hyphen);
+    private static native boolean native_hasGlyph(long native_object, long native_typeface,
+            int bidiFlags, String string);
 }
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 17e5b0b..28c26ff 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -16,6 +16,8 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorInflater;
+import android.animation.AnimatorSet;
+import android.animation.Animator.AnimatorListener;
 import android.annotation.NonNull;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -37,6 +39,7 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * This class uses {@link android.animation.ObjectAnimator} and
@@ -47,8 +50,8 @@
  * </p>
  * <p>
  * First is the XML file for {@link android.graphics.drawable.VectorDrawable}.
- * Note that we allow the animation happen on the group's attributes and path's
- * attributes, which requires they are uniquely named in this xml file. Groups
+ * Note that we allow the animation to happen on the group's attributes and path's
+ * attributes, which requires they are uniquely named in this XML file. Groups
  * and paths without animations do not need names.
  * </p>
  * <li>Here is a simple VectorDrawable in this vectordrawable.xml file.
@@ -71,7 +74,7 @@
  * &lt;/vector&gt;
  * </pre></li>
  * <p>
- * Second is the AnimatedVectorDrawable's xml file, which defines the target
+ * Second is the AnimatedVectorDrawable's XML file, which defines the target
  * VectorDrawable, the target paths and groups to animate, the properties of the
  * path and group to animate and the animations defined as the ObjectAnimators
  * or AnimatorSets.
@@ -90,7 +93,7 @@
  * &lt;/animated-vector&gt;
  * </pre></li>
  * <p>
- * Last is the Animator xml file, which is the same as a normal ObjectAnimator
+ * Last is the Animator XML file, which is the same as a normal ObjectAnimator
  * or AnimatorSet.
  * To complete this example, here are the 2 animator files used in avd.xml:
  * rotation.xml and path_morph.xml.
@@ -107,7 +110,7 @@
  * the other. Note that the paths must be compatible for morphing.
  * In more details, the paths should have exact same length of commands , and
  * exact same length of parameters for each commands.
- * Note that the path string are better stored in strings.xml for reusing.
+ * Note that the path strings are better stored in strings.xml for reusing.
  * <pre>
  * &lt;set xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;
  *     &lt;objectAnimator
@@ -170,7 +173,7 @@
 
     @Override
     public int getChangingConfigurations() {
-        return super.getChangingConfigurations() | mAnimatedVectorState.mChangingConfigurations;
+        return super.getChangingConfigurations() | mAnimatedVectorState.getChangingConfigurations();
     }
 
     @Override
@@ -312,6 +315,15 @@
 
             eventType = parser.next();
         }
+        setupAnimatorSet();
+    }
+
+    private void setupAnimatorSet() {
+        if (mAnimatedVectorState.mTempAnimators != null) {
+            mAnimatedVectorState.mAnimatorSet.playTogether(mAnimatedVectorState.mTempAnimators);
+            mAnimatedVectorState.mTempAnimators.clear();
+            mAnimatedVectorState.mTempAnimators = null;
+        }
     }
 
     @Override
@@ -330,10 +342,44 @@
         }
     }
 
+    /**
+     * Adds a listener to the set of listeners that are sent events through the life of an
+     * animation.
+     *
+     * @param listener the listener to be added to the current set of listeners for this animation.
+     */
+    public void addListener(AnimatorListener listener) {
+        mAnimatedVectorState.mAnimatorSet.addListener(listener);
+    }
+
+    /**
+     * Removes a listener from the set listening to this animation.
+     *
+     * @param listener the listener to be removed from the current set of listeners for this
+     *                 animation.
+     */
+    public void removeListener(AnimatorListener listener) {
+        mAnimatedVectorState.mAnimatorSet.removeListener(listener);
+    }
+
+    /**
+     * Gets the set of {@link android.animation.Animator.AnimatorListener} objects that are currently
+     * listening for events on this <code>AnimatedVectorDrawable</code> object.
+     *
+     * @return List<AnimatorListener> The set of listeners.
+     */
+    public List<AnimatorListener> getListeners() {
+        return mAnimatedVectorState.mAnimatorSet.getListeners();
+    }
+
     private static class AnimatedVectorDrawableState extends ConstantState {
         int mChangingConfigurations;
         VectorDrawable mVectorDrawable;
-        ArrayList<Animator> mAnimators;
+        // Always have a valid animatorSet to handle all the listeners call.
+        AnimatorSet mAnimatorSet = new AnimatorSet();
+        // When parsing the XML, we build individual animator and store in this array. At the end,
+        // we add this array into the mAnimatorSet.
+        private ArrayList<Animator> mTempAnimators;
         ArrayMap<Animator, String> mTargetNameMap;
 
         public AnimatedVectorDrawableState(AnimatedVectorDrawableState copy,
@@ -353,18 +399,23 @@
                     mVectorDrawable.setBounds(copy.mVectorDrawable.getBounds());
                     mVectorDrawable.setAllowCaching(false);
                 }
-                if (copy.mAnimators != null) {
-                    final int numAnimators = copy.mAnimators.size();
-                    mAnimators = new ArrayList<Animator>(numAnimators);
+                if (copy.mAnimatorSet != null) {
+                    final int numAnimators = copy.mTargetNameMap.size();
+                    // Deep copy a animator set, and then setup the target map again.
+                    mAnimatorSet = copy.mAnimatorSet.clone();
                     mTargetNameMap = new ArrayMap<Animator, String>(numAnimators);
+                    // Since the new AnimatorSet is cloned from the old one, the order must be the
+                    // same inside the array.
+                    ArrayList<Animator> oldAnim = copy.mAnimatorSet.getChildAnimations();
+                    ArrayList<Animator> newAnim = mAnimatorSet.getChildAnimations();
+
                     for (int i = 0; i < numAnimators; ++i) {
-                        Animator anim = copy.mAnimators.get(i);
-                        Animator animClone = anim.clone();
-                        String targetName = copy.mTargetNameMap.get(anim);
-                        Object targetObject = mVectorDrawable.getTargetByName(targetName);
-                        animClone.setTarget(targetObject);
-                        mAnimators.add(animClone);
-                        mTargetNameMap.put(animClone, targetName);
+                        // Target name must be the same for new and old
+                        String targetName = copy.mTargetNameMap.get(oldAnim.get(i));
+
+                        Object newTargetObject = mVectorDrawable.getTargetByName(targetName);
+                        newAnim.get(i).setTarget(newTargetObject);
+                        mTargetNameMap.put(newAnim.get(i), targetName);
                     }
                 }
             } else {
@@ -397,11 +448,11 @@
     private void setupAnimatorsForTarget(String name, Animator animator) {
         Object target = mAnimatedVectorState.mVectorDrawable.getTargetByName(name);
         animator.setTarget(target);
-        if (mAnimatedVectorState.mAnimators == null) {
-            mAnimatedVectorState.mAnimators = new ArrayList<Animator>();
+        if (mAnimatedVectorState.mTempAnimators == null) {
+            mAnimatedVectorState.mTempAnimators = new ArrayList<Animator>();
             mAnimatedVectorState.mTargetNameMap = new ArrayMap<Animator, String>();
         }
-        mAnimatedVectorState.mAnimators.add(animator);
+        mAnimatedVectorState.mTempAnimators.add(animator);
         mAnimatedVectorState.mTargetNameMap.put(animator, name);
         if (DBG_ANIMATION_VECTOR_DRAWABLE) {
             Log.v(LOGTAG, "add animator  for target " + name + " " + animator);
@@ -410,27 +461,11 @@
 
     @Override
     public boolean isRunning() {
-        final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
-        final int size = animators.size();
-        for (int i = 0; i < size; i++) {
-            final Animator animator = animators.get(i);
-            if (animator.isRunning()) {
-                return true;
-            }
-        }
-        return false;
+        return mAnimatedVectorState.mAnimatorSet.isRunning();
     }
 
     private boolean isStarted() {
-        final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
-        final int size = animators.size();
-        for (int i = 0; i < size; i++) {
-            final Animator animator = animators.get(i);
-            if (animator.isStarted()) {
-                return true;
-            }
-        }
-        return false;
+        return mAnimatedVectorState.mAnimatorSet.isStarted();
     }
 
     @Override
@@ -439,24 +474,13 @@
         if (isStarted()) {
             return;
         }
-        // Otherwise, kick off every animator.
-        final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
-        final int size = animators.size();
-        for (int i = 0; i < size; i++) {
-            final Animator animator = animators.get(i);
-            animator.start();
-        }
+        mAnimatedVectorState.mAnimatorSet.start();
         invalidateSelf();
     }
 
     @Override
     public void stop() {
-        final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
-        final int size = animators.size();
-        for (int i = 0; i < size; i++) {
-            final Animator animator = animators.get(i);
-            animator.end();
-        }
+        mAnimatedVectorState.mAnimatorSet.end();
     }
 
     /**
@@ -473,27 +497,14 @@
             Log.w(LOGTAG, "AnimatedVectorDrawable can't reverse()");
             return;
         }
-        final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
-        final int size = animators.size();
-        for (int i = 0; i < size; i++) {
-            final Animator animator = animators.get(i);
-            animator.reverse();
-        }
+        mAnimatedVectorState.mAnimatorSet.reverse();
     }
 
     /**
      * @hide
      */
     public boolean canReverse() {
-        final ArrayList<Animator> animators = mAnimatedVectorState.mAnimators;
-        final int size = animators.size();
-        for (int i = 0; i < size; i++) {
-            final Animator animator = animators.get(i);
-            if (!animator.canReverse()) {
-                return false;
-            }
-        }
-        return true;
+        return mAnimatedVectorState.mAnimatorSet.canReverse();
     }
 
     private final Callback mCallback = new Callback() {
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index 68ffed6..6fe6b56 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -454,7 +454,7 @@
 
     @Override
     public int getChangingConfigurations() {
-        return super.getChangingConfigurations() | mBitmapState.mChangingConfigurations;
+        return super.getChangingConfigurations() | mBitmapState.getChangingConfigurations();
     }
 
     private boolean needMirroring() {
@@ -721,7 +721,7 @@
 
         final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.BitmapDrawable);
         updateStateFromTypedArray(a);
-        verifyState(a);
+        verifyRequiredAttributes(a);
         a.recycle();
 
         // Update local properties.
@@ -733,11 +733,13 @@
      *
      * @throws XmlPullParserException if any required attributes are missing
      */
-    private void verifyState(TypedArray a) throws XmlPullParserException {
+    private void verifyRequiredAttributes(TypedArray a) throws XmlPullParserException {
+        // If we're not waiting on a theme, verify required attributes.
         final BitmapState state = mBitmapState;
-        if (state.mBitmap == null) {
+        if (state.mBitmap == null && (state.mThemeAttrs == null
+                || state.mThemeAttrs[R.styleable.BitmapDrawable_src] == 0)) {
             throw new XmlPullParserException(a.getPositionDescription() +
-                    ": <bitmap> requires a valid src attribute");
+                    ": <bitmap> requires a valid 'src' attribute");
         }
     }
 
@@ -759,7 +761,7 @@
             final Bitmap bitmap = BitmapFactory.decodeResource(r, srcResId);
             if (bitmap == null) {
                 throw new XmlPullParserException(a.getPositionDescription() +
-                        ": <bitmap> requires a valid src attribute");
+                        ": <bitmap> requires a valid 'src' attribute");
             }
 
             state.mBitmap = bitmap;
@@ -832,7 +834,7 @@
 
         // Apply theme to contained color state list.
         if (state.mTint != null && state.mTint.canApplyTheme()) {
-            state.mTint.applyTheme(t);
+            state.mTint = state.mTint.obtainForTheme(t);
         }
 
         // Update local properties.
@@ -880,7 +882,7 @@
 
     @Override
     public final ConstantState getConstantState() {
-        mBitmapState.mChangingConfigurations = getChangingConfigurations();
+        mBitmapState.mChangingConfigurations |= getChangingConfigurations();
         return mBitmapState;
     }
 
@@ -948,7 +950,8 @@
 
         @Override
         public int getChangingConfigurations() {
-            return mChangingConfigurations;
+            return mChangingConfigurations
+                    | (mTint != null ? mTint.getChangingConfigurations() : 0);
         }
     }
 
diff --git a/graphics/java/android/graphics/drawable/ColorDrawable.java b/graphics/java/android/graphics/drawable/ColorDrawable.java
index f75ab36..8e91621 100644
--- a/graphics/java/android/graphics/drawable/ColorDrawable.java
+++ b/graphics/java/android/graphics/drawable/ColorDrawable.java
@@ -276,7 +276,7 @@
         }
 
         if (state.mTint != null && state.mTint.canApplyTheme()) {
-            state.mTint.applyTheme(t);
+            state.mTint = state.mTint.obtainForTheme(t);
         }
 
         updateLocalState(t.getResources());
@@ -327,7 +327,8 @@
 
         @Override
         public int getChangingConfigurations() {
-            return mChangingConfigurations;
+            return mChangingConfigurations
+                    | (mTint != null ? mTint.getChangingConfigurations() : 0);
         }
     }
 
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 56876e94..e8b8c77 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -816,9 +816,12 @@
      * returned.  You can use the method {@link #resolveOpacity} to perform a
      * standard reduction of two opacities to the appropriate single output.
      *
-     * <p>Note that the returned value does <em>not</em> take into account a
+     * <p>Note that the returned value does not necessarily take into account a
      * custom alpha or color filter that has been applied by the client through
-     * the {@link #setAlpha} or {@link #setColorFilter} methods.
+     * the {@link #setAlpha} or {@link #setColorFilter} methods. Some subclasses,
+     * such as {@link BitmapDrawable}, {@link ColorDrawable}, and {@link GradientDrawable},
+     * do account for the value of {@link #setAlpha}, but the general behavior is dependent
+     * upon the implementation of the subclass.
      *
      * @return int The opacity class of the Drawable.
      *
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index ddcb48b..b03fe3a 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -87,8 +87,7 @@
     @Override
     public int getChangingConfigurations() {
         return super.getChangingConfigurations()
-                | mDrawableContainerState.mChangingConfigurations
-                | mDrawableContainerState.mChildrenChangingConfigurations;
+                | mDrawableContainerState.getChangingConfigurations();
     }
 
     private boolean needsMirroring() {
@@ -865,6 +864,9 @@
                 for (int i = 0; i < N; i++) {
                     if (drawables[i] != null && drawables[i].canApplyTheme()) {
                         drawables[i].applyTheme(theme);
+
+                        // Update cached mask of child changing configurations.
+                        mChildrenChangingConfigurations |= drawables[i].getChangingConfigurations();
                     }
                 }
             }
diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java
index 1d6c60f..0da4275 100644
--- a/graphics/java/android/graphics/drawable/DrawableWrapper.java
+++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java
@@ -180,8 +180,7 @@
     @Override
     public int getChangingConfigurations() {
         return super.getChangingConfigurations()
-                | (mState != null ? mState.mChangingConfigurations : 0)
-                | mDrawable.getChangingConfigurations();
+                | (mState != null ? mState.getChangingConfigurations() : 0);
     }
 
     @Override
@@ -433,7 +432,7 @@
 
         @Override
         public int getChangingConfigurations() {
-            return mChangingConfigurations;
+            return mChangingConfigurations | mDrawableState.getChangingConfigurations();
         }
 
         public boolean canConstantState() {
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index eff152c..4c2817c 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -491,19 +491,21 @@
     }
 
     /**
-     * <p>Sets the colors used to draw the gradient. Each color is specified as an
-     * ARGB integer and the array must contain at least 2 colors.</p>
-     * <p><strong>Note</strong>: changing colors will affect all instances
-     * of a drawable loaded from a resource. It is recommended to invoke
-     * {@link #mutate()} before changing the colors.</p>
+     * Sets the colors used to draw the gradient.
+     * <p>
+     * Each color is specified as an ARGB integer and the array must contain at
+     * least 2 colors.
+     * <p>
+     * <strong>Note</strong>: changing colors will affect all instances of a
+     * drawable loaded from a resource. It is recommended to invoke
+     * {@link #mutate()} before changing the colors.
      *
-     * @param colors 2 or more ARGB colors
-     *
+     * @param colors an array containing 2 or more ARGB colors
      * @see #mutate()
      * @see #setColor(int)
      */
     public void setColors(@ColorInt int[] colors) {
-        mGradientState.setColors(colors);
+        mGradientState.setGradientColors(colors);
         mGradientIsDirty = true;
         invalidateSelf();
     }
@@ -568,7 +570,7 @@
             mFillPaint.setAlpha(currFillAlpha);
             mFillPaint.setDither(st.mDither);
             mFillPaint.setColorFilter(colorFilter);
-            if (colorFilter != null && st.mColorStateList == null) {
+            if (colorFilter != null && st.mSolidColors == null) {
                 mFillPaint.setColor(mAlpha << 24);
             }
             if (haveStroke) {
@@ -715,7 +717,7 @@
      * @see #setColors(int[])
      */
     public void setColor(@ColorInt int argb) {
-        mGradientState.setColorStateList(ColorStateList.valueOf(argb));
+        mGradientState.setSolidColors(ColorStateList.valueOf(argb));
         mFillPaint.setColor(argb);
         invalidateSelf();
     }
@@ -734,7 +736,7 @@
      * @see #mutate()
      */
     public void setColor(ColorStateList colorStateList) {
-        mGradientState.setColorStateList(colorStateList);
+        mGradientState.setSolidColors(colorStateList);
         final int color;
         if (colorStateList == null) {
             color = Color.TRANSPARENT;
@@ -751,9 +753,9 @@
         boolean invalidateSelf = false;
 
         final GradientState s = mGradientState;
-        final ColorStateList stateList = s.mColorStateList;
-        if (stateList != null) {
-            final int newColor = stateList.getColorForState(stateSet, 0);
+        final ColorStateList solidColors = s.mSolidColors;
+        if (solidColors != null) {
+            final int newColor = solidColors.getColorForState(stateSet, 0);
             final int oldColor = mFillPaint.getColor();
             if (oldColor != newColor) {
                 mFillPaint.setColor(newColor);
@@ -763,12 +765,12 @@
 
         final Paint strokePaint = mStrokePaint;
         if (strokePaint != null) {
-            final ColorStateList strokeStateList = s.mStrokeColorStateList;
-            if (strokeStateList != null) {
-                final int newStrokeColor = strokeStateList.getColorForState(stateSet, 0);
-                final int oldStrokeColor = strokePaint.getColor();
-                if (oldStrokeColor != newStrokeColor) {
-                    strokePaint.setColor(newStrokeColor);
+            final ColorStateList strokeColors = s.mStrokeColors;
+            if (strokeColors != null) {
+                final int newColor = strokeColors.getColorForState(stateSet, 0);
+                final int oldColor = strokePaint.getColor();
+                if (oldColor != newColor) {
+                    strokePaint.setColor(newColor);
                     invalidateSelf = true;
                 }
             }
@@ -791,14 +793,14 @@
     public boolean isStateful() {
         final GradientState s = mGradientState;
         return super.isStateful()
-                || (s.mColorStateList != null && s.mColorStateList.isStateful())
-                || (s.mStrokeColorStateList != null && s.mStrokeColorStateList.isStateful())
+                || (s.mSolidColors != null && s.mSolidColors.isStateful())
+                || (s.mStrokeColors != null && s.mStrokeColors.isStateful())
                 || (s.mTint != null && s.mTint.isStateful());
     }
 
     @Override
     public int getChangingConfigurations() {
-        return super.getChangingConfigurations() | mGradientState.mChangingConfigurations;
+        return super.getChangingConfigurations() | mGradientState.getChangingConfigurations();
     }
 
     @Override
@@ -899,10 +901,10 @@
             mRect.set(bounds.left + inset, bounds.top + inset,
                       bounds.right - inset, bounds.bottom - inset);
 
-            final int[] colors = st.mColors;
-            if (colors != null) {
-                RectF r = mRect;
-                float x0, x1, y0, y1;
+            final int[] gradientColors = st.mGradientColors;
+            if (gradientColors != null) {
+                final RectF r = mRect;
+                final float x0, x1, y0, y1;
 
                 if (st.mGradient == LINEAR_GRADIENT) {
                     final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f;
@@ -942,7 +944,7 @@
                     }
 
                     mFillPaint.setShader(new LinearGradient(x0, y0, x1, y1,
-                            colors, st.mPositions, Shader.TileMode.CLAMP));
+                            gradientColors, st.mPositions, Shader.TileMode.CLAMP));
                 } else if (st.mGradient == RADIAL_GRADIENT) {
                     x0 = r.left + (r.right - r.left) * st.mCenterX;
                     y0 = r.top + (r.bottom - r.top) * st.mCenterY;
@@ -971,22 +973,22 @@
                     }
 
                     mFillPaint.setShader(new RadialGradient(
-                            x0, y0, radius, colors, null, Shader.TileMode.CLAMP));
+                            x0, y0, radius, gradientColors, null, Shader.TileMode.CLAMP));
                 } else if (st.mGradient == SWEEP_GRADIENT) {
                     x0 = r.left + (r.right - r.left) * st.mCenterX;
                     y0 = r.top + (r.bottom - r.top) * st.mCenterY;
 
-                    int[] tempColors = colors;
+                    int[] tempColors = gradientColors;
                     float[] tempPositions = null;
 
                     if (st.mUseLevel) {
                         tempColors = st.mTempColors;
-                        final int length = colors.length;
+                        final int length = gradientColors.length;
                         if (tempColors == null || tempColors.length != length + 1) {
                             tempColors = st.mTempColors = new int[length + 1];
                         }
-                        System.arraycopy(colors, 0, tempColors, 0, length);
-                        tempColors[length] = colors[length - 1];
+                        System.arraycopy(gradientColors, 0, tempColors, 0, length);
+                        tempColors[length] = gradientColors[length - 1];
 
                         tempPositions = st.mTempPositions;
                         final float fraction = 1.0f / (length - 1);
@@ -1006,7 +1008,7 @@
 
                 // If we don't have a solid color, the alpha channel must be
                 // maxed out so that alpha modulation works correctly.
-                if (st.mColorStateList == null) {
+                if (st.mSolidColors == null) {
                     mFillPaint.setColor(Color.BLACK);
                 }
             }
@@ -1044,15 +1046,15 @@
         }
 
         if (state.mTint != null && state.mTint.canApplyTheme()) {
-            state.mTint.applyTheme(t);
+            state.mTint = state.mTint.obtainForTheme(t);
         }
 
-        if (state.mColorStateList != null && state.mColorStateList.canApplyTheme()) {
-            state.mColorStateList.applyTheme(t);
+        if (state.mSolidColors != null && state.mSolidColors.canApplyTheme()) {
+            state.mSolidColors = state.mSolidColors.obtainForTheme(t);
         }
 
-        if (state.mStrokeColorStateList != null && state.mStrokeColorStateList.canApplyTheme()) {
-            state.mStrokeColorStateList.applyTheme(t);
+        if (state.mStrokeColors != null && state.mStrokeColors.canApplyTheme()) {
+            state.mStrokeColors = state.mStrokeColors.obtainForTheme(t);
         }
 
         applyThemeChildElements(t);
@@ -1288,7 +1290,7 @@
         ColorStateList colorStateList = a.getColorStateList(
                 R.styleable.GradientDrawableStroke_color);
         if (colorStateList == null) {
-            colorStateList = st.mStrokeColorStateList;
+            colorStateList = st.mStrokeColors;
         }
 
         if (dashWidth != 0.0f) {
@@ -1346,10 +1348,10 @@
                 R.styleable.GradientDrawableGradient_endColor, 0);
 
         if (hasCenterColor) {
-            st.mColors = new int[3];
-            st.mColors[0] = startColor;
-            st.mColors[1] = centerColor;
-            st.mColors[2] = endColor;
+            st.mGradientColors = new int[3];
+            st.mGradientColors[0] = startColor;
+            st.mGradientColors[1] = centerColor;
+            st.mGradientColors[2] = endColor;
 
             st.mPositions = new float[3];
             st.mPositions[0] = 0.0f;
@@ -1357,9 +1359,9 @@
             st.mPositions[1] = st.mCenterX != 0.5f ? st.mCenterX : st.mCenterY;
             st.mPositions[2] = 1f;
         } else {
-            st.mColors = new int[2];
-            st.mColors[0] = startColor;
-            st.mColors[1] = endColor;
+            st.mGradientColors = new int[2];
+            st.mGradientColors[0] = startColor;
+            st.mGradientColors[1] = endColor;
         }
 
         if (st.mGradient == LINEAR_GRADIENT) {
@@ -1552,9 +1554,9 @@
         public int mGradient = LINEAR_GRADIENT;
         public int mAngle = 0;
         public Orientation mOrientation;
-        public ColorStateList mColorStateList;
-        public ColorStateList mStrokeColorStateList;
-        public int[] mColors;
+        public ColorStateList mSolidColors;
+        public ColorStateList mStrokeColors;
+        public int[] mGradientColors;
         public int[] mTempColors; // no need to copy
         public float[] mTempPositions; // no need to copy
         public float[] mPositions;
@@ -1593,9 +1595,9 @@
         int[] mAttrCorners;
         int[] mAttrPadding;
 
-        public GradientState(Orientation orientation, int[] colors) {
+        public GradientState(Orientation orientation, int[] gradientColors) {
             mOrientation = orientation;
-            setColors(colors);
+            setGradientColors(gradientColors);
         }
 
         public GradientState(GradientState state) {
@@ -1604,14 +1606,14 @@
             mGradient = state.mGradient;
             mAngle = state.mAngle;
             mOrientation = state.mOrientation;
-            mColorStateList = state.mColorStateList;
-            if (state.mColors != null) {
-                mColors = state.mColors.clone();
+            mSolidColors = state.mSolidColors;
+            if (state.mGradientColors != null) {
+                mGradientColors = state.mGradientColors.clone();
             }
             if (state.mPositions != null) {
                 mPositions = state.mPositions.clone();
             }
-            mStrokeColorStateList = state.mStrokeColorStateList;
+            mStrokeColors = state.mStrokeColors;
             mStrokeWidth = state.mStrokeWidth;
             mStrokeDashWidth = state.mStrokeDashWidth;
             mStrokeDashGap = state.mStrokeDashGap;
@@ -1655,8 +1657,8 @@
                     || mAttrSolid != null || mAttrStroke != null
                     || mAttrCorners != null || mAttrPadding != null
                     || (mTint != null && mTint.canApplyTheme())
-                    || (mStrokeColorStateList != null && mStrokeColorStateList.canApplyTheme())
-                    || (mColorStateList != null && mColorStateList.canApplyTheme())
+                    || (mStrokeColors != null && mStrokeColors.canApplyTheme())
+                    || (mSolidColors != null && mSolidColors.canApplyTheme())
                     || super.canApplyTheme();
         }
 
@@ -1672,7 +1674,10 @@
 
         @Override
         public int getChangingConfigurations() {
-            return mChangingConfigurations;
+            return mChangingConfigurations
+                    | (mStrokeColors != null ? mStrokeColors.getChangingConfigurations() : 0)
+                    | (mSolidColors != null ? mSolidColors.getChangingConfigurations() : 0)
+                    | (mTint != null ? mTint.getChangingConfigurations() : 0);
         }
 
         public void setShape(int shape) {
@@ -1689,15 +1694,15 @@
             mCenterY = y;
         }
 
-        public void setColors(int[] colors) {
-            mColors = colors;
-            mColorStateList = null;
+        public void setGradientColors(int[] colors) {
+            mGradientColors = colors;
+            mSolidColors = null;
             computeOpacity();
         }
 
-        public void setColorStateList(ColorStateList colorStateList) {
-            mColors = null;
-            mColorStateList = colorStateList;
+        public void setSolidColors(ColorStateList colors) {
+            mGradientColors = null;
+            mSolidColors = colors;
             computeOpacity();
         }
 
@@ -1705,16 +1710,16 @@
             mOpaqueOverBounds = false;
             mOpaqueOverShape = false;
 
-            if (mColors != null) {
-                for (int i = 0; i < mColors.length; i++) {
-                    if (!isOpaque(mColors[i])) {
+            if (mGradientColors != null) {
+                for (int i = 0; i < mGradientColors.length; i++) {
+                    if (!isOpaque(mGradientColors[i])) {
                         return;
                     }
                 }
             }
 
             // An unfilled shape is not opaque over bounds or shape
-            if (mColors == null && mColorStateList == null) {
+            if (mGradientColors == null && mSolidColors == null) {
                 return;
             }
 
@@ -1726,10 +1731,9 @@
                     && mRadiusArray == null;
         }
 
-        public void setStroke(
-                int width, ColorStateList colorStateList, float dashWidth, float dashGap) {
+        public void setStroke(int width, ColorStateList colors, float dashWidth, float dashGap) {
             mStrokeWidth = width;
-            mStrokeColorStateList = colorStateList;
+            mStrokeColors = colors;
             mStrokeDashWidth = dashWidth;
             mStrokeDashGap = dashGap;
             computeOpacity();
@@ -1781,11 +1785,11 @@
     private void updateLocalState(Resources res) {
         final GradientState state = mGradientState;
 
-        if (state.mColorStateList != null) {
+        if (state.mSolidColors != null) {
             final int[] currentState = getState();
-            final int stateColor = state.mColorStateList.getColorForState(currentState, 0);
+            final int stateColor = state.mSolidColors.getColorForState(currentState, 0);
             mFillPaint.setColor(stateColor);
-        } else if (state.mColors == null) {
+        } else if (state.mGradientColors == null) {
             // If we don't have a solid color and we don't have a gradient,
             // the app is stroking the shape, set the color to the default
             // value of state.mSolidColor
@@ -1802,9 +1806,9 @@
             mStrokePaint.setStyle(Paint.Style.STROKE);
             mStrokePaint.setStrokeWidth(state.mStrokeWidth);
 
-            if (state.mStrokeColorStateList != null) {
+            if (state.mStrokeColors != null) {
                 final int[] currentState = getState();
-                final int strokeStateColor = state.mStrokeColorStateList.getColorForState(
+                final int strokeStateColor = state.mStrokeColors.getColorForState(
                         currentState, 0);
                 mStrokePaint.setColor(strokeStateColor);
             }
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index 97f7105..e1ebdbb 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -242,26 +242,12 @@
     }
 
     @Override
-    public ConstantState getConstantState() {
-        if (mState.canConstantState()) {
-            mState.mChangingConfigurations = getChangingConfigurations();
-            return mState;
-        }
-        return null;
-    }
-
-    @Override
     DrawableWrapperState mutateConstantState() {
         mState = new InsetState(mState);
         return mState;
     }
 
     static final class InsetState extends DrawableWrapper.DrawableWrapperState {
-        int[] mThemeAttrs;
-        int mChangingConfigurations;
-
-        ConstantState mDrawableState;
-
         int mInsetLeft = 0;
         int mInsetTop = 0;
         int mInsetRight = 0;
diff --git a/graphics/java/android/graphics/drawable/LayerDrawable.java b/graphics/java/android/graphics/drawable/LayerDrawable.java
index 30fbe16..a2f71e5 100644
--- a/graphics/java/android/graphics/drawable/LayerDrawable.java
+++ b/graphics/java/android/graphics/drawable/LayerDrawable.java
@@ -296,6 +296,9 @@
             final Drawable d = layer.mDrawable;
             if (d.canApplyTheme()) {
                 d.applyTheme(t);
+
+                // Update cached mask of child changing configurations.
+                state.mChildrenChangingConfigurations |= d.getChangingConfigurations();
             }
         }
 
@@ -882,9 +885,7 @@
 
     @Override
     public int getChangingConfigurations() {
-        return super.getChangingConfigurations()
-                | mLayerState.mChangingConfigurations
-                | mLayerState.mChildrenChangingConfigurations;
+        return super.getChangingConfigurations() | mLayerState.getChangingConfigurations();
     }
 
     @Override
@@ -1493,7 +1494,8 @@
 
         @Override
         public int getChangingConfigurations() {
-            return mChangingConfigurations;
+            return mChangingConfigurations
+                    | mChildrenChangingConfigurations;
         }
 
         public final int getOpacity() {
diff --git a/graphics/java/android/graphics/drawable/NinePatchDrawable.java b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
index 487162e..9bf33cf 100644
--- a/graphics/java/android/graphics/drawable/NinePatchDrawable.java
+++ b/graphics/java/android/graphics/drawable/NinePatchDrawable.java
@@ -266,7 +266,7 @@
 
     @Override
     public int getChangingConfigurations() {
-        return super.getChangingConfigurations() | mNinePatchState.mChangingConfigurations;
+        return super.getChangingConfigurations() | mNinePatchState.getChangingConfigurations();
     }
 
     @Override
@@ -498,7 +498,7 @@
         }
 
         if (state.mTint != null && state.mTint.canApplyTheme()) {
-            state.mTint.applyTheme(t);
+            state.mTint = state.mTint.obtainForTheme(t);
         }
 
         updateLocalState(t.getResources());
@@ -680,7 +680,8 @@
 
         @Override
         public int getChangingConfigurations() {
-            return mChangingConfigurations;
+            return mChangingConfigurations
+                    | (mTint != null ? mTint.getChangingConfigurations() : 0);
         }
     }
 
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 23f93fd..6731a17 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -479,7 +479,7 @@
         }
 
         if (state.mColor != null && state.mColor.canApplyTheme()) {
-            state.mColor.applyTheme(t);
+            state.mColor = state.mColor.obtainForTheme(t);
         }
 
         updateLocalState();
@@ -955,6 +955,12 @@
         public Drawable newDrawable(Resources res) {
             return new RippleDrawable(this, res);
         }
+
+        @Override
+        public int getChangingConfigurations() {
+            return super.getChangingConfigurations()
+                    | (mColor != null ? mColor.getChangingConfigurations() : 0);
+        }
     }
 
     private RippleDrawable(RippleState state, Resources res) {
diff --git a/graphics/java/android/graphics/drawable/RotateDrawable.java b/graphics/java/android/graphics/drawable/RotateDrawable.java
index 15e16f1..036a078 100644
--- a/graphics/java/android/graphics/drawable/RotateDrawable.java
+++ b/graphics/java/android/graphics/drawable/RotateDrawable.java
@@ -89,9 +89,6 @@
 
         final RotateState state = mState;
 
-        // Account for any configuration changes.
-        state.mChangingConfigurations |= a.getChangingConfigurations();
-
         // Extract the theme attributes, if any.
         state.mThemeAttrs = a.extractThemeAttrs();
 
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index fc88c15..334b3bd 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -53,9 +53,9 @@
  * For more information about how to use ShapeDrawable, read the <a
  * href="{@docRoot}guide/topics/graphics/2d-graphics.html#shape-drawable">
  * Canvas and Drawables</a> document. For more information about defining a
- * ShapeDrawable in XML, read the <a href="{@docRoot}
- * guide/topics/resources/drawable-resource.html#Shape">Drawable Resources</a>
- * document.
+ * ShapeDrawable in XML, read the
+ * <a href="{@docRoot}guide/topics/resources/drawable-resource.html#Shape">
+ * Drawable Resources</a> document.
  * </p>
  * </div>
  *
@@ -261,8 +261,7 @@
 
     @Override
     public int getChangingConfigurations() {
-        return super.getChangingConfigurations()
-                | mShapeState.mChangingConfigurations;
+        return super.getChangingConfigurations() | mShapeState.getChangingConfigurations();
     }
 
     /**
@@ -427,7 +426,7 @@
 
         // Apply theme to contained color state list.
         if (state.mTint != null && state.mTint.canApplyTheme()) {
-            state.mTint.applyTheme(t);
+            state.mTint = state.mTint.obtainForTheme(t);
         }
 
         // Update local properties.
@@ -578,7 +577,8 @@
 
         @Override
         public int getChangingConfigurations() {
-            return mChangingConfigurations;
+            return mChangingConfigurations
+                    | (mTint != null ? mTint.getChangingConfigurations() : 0);
         }
     }
 
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index c5e53da..a542feb 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -56,21 +56,21 @@
  * <p/>
  * <dt><code>&lt;vector></code></dt>
  * <dl>
- * <dd>Used to defined a vector drawable
+ * <dd>Used to define a vector drawable
  * <dl>
  * <dt><code>android:name</code></dt>
  * <dd>Defines the name of this vector drawable.</dd>
  * <dt><code>android:width</code></dt>
- * <dd>Used to defined the intrinsic width of the drawable.
+ * <dd>Used to define the intrinsic width of the drawable.
  * This support all the dimension units, normally specified with dp.</dd>
  * <dt><code>android:height</code></dt>
- * <dd>Used to defined the intrinsic height the drawable.
+ * <dd>Used to define the intrinsic height the drawable.
  * This support all the dimension units, normally specified with dp.</dd>
  * <dt><code>android:viewportWidth</code></dt>
- * <dd>Used to defined the width of the viewport space. Viewport is basically
+ * <dd>Used to define the width of the viewport space. Viewport is basically
  * the virtual canvas where the paths are drawn on.</dd>
  * <dt><code>android:viewportHeight</code></dt>
- * <dd>Used to defined the height of the viewport space. Viewport is basically
+ * <dd>Used to define the height of the viewport space. Viewport is basically
  * the virtual canvas where the paths are drawn on.</dd>
  * <dt><code>android:tint</code></dt>
  * <dd>The color to apply to the drawable as a tint. By default, no tint is applied.</dd>
@@ -120,7 +120,7 @@
  * <dt><code>android:name</code></dt>
  * <dd>Defines the name of the path.</dd>
  * <dt><code>android:pathData</code></dt>
- * <dd>Defines path string. This is using exactly same format as "d" attribute
+ * <dd>Defines path data using exactly same format as "d" attribute
  * in the SVG's path data. This is defined in the viewport space.</dd>
  * <dt><code>android:fillColor</code></dt>
  * <dd>Defines the color to fill the path (none if not present).</dd>
@@ -156,7 +156,7 @@
  * <dt><code>android:name</code></dt>
  * <dd>Defines the name of the clip path.</dd>
  * <dt><code>android:pathData</code></dt>
- * <dd>Defines clip path string. This is using exactly same format as "d" attribute
+ * <dd>Defines clip path using the same format as "d" attribute
  * in the SVG's path data.</dd>
  * </dl></dd>
  * </dl>
@@ -249,8 +249,8 @@
     @Override
     public void draw(Canvas canvas) {
         final Rect bounds = getBounds();
-        if (bounds.width() == 0 || bounds.height() == 0) {
-            // too small to draw
+        if (bounds.width() <= 0 || bounds.height() <= 0) {
+            // Nothing to draw
             return;
         }
 
@@ -389,7 +389,7 @@
 
         // Apply theme to contained color state list.
         if (state.mTint != null && state.mTint.canApplyTheme()) {
-            state.mTint.applyTheme(t);
+            state.mTint = state.mTint.obtainForTheme(t);
         }
 
         final VPathRenderer path = state.mVPathRenderer;
@@ -625,7 +625,7 @@
 
     @Override
     public int getChangingConfigurations() {
-        return super.getChangingConfigurations() | mVectorState.mChangingConfigurations;
+        return super.getChangingConfigurations() | mVectorState.getChangingConfigurations();
     }
 
     void setAllowCaching(boolean allowCaching) {
@@ -784,7 +784,8 @@
 
         @Override
         public int getChangingConfigurations() {
-            return mChangingConfigurations;
+            return mChangingConfigurations
+                    | (mTint != null ? mTint.getChangingConfigurations() : 0);
         }
     }
 
@@ -805,7 +806,6 @@
         // is no need for deep copying.
         private final Path mPath;
         private final Path mRenderPath;
-        private static final Matrix IDENTITY_MATRIX = new Matrix();
         private final Matrix mFinalPathMatrix = new Matrix();
 
         private Paint mStrokePaint;
@@ -948,7 +948,7 @@
 
         public void draw(Canvas canvas, int w, int h, ColorFilter filter) {
             // Travese the tree in pre-order to draw.
-            drawGroupTree(mRootGroup, IDENTITY_MATRIX, canvas, w, h, filter);
+            drawGroupTree(mRootGroup, Matrix.IDENTITY_MATRIX, canvas, w, h, filter);
         }
 
         private void drawPath(VGroup vGroup, VPath vPath, Canvas canvas, int w, int h,
diff --git a/include/android_runtime/android_view_Surface.h b/include/android_runtime/android_view_Surface.h
index a6836a8..ed83314 100644
--- a/include/android_runtime/android_view_Surface.h
+++ b/include/android_runtime/android_view_Surface.h
@@ -43,6 +43,7 @@
     NV21              = 0x11,
     YUY2              = 0x14,
     RAW_SENSOR        = 0x20,
+    PRIVATE           = 0x22,
     YUV_420_888       = 0x23,
     RAW10             = 0x25,
     JPEG              = 0x100,
diff --git a/include/androidfw/BackupHelpers.h b/include/androidfw/BackupHelpers.h
index 0841af6..fc1ad47 100644
--- a/include/androidfw/BackupHelpers.h
+++ b/include/androidfw/BackupHelpers.h
@@ -17,6 +17,8 @@
 #ifndef _UTILS_BACKUP_HELPERS_H
 #define _UTILS_BACKUP_HELPERS_H
 
+#include <sys/stat.h>
+
 #include <utils/Errors.h>
 #include <utils/String8.h>
 #include <utils/KeyedVector.h>
@@ -135,7 +137,8 @@
         char const* const* files, char const* const *keys, int fileCount);
 
 int write_tarfile(const String8& packageName, const String8& domain,
-        const String8& rootPath, const String8& filePath, BackupDataWriter* outputStream);
+        const String8& rootPath, const String8& filePath, off_t* outSize,
+        BackupDataWriter* outputStream);
 
 class RestoreHelperBase
 {
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 65160d5..a5776a4 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1333,7 +1333,11 @@
         FLAG_COMPLEX = 0x0001,
         // If set, this resource has been declared public, so libraries
         // are allowed to reference it.
-        FLAG_PUBLIC = 0x0002
+        FLAG_PUBLIC = 0x0002,
+        // If set, this is a weak resource and may be overriden by strong
+        // resources of the same name/type. This is only useful during
+        // linking with other resource tables.
+        FLAG_WEAK = 0x0004
     };
     uint16_t flags;
     
diff --git a/keystore/java/android/security/AndroidKeyPairGenerator.java b/keystore/java/android/security/AndroidKeyPairGenerator.java
index 9d9a173..5fae831 100644
--- a/keystore/java/android/security/AndroidKeyPairGenerator.java
+++ b/keystore/java/android/security/AndroidKeyPairGenerator.java
@@ -136,6 +136,8 @@
             throw new IllegalStateException("could not generate key in keystore");
         }
 
+        Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
+
         final PrivateKey privKey;
         final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
         try {
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index acbae8f..0bd1dbd 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -19,6 +19,9 @@
 import com.android.org.conscrypt.OpenSSLEngine;
 import com.android.org.conscrypt.OpenSSLKeyHolder;
 
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
 import android.util.Log;
 
 import java.io.ByteArrayInputStream;
@@ -31,6 +34,7 @@
 import java.security.KeyStore.PrivateKeyEntry;
 import java.security.KeyStore.ProtectionParameter;
 import java.security.KeyStore;
+import java.security.KeyStore.SecretKeyEntry;
 import java.security.KeyStoreException;
 import java.security.KeyStoreSpi;
 import java.security.NoSuchAlgorithmException;
@@ -50,6 +54,8 @@
 import java.util.Iterator;
 import java.util.Set;
 
+import javax.crypto.SecretKey;
+
 /**
  * A java.security.KeyStore interface for the Android KeyStore. An instance of
  * it can be created via the {@link java.security.KeyStore#getInstance(String)
@@ -77,18 +83,72 @@
     @Override
     public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
             UnrecoverableKeyException {
-        if (!isKeyEntry(alias)) {
-            return null;
+        if (isPrivateKeyEntry(alias)) {
+            final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
+            try {
+                return engine.getPrivateKeyById(Credentials.USER_PRIVATE_KEY + alias);
+            } catch (InvalidKeyException e) {
+                UnrecoverableKeyException t = new UnrecoverableKeyException("Can't get key");
+                t.initCause(e);
+                throw t;
+            }
+        } else if (isSecretKeyEntry(alias)) {
+            KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
+            String keyAliasInKeystore = Credentials.USER_SECRET_KEY + alias;
+            int errorCode = mKeyStore.getKeyCharacteristics(
+                    keyAliasInKeystore, null, null, keyCharacteristics);
+            if ((errorCode != KeymasterDefs.KM_ERROR_OK)
+                    && (errorCode != android.security.KeyStore.NO_ERROR)) {
+                throw new UnrecoverableKeyException("Failed to load information about key."
+                        + " Error code: " + errorCode);
+            }
+
+            int keymasterAlgorithm =
+                    keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_ALGORITHM, -1);
+            if (keymasterAlgorithm == -1) {
+                keymasterAlgorithm =
+                        keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_ALGORITHM, -1);
+            }
+            if (keymasterAlgorithm == -1) {
+                throw new UnrecoverableKeyException("Key algorithm unknown");
+            }
+            @KeyStoreKeyConstraints.AlgorithmEnum int keyAlgorithm;
+            try {
+                keyAlgorithm = KeyStoreKeyConstraints.Algorithm.fromKeymaster(keymasterAlgorithm);
+            } catch (IllegalArgumentException e) {
+                throw (UnrecoverableKeyException)
+                        new UnrecoverableKeyException("Unsupported key algorithm").initCause(e);
+            }
+
+            int keymasterDigest =
+                    keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_DIGEST, -1);
+            if (keymasterDigest == -1) {
+                keymasterDigest =
+                        keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_DIGEST, -1);
+            }
+            @KeyStoreKeyConstraints.DigestEnum Integer digest = null;
+            if (keymasterDigest != -1) {
+                try {
+                    digest = KeyStoreKeyConstraints.Digest.fromKeymaster(keymasterDigest);
+                } catch (IllegalArgumentException e) {
+                    throw (UnrecoverableKeyException)
+                            new UnrecoverableKeyException("Unsupported digest").initCause(e);
+                }
+            }
+
+            String keyAlgorithmString;
+            try {
+                keyAlgorithmString = KeyStoreKeyConstraints.Algorithm.toJCASecretKeyAlgorithm(
+                    keyAlgorithm, digest);
+            } catch (IllegalArgumentException e) {
+                throw (UnrecoverableKeyException)
+                        new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
+            }
+
+            return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmString);
         }
 
-        final OpenSSLEngine engine = OpenSSLEngine.getInstance("keystore");
-        try {
-            return engine.getPrivateKeyById(Credentials.USER_PRIVATE_KEY + alias);
-        } catch (InvalidKeyException e) {
-            UnrecoverableKeyException t = new UnrecoverableKeyException("Can't get key");
-            t.initCause(e);
-            throw t;
-        }
+        return null;
     }
 
     @Override
@@ -186,6 +246,11 @@
             return d;
         }
 
+        d = getModificationDate(Credentials.USER_SECRET_KEY + alias);
+        if (d != null) {
+            return d;
+        }
+
         d = getModificationDate(Credentials.USER_CERTIFICATE + alias);
         if (d != null) {
             return d;
@@ -203,8 +268,10 @@
 
         if (key instanceof PrivateKey) {
             setPrivateKeyEntry(alias, (PrivateKey) key, chain, null);
+        } else if (key instanceof SecretKey) {
+            setSecretKeyEntry(alias, (SecretKey) key, null);
         } else {
-            throw new KeyStoreException("Only PrivateKeys are supported");
+            throw new KeyStoreException("Only PrivateKey and SecretKey are supported");
         }
     }
 
@@ -319,6 +386,7 @@
             Credentials.deleteAllTypesForAlias(mKeyStore, alias);
         } else {
             Credentials.deleteCertificateTypesForAlias(mKeyStore, alias);
+            Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias);
         }
 
         final int flags = (params == null) ? 0 : params.getFlags();
@@ -340,6 +408,180 @@
         }
     }
 
+    private void setSecretKeyEntry(String entryAlias, SecretKey key, KeyStoreParameter params)
+            throws KeyStoreException {
+        if (key instanceof KeyStoreSecretKey) {
+            // KeyStore-backed secret key. It cannot be duplicated into another entry and cannot
+            // overwrite its own entry.
+            String keyAliasInKeystore = ((KeyStoreSecretKey) key).getAlias();
+            if (keyAliasInKeystore == null) {
+                throw new KeyStoreException("KeyStore-backed secret key does not have an alias");
+            }
+            if (!keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
+                throw new KeyStoreException("KeyStore-backed secret key has invalid alias: "
+                        + keyAliasInKeystore);
+            }
+            String keyEntryAlias =
+                    keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
+            if (!entryAlias.equals(keyEntryAlias)) {
+                throw new KeyStoreException("Can only replace KeyStore-backed keys with same"
+                        + " alias: " + entryAlias + " != " + keyEntryAlias);
+            }
+            // This is the entry where this key is already stored. No need to do anything.
+            if (params != null) {
+                throw new KeyStoreException("Modifying KeyStore-backed key using protection"
+                        + " parameters not supported");
+            }
+            return;
+        }
+
+        if (params == null) {
+            throw new KeyStoreException(
+                    "Protection parameters must be specified when importing a symmetric key");
+        }
+
+        // Not a KeyStore-backed secret key -- import its key material into keystore.
+        String keyExportFormat = key.getFormat();
+        if (keyExportFormat == null) {
+            throw new KeyStoreException(
+                    "Only secret keys that export their key material are supported");
+        } else if (!"RAW".equals(keyExportFormat)) {
+            throw new KeyStoreException(
+                    "Unsupported secret key material export format: " + keyExportFormat);
+        }
+        byte[] keyMaterial = key.getEncoded();
+        if (keyMaterial == null) {
+            throw new KeyStoreException("Key did not export its key material despite supporting"
+                    + " RAW format export");
+        }
+
+        String keyAlgorithmString = key.getAlgorithm();
+        @KeyStoreKeyConstraints.AlgorithmEnum int keyAlgorithm;
+        @KeyStoreKeyConstraints.DigestEnum Integer digest;
+        try {
+            keyAlgorithm =
+                    KeyStoreKeyConstraints.Algorithm.fromJCASecretKeyAlgorithm(keyAlgorithmString);
+            digest = KeyStoreKeyConstraints.Digest.fromJCASecretKeyAlgorithm(keyAlgorithmString);
+        } catch (IllegalArgumentException e) {
+            throw new KeyStoreException("Unsupported secret key algorithm: " + keyAlgorithmString);
+        }
+
+        if ((params.getAlgorithm() != null) && (params.getAlgorithm() != keyAlgorithm)) {
+            throw new KeyStoreException("Key algorithm mismatch. Key: " + keyAlgorithmString
+                    + ", parameter spec: "
+                    + KeyStoreKeyConstraints.Algorithm.toString(params.getAlgorithm()));
+        }
+
+        KeymasterArguments args = new KeymasterArguments();
+        args.addInt(KeymasterDefs.KM_TAG_ALGORITHM,
+                KeyStoreKeyConstraints.Algorithm.toKeymaster(keyAlgorithm));
+
+        if (digest != null) {
+            // Digest available from JCA key algorithm
+            if (params.getDigest() != null) {
+                // Digest also specified in parameters -- check that these two match
+                if (digest != params.getDigest()) {
+                    throw new KeyStoreException("Key digest mismatch. Key: " + keyAlgorithmString
+                            + ", parameter spec: "
+                            + KeyStoreKeyConstraints.Digest.toString(params.getDigest()));
+                }
+            }
+        } else {
+            // Digest not available from JCA key algorithm
+            digest = params.getDigest();
+        }
+        if (digest != null) {
+            args.addInt(KeymasterDefs.KM_TAG_DIGEST,
+                    KeyStoreKeyConstraints.Digest.toKeymaster(digest));
+            Integer digestOutputSizeBytes =
+                    KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest);
+            if (digestOutputSizeBytes != null) {
+                // TODO: Remove MAC length constraint once Keymaster API no longer requires it.
+                // TODO: Switch to bits instead of bytes, once this is fixed in Keymaster
+                args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes);
+            }
+        }
+        if (keyAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) {
+            if (digest == null) {
+                throw new IllegalStateException("Digest algorithm must be specified for key"
+                        + " algorithm " + keyAlgorithmString);
+            }
+        }
+
+        @KeyStoreKeyConstraints.PurposeEnum int purposes = (params.getPurposes() != null)
+                ? params.getPurposes()
+                : (KeyStoreKeyConstraints.Purpose.ENCRYPT
+                        | KeyStoreKeyConstraints.Purpose.DECRYPT
+                        | KeyStoreKeyConstraints.Purpose.SIGN
+                        | KeyStoreKeyConstraints.Purpose.VERIFY);
+        for (int keymasterPurpose :
+            KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
+            args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
+        }
+        if (params.getBlockMode() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE,
+                    KeyStoreKeyConstraints.BlockMode.toKeymaster(params.getBlockMode()));
+        }
+        if (params.getPadding() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_PADDING,
+                    KeyStoreKeyConstraints.Padding.toKeymaster(params.getPadding()));
+        }
+        if (params.getMaxUsesPerBoot() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT, params.getMaxUsesPerBoot());
+        }
+        if (params.getMinSecondsBetweenOperations() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS,
+                    params.getMinSecondsBetweenOperations());
+        }
+        if (params.getUserAuthenticators().isEmpty()) {
+            args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
+        } else {
+            args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
+                    KeyStoreKeyConstraints.UserAuthenticator.allToKeymaster(
+                            params.getUserAuthenticators()));
+        }
+        if (params.isInvalidatedOnNewFingerprintEnrolled()) {
+            // TODO: Add the invalidate on fingerprint enrolled constraint once Keymaster supports
+            // that.
+        }
+        if (params.getUserAuthenticationValidityDurationSeconds() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
+                    params.getUserAuthenticationValidityDurationSeconds());
+        }
+        args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
+                (params.getKeyValidityStart() != null)
+                        ? params.getKeyValidityStart() : new Date(0));
+        args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
+                (params.getKeyValidityForOriginationEnd() != null)
+                        ? params.getKeyValidityForOriginationEnd() : new Date(Long.MAX_VALUE));
+        args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
+                (params.getKeyValidityForConsumptionEnd() != null)
+                        ? params.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE));
+
+        // TODO: Remove this once keymaster does not require us to specify the size of imported key.
+        args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keyMaterial.length * 8);
+
+        if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
+                || ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) {
+            // Permit caller-specified IV. This is needed for the Cipher abstraction.
+            args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
+        }
+
+        Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias);
+        String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias;
+        int errorCode = mKeyStore.importKey(
+                keyAliasInKeystore,
+                args,
+                KeymasterDefs.KM_KEY_FORMAT_RAW,
+                keyMaterial,
+                params.getFlags(),
+                new KeyCharacteristics());
+        if (errorCode != android.security.KeyStore.NO_ERROR) {
+            throw new KeyStoreException("Failed to import secret key. Keystore error code: "
+                + errorCode);
+        }
+    }
+
     @Override
     public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
             throws KeyStoreException {
@@ -413,6 +655,7 @@
         }
 
         return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias)
+                || mKeyStore.contains(Credentials.USER_SECRET_KEY + alias)
                 || mKeyStore.contains(Credentials.USER_CERTIFICATE + alias)
                 || mKeyStore.contains(Credentials.CA_CERTIFICATE + alias);
     }
@@ -428,6 +671,10 @@
     }
 
     private boolean isKeyEntry(String alias) {
+        return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias);
+    }
+
+    private boolean isPrivateKeyEntry(String alias) {
         if (alias == null) {
             throw new NullPointerException("alias == null");
         }
@@ -435,6 +682,14 @@
         return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias);
     }
 
+    private boolean isSecretKeyEntry(String alias) {
+        if (alias == null) {
+            throw new NullPointerException("alias == null");
+        }
+
+        return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias);
+    }
+
     private boolean isCertificateEntry(String alias) {
         if (alias == null) {
             throw new NullPointerException("alias == null");
@@ -554,11 +809,14 @@
             PrivateKeyEntry prE = (PrivateKeyEntry) entry;
             setPrivateKeyEntry(alias, prE.getPrivateKey(), prE.getCertificateChain(),
                     (KeyStoreParameter) param);
-            return;
+        } else if (entry instanceof SecretKeyEntry) {
+            SecretKeyEntry secE = (SecretKeyEntry) entry;
+            setSecretKeyEntry(alias, secE.getSecretKey(), (KeyStoreParameter) param);
+        } else {
+            throw new KeyStoreException(
+                    "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry"
+                    + "; was " + entry);
         }
-
-        throw new KeyStoreException(
-                "Entry must be a PrivateKeyEntry or TrustedCertificateEntry; was " + entry);
     }
 
 }
diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java
index 9081e92..a7c2ddb 100644
--- a/keystore/java/android/security/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/AndroidKeyStoreProvider.java
@@ -16,8 +16,12 @@
 
 package android.security;
 
+import java.lang.reflect.Method;
 import java.security.Provider;
 
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
 /**
  * A provider focused on providing JCA interfaces for the Android KeyStore.
  *
@@ -35,5 +39,78 @@
         // java.security.KeyPairGenerator
         put("KeyPairGenerator.EC", AndroidKeyPairGenerator.EC.class.getName());
         put("KeyPairGenerator.RSA", AndroidKeyPairGenerator.RSA.class.getName());
+
+        // javax.crypto.KeyGenerator
+        put("KeyGenerator.AES", KeyStoreKeyGeneratorSpi.AES.class.getName());
+        put("KeyGenerator.HmacSHA256", KeyStoreKeyGeneratorSpi.HmacSHA256.class.getName());
+
+        // java.security.SecretKeyFactory
+        put("SecretKeyFactory.AES", KeyStoreSecretKeyFactorySpi.class.getName());
+        put("SecretKeyFactory.HmacSHA256", KeyStoreSecretKeyFactorySpi.class.getName());
+
+        // javax.crypto.Mac
+        putMacImpl("HmacSHA256", KeyStoreHmacSpi.HmacSHA256.class.getName());
+
+        // javax.crypto.Cipher
+        putSymmetricCipherImpl("AES/ECB/NoPadding",
+                KeyStoreCipherSpi.AES.ECB.NoPadding.class.getName());
+        putSymmetricCipherImpl("AES/ECB/PKCS7Padding",
+                KeyStoreCipherSpi.AES.ECB.PKCS7Padding.class.getName());
+
+        putSymmetricCipherImpl("AES/CBC/NoPadding",
+                KeyStoreCipherSpi.AES.CBC.NoPadding.class.getName());
+        putSymmetricCipherImpl("AES/CBC/PKCS7Padding",
+                KeyStoreCipherSpi.AES.CBC.PKCS7Padding.class.getName());
+
+        putSymmetricCipherImpl("AES/CTR/NoPadding",
+                KeyStoreCipherSpi.AES.CTR.NoPadding.class.getName());
+    }
+
+    private void putMacImpl(String algorithm, String implClass) {
+        put("Mac." + algorithm, implClass);
+        put("Mac." + algorithm + " SupportedKeyClasses", KeyStoreSecretKey.class.getName());
+    }
+
+    private void putSymmetricCipherImpl(String transformation, String implClass) {
+        put("Cipher." + transformation, implClass);
+        put("Cipher." + transformation + " SupportedKeyClasses", KeyStoreSecretKey.class.getName());
+    }
+
+    /**
+     * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto
+     * primitive.
+     *
+     * <p>The following primitives are supported: {@link Cipher} and {@link Mac}.
+     *
+     * @return KeyStore operation handle or {@code null} if the provided primitive's KeyStore
+     *         operation is not in progress.
+     *
+     * @throws IllegalArgumentException if the provided primitive is not supported or is not backed
+     *         by AndroidKeyStore provider.
+     */
+    public static Long getKeyStoreOperationHandle(Object cryptoPrimitive) {
+        if (cryptoPrimitive == null) {
+            throw new NullPointerException();
+        }
+        if ((!(cryptoPrimitive instanceof Mac)) && (!(cryptoPrimitive instanceof Cipher))) {
+            throw new IllegalArgumentException("Unsupported crypto primitive: " + cryptoPrimitive);
+        }
+        Object spi;
+        // TODO: Replace this Reflection based codewith direct invocations once the libcore changes
+        // are in.
+        try {
+            Method getSpiMethod = cryptoPrimitive.getClass().getDeclaredMethod("getSpi");
+            getSpiMethod.setAccessible(true);
+            spi = getSpiMethod.invoke(cryptoPrimitive);
+        } catch (ReflectiveOperationException e) {
+            throw new IllegalArgumentException(
+                    "Unsupported crypto primitive: " + cryptoPrimitive, e);
+        }
+        if (!(spi instanceof KeyStoreCryptoOperation)) {
+            throw new IllegalArgumentException(
+                    "Crypto primitive not backed by Android KeyStore: " + cryptoPrimitive
+                    + ", spi: " + spi);
+        }
+        return ((KeyStoreCryptoOperation) spi).getOperationHandle();
     }
 }
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index af76d9d..6283e02 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -61,6 +61,9 @@
     /** Key prefix for user private keys. */
     public static final String USER_PRIVATE_KEY = "USRPKEY_";
 
+    /** Key prefix for user secret keys. */
+    public static final String USER_SECRET_KEY = "USRSKEY_";
+
     /** Key prefix for VPN. */
     public static final String VPN = "VPN_";
 
@@ -218,7 +221,8 @@
          * Make sure every type is deleted. There can be all three types, so
          * don't use a conditional here.
          */
-        return keystore.delKey(Credentials.USER_PRIVATE_KEY + alias)
+        return keystore.delete(Credentials.USER_PRIVATE_KEY + alias)
+                | keystore.delete(Credentials.USER_SECRET_KEY + alias)
                 | deleteCertificateTypesForAlias(keystore, alias);
     }
 
@@ -235,4 +239,20 @@
         return keystore.delete(Credentials.USER_CERTIFICATE + alias)
                 | keystore.delete(Credentials.CA_CERTIFICATE + alias);
     }
+
+    /**
+     * Delete private key for a particular {@code alias}.
+     * Returns {@code true} if an entry was was deleted.
+     */
+    static boolean deletePrivateKeyTypeForAlias(KeyStore keystore, String alias) {
+        return keystore.delete(Credentials.USER_PRIVATE_KEY + alias);
+    }
+
+    /**
+     * Delete secret key for a particular {@code alias}.
+     * Returns {@code true} if an entry was was deleted.
+     */
+    static boolean deleteSecretKeyTypeForAlias(KeyStore keystore, String alias) {
+        return keystore.delete(Credentials.USER_SECRET_KEY + alias);
+    }
 }
diff --git a/keystore/java/android/security/CryptoOperationException.java b/keystore/java/android/security/CryptoOperationException.java
new file mode 100644
index 0000000..00c142f
--- /dev/null
+++ b/keystore/java/android/security/CryptoOperationException.java
@@ -0,0 +1,61 @@
+/*
+ * 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 android.security;
+
+/**
+ * Base class for exceptions during cryptographic operations which cannot throw a suitable checked
+ * exception.
+ *
+ * <p>The contract of the majority of crypto primitives/operations (e.g. {@code Cipher} or
+ * {@code Signature}) is that they can throw a checked exception during initialization, but are not
+ * permitted to throw a checked exception during operation. Because crypto operations can fail
+ * for a variety of reasons after initialization, this base class provides type-safety for unchecked
+ * exceptions that may be thrown in those cases.
+ *
+ * @hide
+ */
+public class CryptoOperationException extends RuntimeException {
+
+    /**
+     * Constructs a new {@code CryptoOperationException} without detail message and cause.
+     */
+    public CryptoOperationException() {
+        super();
+    }
+
+    /**
+     * Constructs a new {@code CryptoOperationException} with the provided detail message and no
+     * cause.
+     */
+    public CryptoOperationException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new {@code CryptoOperationException} with the provided detail message and cause.
+     */
+    public CryptoOperationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Constructs a new {@code CryptoOperationException} with the provided cause.
+     */
+    public CryptoOperationException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git a/keystore/java/android/security/KeyExpiredException.java b/keystore/java/android/security/KeyExpiredException.java
new file mode 100644
index 0000000..35a5acc
--- /dev/null
+++ b/keystore/java/android/security/KeyExpiredException.java
@@ -0,0 +1,47 @@
+/*
+ * 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 android.security;
+
+/**
+ * Indicates that a cryptographic operation failed because the employed key's validity end date
+ * is in the past.
+ *
+ * @hide
+ */
+public class KeyExpiredException extends CryptoOperationException {
+
+    /**
+     * Constructs a new {@code KeyExpiredException} without detail message and cause.
+     */
+    public KeyExpiredException() {
+        super("Key expired");
+    }
+
+    /**
+     * Constructs a new {@code KeyExpiredException} with the provided detail message and no cause.
+     */
+    public KeyExpiredException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new {@code KeyExpiredException} with the provided detail message and cause.
+     */
+    public KeyExpiredException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java
new file mode 100644
index 0000000..7058383
--- /dev/null
+++ b/keystore/java/android/security/KeyGeneratorSpec.java
@@ -0,0 +1,521 @@
+/*
+ * 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 android.security;
+
+import android.content.Context;
+import android.text.TextUtils;
+
+import java.security.cert.Certificate;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
+/**
+ * {@link AlgorithmParameterSpec} for initializing a {@code KeyGenerator} that works with
+ * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore facility</a>.
+ *
+ * <p>The Android KeyStore facility is accessed through a {@link KeyGenerator} API
+ * using the {@code AndroidKeyStore} provider. The {@code context} passed in may be used to pop up
+ * some UI to ask the user to unlock or initialize the Android KeyStore facility.
+ *
+ * <p>After generation, the {@code keyStoreAlias} is used with the
+ * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)}
+ * interface to retrieve the {@link SecretKey} and its associated {@link Certificate} chain.
+ *
+ * @hide
+ */
+public class KeyGeneratorSpec implements AlgorithmParameterSpec {
+
+    private final Context mContext;
+    private final String mKeystoreAlias;
+    private final int mFlags;
+    private final Integer mKeySize;
+    private final Date mKeyValidityStart;
+    private final Date mKeyValidityForOriginationEnd;
+    private final Date mKeyValidityForConsumptionEnd;
+    private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+    private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+    private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+    private final Integer mMinSecondsBetweenOperations;
+    private final Integer mMaxUsesPerBoot;
+    private final Set<Integer> mUserAuthenticators;
+    private final Integer mUserAuthenticationValidityDurationSeconds;
+    private final boolean mInvalidatedOnNewFingerprintEnrolled;
+
+    private KeyGeneratorSpec(
+            Context context,
+            String keyStoreAlias,
+            int flags,
+            Integer keySize,
+            Date keyValidityStart,
+            Date keyValidityForOriginationEnd,
+            Date keyValidityForConsumptionEnd,
+            @KeyStoreKeyConstraints.PurposeEnum Integer purposes,
+            @KeyStoreKeyConstraints.PaddingEnum Integer padding,
+            @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
+            Integer minSecondsBetweenOperations,
+            Integer maxUsesPerBoot,
+            Set<Integer> userAuthenticators,
+            Integer userAuthenticationValidityDurationSeconds,
+            boolean invalidatedOnNewFingerprintEnrolled) {
+        if (context == null) {
+            throw new IllegalArgumentException("context == null");
+        } else if (TextUtils.isEmpty(keyStoreAlias)) {
+            throw new IllegalArgumentException("keyStoreAlias must not be empty");
+        } else if ((userAuthenticationValidityDurationSeconds != null)
+                && (userAuthenticationValidityDurationSeconds < 0)) {
+            throw new IllegalArgumentException(
+                    "userAuthenticationValidityDurationSeconds must not be negative");
+        }
+
+        mContext = context;
+        mKeystoreAlias = keyStoreAlias;
+        mFlags = flags;
+        mKeySize = keySize;
+        mKeyValidityStart = keyValidityStart;
+        mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
+        mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
+        mPurposes = purposes;
+        mPadding = padding;
+        mBlockMode = blockMode;
+        mMinSecondsBetweenOperations = minSecondsBetweenOperations;
+        mMaxUsesPerBoot = maxUsesPerBoot;
+        mUserAuthenticators = (userAuthenticators != null)
+                ? new HashSet<Integer>(userAuthenticators)
+                : Collections.<Integer>emptySet();
+        mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+        mInvalidatedOnNewFingerprintEnrolled = invalidatedOnNewFingerprintEnrolled;
+    }
+
+    /**
+     * Gets the Android context used for operations with this instance.
+     */
+    public Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * Returns the alias that will be used in the {@code java.security.KeyStore} in conjunction with
+     * the {@code AndroidKeyStore}.
+     */
+    public String getKeystoreAlias() {
+        return mKeystoreAlias;
+    }
+
+    /**
+     * @hide
+     */
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * Gets the requested key size or {@code null} if the default size should be used.
+     */
+    public Integer getKeySize() {
+        return mKeySize;
+    }
+
+    /**
+     * Gets the time instant before which the key is not yet valid.
+     *
+     * @return instant or {@code null} if not restricted.
+     */
+    public Date getKeyValidityStart() {
+        return mKeyValidityStart;
+    }
+
+    /**
+     * Gets the time instant after which the key is no longer valid for decryption and verification.
+     *
+     * @return instant or {@code null} if not restricted.
+     *
+     * @hide
+     */
+    public Date getKeyValidityForConsumptionEnd() {
+        return mKeyValidityForConsumptionEnd;
+    }
+
+    /**
+     * Gets the time instant after which the key is no longer valid for encryption and signing.
+     *
+     * @return instant or {@code null} if not restricted.
+     */
+    public Date getKeyValidityForOriginationEnd() {
+        return mKeyValidityForOriginationEnd;
+    }
+
+    /**
+     * Gets the set of purposes for which the key can be used.
+     *
+     * @return set of purposes or {@code null} if the key can be used for any purpose.
+     */
+    public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() {
+        return mPurposes;
+    }
+
+    /**
+     * Gets the padding scheme to which the key is restricted.
+     *
+     * @return padding scheme or {@code null} if the padding scheme is not restricted.
+     */
+    public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
+        return mPadding;
+    }
+
+    /**
+     * Gets the block mode to which the key is restricted when used for encryption or decryption.
+     *
+     * @return block more or {@code null} if block mode is not restricted.
+     *
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
+        return mBlockMode;
+    }
+
+    /**
+     * Gets the minimum number of seconds that must expire since the most recent use of the key
+     * before it can be used again.
+     *
+     * @return number of seconds or {@code null} if there is no restriction on how frequently a key
+     *         can be used.
+     *
+     * @hide
+     */
+    public Integer getMinSecondsBetweenOperations() {
+        return mMinSecondsBetweenOperations;
+    }
+
+    /**
+     * Gets the number of times the key can be used without rebooting the device.
+     *
+     * @return maximum number of times or {@code null} if there is no restriction.
+     * @hide
+     */
+    public Integer getMaxUsesPerBoot() {
+        return mMaxUsesPerBoot;
+    }
+
+    /**
+     * Gets the user authenticators which protect access to this key. The key can only be used iff
+     * the user has authenticated to at least one of these user authenticators.
+     *
+     * @return user authenticators or empty set if the key can be used without user authentication.
+     *
+     * @hide
+     */
+    public Set<Integer> getUserAuthenticators() {
+        return new HashSet<Integer>(mUserAuthenticators);
+    }
+
+    /**
+     * Gets the duration of time (seconds) for which this key can be used after the user
+     * successfully authenticates to one of the associated user authenticators.
+     *
+     * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+     *         is required for every use of the key.
+     *
+     * @hide
+     */
+    public Integer getUserAuthenticationValidityDurationSeconds() {
+        return mUserAuthenticationValidityDurationSeconds;
+    }
+
+    /**
+     * Returns {@code true} if this key must be permanently invalidated once a new fingerprint is
+     * enrolled. This constraint only has effect if fingerprint reader is one of the user
+     * authenticators protecting access to this key.
+     *
+     * @see #getUserAuthenticators()
+     *
+     * @hide
+     */
+    public boolean isInvalidatedOnNewFingerprintEnrolled() {
+        return mInvalidatedOnNewFingerprintEnrolled;
+    }
+
+    /**
+     * Returns {@code true} if the key must be encrypted in the {@link java.security.KeyStore}.
+     */
+    public boolean isEncryptionRequired() {
+        return (mFlags & KeyStore.FLAG_ENCRYPTED) != 0;
+    }
+
+    public static class Builder {
+        private final Context mContext;
+        private String mKeystoreAlias;
+        private int mFlags;
+        private Integer mKeySize;
+        private Date mKeyValidityStart;
+        private Date mKeyValidityForOriginationEnd;
+        private Date mKeyValidityForConsumptionEnd;
+        private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+        private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+        private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+        private Integer mMinSecondsBetweenOperations;
+        private Integer mMaxUsesPerBoot;
+        private Set<Integer> mUserAuthenticators;
+        private Integer mUserAuthenticationValidityDurationSeconds;
+        private boolean mInvalidatedOnNewFingerprintEnrolled;
+
+        /**
+         * Creates a new instance of the {@code Builder} with the given {@code context}. The
+         * {@code context} passed in may be used to pop up some UI to ask the user to unlock or
+         * initialize the Android KeyStore facility.
+         */
+        public Builder(Context context) {
+            if (context == null) {
+                throw new NullPointerException("context == null");
+            }
+            mContext = context;
+        }
+
+        /**
+         * Sets the alias to be used to retrieve the key later from a {@link java.security.KeyStore}
+         * instance using the {@code AndroidKeyStore} provider.
+         *
+         * <p>The alias must be provided. There is no default.
+         */
+        public Builder setAlias(String alias) {
+            if (alias == null) {
+                throw new NullPointerException("alias == null");
+            }
+            mKeystoreAlias = alias;
+            return this;
+        }
+
+        /**
+         * Sets the size (in bits) of the key to be generated.
+         *
+         * <p>By default, the key size will be determines based on the key algorithm. For example,
+         * for {@code HmacSHA256}, the key size will default to {@code 256}.
+         */
+        public Builder setKeySize(int keySize) {
+            mKeySize = keySize;
+            return this;
+        }
+
+        /**
+         * Indicates that this key must be encrypted at rest on storage. Note that enabling this
+         * will require that the user enable a strong lock screen (e.g., PIN, password) before
+         * creating or using the generated key is successful.
+         */
+        public Builder setEncryptionRequired(boolean required) {
+            if (required) {
+                mFlags |= KeyStore.FLAG_ENCRYPTED;
+            } else {
+                mFlags &= ~KeyStore.FLAG_ENCRYPTED;
+            }
+            return this;
+        }
+
+        /**
+         * Sets the time instant before which the key is not yet valid.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityStart(Date startDate) {
+            mKeyValidityStart = startDate;
+            return this;
+        }
+
+        /**
+         * Sets the time instant after which the key is no longer valid.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityStart(Date)
+         * @see #setKeyValidityForConsumptionEnd(Date)
+         * @see #setKeyValidityForOriginationEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityEnd(Date endDate) {
+            setKeyValidityForOriginationEnd(endDate);
+            setKeyValidityForConsumptionEnd(endDate);
+            return this;
+        }
+
+        /**
+         * Sets the time instant after which the key is no longer valid for encryption and signing.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityForConsumptionEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityForOriginationEnd(Date endDate) {
+            mKeyValidityForOriginationEnd = endDate;
+            return this;
+        }
+
+        /**
+         * Sets the time instant after which the key is no longer valid for decryption and
+         * verification.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityForOriginationEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityForConsumptionEnd(Date endDate) {
+            mKeyValidityForConsumptionEnd = endDate;
+            return this;
+        }
+
+        /**
+         * Restricts the purposes for which the key can be used to the provided set of purposes.
+         *
+         * <p>By default, the key can be used for encryption, decryption, signing, and verification.
+         *
+         * @hide
+         */
+        public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) {
+            mPurposes = purposes;
+            return this;
+        }
+
+        /**
+         * Restricts the key to being used only with the provided padding scheme. Attempts to use
+         * the key with any other padding will be rejected.
+         *
+         * <p>This restriction must be specified for keys which are used for encryption/decryption.
+         *
+         * @hide
+         */
+        public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) {
+            mPadding = padding;
+            return this;
+        }
+
+        /**
+         * Restricts the key to being used only with the provided block mode when encrypting or
+         * decrypting. Attempts to use the key with any other block modes will be rejected.
+         *
+         * <p>This restriction must be specified for keys which are used for encryption/decryption.
+         *
+         * @hide
+         */
+        public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) {
+            mBlockMode = blockMode;
+            return this;
+        }
+
+        /**
+         * Sets the minimum number of seconds that must expire since the most recent use of the key
+         * before it can be used again.
+         *
+         * <p>By default, there is no restriction on how frequently a key can be used.
+         *
+         * @hide
+         */
+        public Builder setMinSecondsBetweenOperations(int seconds) {
+            mMinSecondsBetweenOperations = seconds;
+            return this;
+        }
+
+        /**
+         * Sets the maximum number of times a key can be used without rebooting the device.
+         *
+         * <p>By default, the key can be used for an unlimited number of times.
+         *
+         * @hide
+         */
+        public Builder setMaxUsesPerBoot(int count) {
+            mMaxUsesPerBoot = count;
+            return this;
+        }
+
+        /**
+         * Sets the user authenticators which protect access to this key. The key can only be used
+         * iff the user has authenticated to at least one of these user authenticators.
+         *
+         * <p>By default, the key can be used without user authentication.
+         *
+         * @param userAuthenticators user authenticators or empty list if this key can be accessed
+         *        without user authentication.
+         *
+         * @see #setUserAuthenticationValidityDurationSeconds(int)
+         *
+         * @hide
+         */
+        public Builder setUserAuthenticators(Set<Integer> userAuthenticators) {
+            mUserAuthenticators =
+                    (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null;
+            return this;
+        }
+
+        /**
+         * Sets the duration of time (seconds) for which this key can be used after the user
+         * successfully authenticates to one of the associated user authenticators.
+         *
+         * <p>By default, the user needs to authenticate for every use of the key.
+         *
+         * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+         *        every use of the key.
+         *
+         * @see #setUserAuthenticators(Set)
+         *
+         * @hide
+         */
+        public Builder setUserAuthenticationValidityDurationSeconds(int seconds) {
+            mUserAuthenticationValidityDurationSeconds = seconds;
+            return this;
+        }
+
+        /**
+         * Sets whether this key must be invalidated (permanently) once a new fingerprint is
+         * enrolled. This only has effect if fingerprint reader is one of the user authenticators
+         * protecting access to the key.
+         *
+         * <p>By default, enrolling a new fingerprint does not invalidate the key.
+         *
+         * @see #setUserAuthenticators(Set)
+         *
+         * @hide
+         */
+        public Builder setInvalidatedOnNewFingerprintEnrolled(boolean invalidated) {
+            mInvalidatedOnNewFingerprintEnrolled = invalidated;
+            return this;
+        }
+
+        /**
+         * Builds a new instance instance of {@code KeyGeneratorSpec}.
+         *
+         * @throws IllegalArgumentException if a required field is missing or violates a constraint.
+         */
+        public KeyGeneratorSpec build() {
+            return new KeyGeneratorSpec(mContext, mKeystoreAlias, mFlags, mKeySize,
+                    mKeyValidityStart, mKeyValidityForOriginationEnd, mKeyValidityForConsumptionEnd,
+                    mPurposes, mPadding, mBlockMode, mMinSecondsBetweenOperations, mMaxUsesPerBoot,
+                    mUserAuthenticators, mUserAuthenticationValidityDurationSeconds,
+                    mInvalidatedOnNewFingerprintEnrolled);
+        }
+    }
+}
diff --git a/keystore/java/android/security/KeyNotYetValidException.java b/keystore/java/android/security/KeyNotYetValidException.java
new file mode 100644
index 0000000..f1c2cac
--- /dev/null
+++ b/keystore/java/android/security/KeyNotYetValidException.java
@@ -0,0 +1,48 @@
+/*
+ * 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 android.security;
+
+/**
+ * Indicates that a cryptographic operation failed because the employed key's validity start date
+ * is in the future.
+ *
+ * @hide
+ */
+public class KeyNotYetValidException extends CryptoOperationException {
+
+    /**
+     * Constructs a new {@code KeyNotYetValidException} without detail message and cause.
+     */
+    public KeyNotYetValidException() {
+        super("Key not yet valid");
+    }
+
+    /**
+     * Constructs a new {@code KeyNotYetValidException} with the provided detail message and no
+     * cause.
+     */
+    public KeyNotYetValidException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new {@code KeyNotYetValidException} with the provided detail message and cause.
+     */
+    public KeyNotYetValidException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java
index cc097aa..0001604 100644
--- a/keystore/java/android/security/KeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/KeyPairGeneratorSpec.java
@@ -24,7 +24,10 @@
 import java.security.PrivateKey;
 import java.security.cert.Certificate;
 import java.security.spec.AlgorithmParameterSpec;
+import java.util.Collections;
 import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
 
 import javax.security.auth.x500.X500Principal;
 
@@ -72,6 +75,28 @@
 
     private final int mFlags;
 
+    private final Date mKeyValidityStart;
+
+    private final Date mKeyValidityForOriginationEnd;
+
+    private final Date mKeyValidityForConsumptionEnd;
+
+    private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+
+    private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+
+    private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+
+    private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+
+    private final Integer mMinSecondsBetweenOperations;
+
+    private final Integer mMaxUsesPerBoot;
+
+    private final Set<Integer> mUserAuthenticators;
+
+    private final Integer mUserAuthenticationValidityDurationSeconds;
+
     /**
      * Parameter specification for the "{@code AndroidKeyPairGenerator}"
      * instance of the {@link java.security.KeyPairGenerator} API. The
@@ -106,7 +131,18 @@
      */
     public KeyPairGeneratorSpec(Context context, String keyStoreAlias, String keyType, int keySize,
             AlgorithmParameterSpec spec, X500Principal subjectDN, BigInteger serialNumber,
-            Date startDate, Date endDate, int flags) {
+            Date startDate, Date endDate, int flags,
+            Date keyValidityStart,
+            Date keyValidityForOriginationEnd,
+            Date keyValidityForConsumptionEnd,
+            @KeyStoreKeyConstraints.PurposeEnum Integer purposes,
+            @KeyStoreKeyConstraints.DigestEnum Integer digest,
+            @KeyStoreKeyConstraints.PaddingEnum Integer padding,
+            @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
+            Integer minSecondsBetweenOperations,
+            Integer maxUsesPerBoot,
+            Set<Integer> userAuthenticators,
+            Integer userAuthenticationValidityDurationSeconds) {
         if (context == null) {
             throw new IllegalArgumentException("context == null");
         } else if (TextUtils.isEmpty(keyStoreAlias)) {
@@ -121,6 +157,10 @@
             throw new IllegalArgumentException("endDate == null");
         } else if (endDate.before(startDate)) {
             throw new IllegalArgumentException("endDate < startDate");
+        } else if ((userAuthenticationValidityDurationSeconds != null)
+                && (userAuthenticationValidityDurationSeconds < 0)) {
+            throw new IllegalArgumentException(
+                    "userAuthenticationValidityDurationSeconds must not be negative");
         }
 
         mContext = context;
@@ -133,6 +173,31 @@
         mStartDate = startDate;
         mEndDate = endDate;
         mFlags = flags;
+        mKeyValidityStart = keyValidityStart;
+        mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
+        mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
+        mPurposes = purposes;
+        mDigest = digest;
+        mPadding = padding;
+        mBlockMode = blockMode;
+        mMinSecondsBetweenOperations = minSecondsBetweenOperations;
+        mMaxUsesPerBoot = maxUsesPerBoot;
+        mUserAuthenticators = (userAuthenticators != null)
+                ? new HashSet<Integer>(userAuthenticators)
+                : Collections.<Integer>emptySet();
+        mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+    }
+
+    /**
+     * TODO: Remove this constructor once tests are switched over to the new one above.
+     * @hide
+     */
+    public KeyPairGeneratorSpec(Context context, String keyStoreAlias, String keyType, int keySize,
+            AlgorithmParameterSpec spec, X500Principal subjectDN, BigInteger serialNumber,
+            Date startDate, Date endDate, int flags) {
+        this(context, keyStoreAlias, keyType, keySize, spec, subjectDN, serialNumber, startDate,
+                endDate, flags, startDate, endDate, endDate, null, null, null, null, null, null,
+                null, null);
     }
 
     /**
@@ -222,6 +287,145 @@
     }
 
     /**
+     * Gets the time instant before which the key pair is not yet valid.
+     *
+     * @return instant or {@code null} if not restricted.
+     *
+     * @hide
+     */
+    public Date getKeyValidityStart() {
+        return mKeyValidityStart;
+    }
+
+    /**
+     * Gets the time instant after which the key pair is no longer valid for decryption and
+     * verification.
+     *
+     * @return instant or {@code null} if not restricted.
+     *
+     * @hide
+     */
+    public Date getKeyValidityForConsumptionEnd() {
+        return mKeyValidityForConsumptionEnd;
+    }
+
+    /**
+     * Gets the time instant after which the key pair is no longer valid for encryption and signing.
+     *
+     * @return instant or {@code null} if not restricted.
+     *
+     * @hide
+     */
+    public Date getKeyValidityForOriginationEnd() {
+        return mKeyValidityForOriginationEnd;
+    }
+
+    /**
+     * Gets the set of purposes for which the key can be used.
+     *
+     * @return set of purposes or {@code null} if the key can be used for any purpose.
+     *
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() {
+        return mPurposes;
+    }
+
+    /**
+     * Gets the digest to which the key is restricted.
+     *
+     * @return digest or {@code null} if the digest is not restricted.
+     *
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() {
+        return mDigest;
+    }
+
+    /**
+     * Gets the padding scheme to which the key is restricted.
+     *
+     * @return padding scheme or {@code null} if the padding scheme is not restricted.
+     *
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
+        return mPadding;
+    }
+
+    /**
+     * Gets the block mode to which the key is restricted when used for encryption or decryption.
+     *
+     * @return block more or {@code null} if block mode is not restricted.
+     *
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
+        return mBlockMode;
+    }
+
+    /**
+     * Gets the minimum number of seconds that must expire since the most recent use of the private
+     * key before it can be used again.
+     *
+     * <p>This restriction applies only to private key operations. Public key operations are not
+     * restricted.
+     *
+     * @return number of seconds or {@code null} if there is no restriction on how frequently a key
+     *         can be used.
+     *
+     * @hide
+     */
+    public Integer getMinSecondsBetweenOperations() {
+        return mMinSecondsBetweenOperations;
+    }
+
+    /**
+     * Gets the number of times the private key can be used without rebooting the device.
+     *
+     * <p>This restriction applies only to private key operations. Public key operations are not
+     * restricted.
+     *
+     * @return maximum number of times or {@code null} if there is no restriction.
+     *
+     * @hide
+     */
+    public Integer getMaxUsesPerBoot() {
+        return mMaxUsesPerBoot;
+    }
+
+    /**
+     * Gets the user authenticators which protect access to the private key. The key can only be
+     * used iff the user has authenticated to at least one of these user authenticators.
+     *
+     * <p>This restriction applies only to private key operations. Public key operations are not
+     * restricted.
+     *
+     * @return user authenticators or empty set if the key can be used without user authentication.
+     *
+     * @hide
+     */
+    public Set<Integer> getUserAuthenticators() {
+        return new HashSet<Integer>(mUserAuthenticators);
+    }
+
+    /**
+     * Gets the duration of time (seconds) for which the private key can be used after the user
+     * successfully authenticates to one of the associated user authenticators.
+     *
+     * <p>This restriction applies only to private key operations. Public key operations are not
+     * restricted.
+     *
+     * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+     *         is required for every use of the key.
+     *
+     * @hide
+     */
+    public Integer getUserAuthenticationValidityDurationSeconds() {
+        return mUserAuthenticationValidityDurationSeconds;
+    }
+
+    /**
      * Builder class for {@link KeyPairGeneratorSpec} objects.
      * <p>
      * This will build a parameter spec for use with the <a href="{@docRoot}
@@ -263,6 +467,28 @@
 
         private int mFlags;
 
+        private Date mKeyValidityStart;
+
+        private Date mKeyValidityForOriginationEnd;
+
+        private Date mKeyValidityForConsumptionEnd;
+
+        private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+
+        private @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+
+        private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+
+        private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+
+        private Integer mMinSecondsBetweenOperations;
+
+        private Integer mMaxUsesPerBoot;
+
+        private Set<Integer> mUserAuthenticators;
+
+        private Integer mUserAuthenticationValidityDurationSeconds;
+
         /**
          * Creates a new instance of the {@code Builder} with the given
          * {@code context}. The {@code context} passed in may be used to pop up
@@ -389,14 +615,218 @@
         }
 
         /**
+         * Sets the time instant before which the key is not yet valid.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityStart(Date startDate) {
+            mKeyValidityStart = startDate;
+            return this;
+        }
+
+        /**
+         * Sets the time instant after which the key is no longer valid.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityStart(Date)
+         * @see #setKeyValidityForConsumptionEnd(Date)
+         * @see #setKeyValidityForOriginationEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityEnd(Date endDate) {
+            setKeyValidityForOriginationEnd(endDate);
+            setKeyValidityForConsumptionEnd(endDate);
+            return this;
+        }
+
+        /**
+         * Sets the time instant after which the key is no longer valid for encryption and signing.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityForConsumptionEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityForOriginationEnd(Date endDate) {
+            mKeyValidityForOriginationEnd = endDate;
+            return this;
+        }
+
+        /**
+         * Sets the time instant after which the key is no longer valid for decryption and
+         * verification.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityForOriginationEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityForConsumptionEnd(Date endDate) {
+            mKeyValidityForConsumptionEnd = endDate;
+            return this;
+        }
+
+        /**
+         * Restricts the purposes for which the key can be used to the provided set of purposes.
+         *
+         * <p>By default, the key can be used for encryption, decryption, signing, and verification.
+         *
+         * @hide
+         */
+        public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) {
+            mPurposes = purposes;
+            return this;
+        }
+
+        /**
+         * Restricts the key to being used only with the provided digest. Attempts to use the key
+         * with any other digests be rejected.
+         *
+         * <p>This restriction must be specified for keys which are used for signing/verification.
+         *
+         * @hide
+         */
+        public Builder setDigest(@KeyStoreKeyConstraints.DigestEnum int digest) {
+            mDigest = digest;
+            return this;
+        }
+
+        /**
+         * Restricts the key to being used only with the provided padding scheme. Attempts to use
+         * the key with any other padding will be rejected.
+         *
+         * <p>This restriction must be specified for keys which are used for encryption/decryption.
+         *
+         * @hide
+         */
+        public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) {
+            mPadding = padding;
+            return this;
+        }
+
+        /**
+         * Restricts the key to being used only with the provided block mode when encrypting or
+         * decrypting. Attempts to use the key with any other block modes will be rejected.
+         *
+         * <p>This restriction must be specified for keys which are used for encryption/decryption.
+         *
+         * @hide
+         */
+        public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) {
+            mBlockMode = blockMode;
+            return this;
+        }
+
+        /**
+         * Sets the minimum number of seconds that must expire since the most recent use of the key
+         * before it can be used again.
+         *
+         * <p>By default, there is no restriction on how frequently a key can be used.
+         *
+         * <p>This restriction applies only to private key operations. Public key operations are not
+         * restricted.
+         *
+         * @hide
+         */
+        public Builder setMinSecondsBetweenOperations(int seconds) {
+            mMinSecondsBetweenOperations = seconds;
+            return this;
+        }
+
+        /**
+         * Sets the maximum number of times a key can be used without rebooting the device.
+         *
+         * <p>By default, the key can be used for an unlimited number of times.
+         *
+         * <p>This restriction applies only to private key operations. Public key operations are not
+         * restricted.
+         *
+         * @hide
+         */
+        public Builder setMaxUsesPerBoot(int count) {
+            mMaxUsesPerBoot = count;
+            return this;
+        }
+
+        /**
+         * Sets the user authenticators which protect access to this key. The key can only be used
+         * iff the user has authenticated to at least one of these user authenticators.
+         *
+         * <p>By default, the key can be used without user authentication.
+         *
+         * <p>This restriction applies only to private key operations. Public key operations are not
+         * restricted.
+         *
+         * @param userAuthenticators user authenticators or empty list if this key can be accessed
+         *        without user authentication.
+         *
+         * @see #setUserAuthenticationValidityDurationSeconds(int)
+         *
+         * @hide
+         */
+        public Builder setUserAuthenticators(Set<Integer> userAuthenticators) {
+            mUserAuthenticators =
+                    (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null;
+            return this;
+        }
+
+        /**
+         * Sets the duration of time (seconds) for which this key can be used after the user
+         * successfully authenticates to one of the associated user authenticators.
+         *
+         * <p>By default, the user needs to authenticate for every use of the key.
+         *
+         * <p>This restriction applies only to private key operations. Public key operations are not
+         * restricted.
+         *
+         * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+         *        every use of the key.
+         *
+         * @see #setUserAuthenticators(Set)
+         *
+         * @hide
+         */
+        public Builder setUserAuthenticationValidityDurationSeconds(int seconds) {
+            mUserAuthenticationValidityDurationSeconds = seconds;
+            return this;
+        }
+
+        /**
          * Builds the instance of the {@code KeyPairGeneratorSpec}.
          *
          * @throws IllegalArgumentException if a required field is missing
          * @return built instance of {@code KeyPairGeneratorSpec}
          */
         public KeyPairGeneratorSpec build() {
-            return new KeyPairGeneratorSpec(mContext, mKeystoreAlias, mKeyType, mKeySize, mSpec,
-                    mSubjectDN, mSerialNumber, mStartDate, mEndDate, mFlags);
+            return new KeyPairGeneratorSpec(mContext,
+                    mKeystoreAlias,
+                    mKeyType,
+                    mKeySize,
+                    mSpec,
+                    mSubjectDN,
+                    mSerialNumber,
+                    mStartDate,
+                    mEndDate,
+                    mFlags,
+                    mKeyValidityStart,
+                    mKeyValidityForOriginationEnd,
+                    mKeyValidityForConsumptionEnd,
+                    mPurposes,
+                    mDigest,
+                    mPadding,
+                    mBlockMode,
+                    mMinSecondsBetweenOperations,
+                    mMaxUsesPerBoot,
+                    mUserAuthenticators,
+                    mUserAuthenticationValidityDurationSeconds);
         }
     }
 }
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index bfbf028..94a479b 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -25,6 +25,7 @@
 import android.security.keymaster.ExportResult;
 import android.security.keymaster.KeyCharacteristics;
 import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterBlob;
 import android.security.keymaster.OperationResult;
 import android.util.Log;
 
@@ -388,22 +389,22 @@
         }
     }
 
-    public int generateKey(String alias, KeymasterArguments args, int uid, int flags,
-            KeyCharacteristics outCharacteristics) {
+    public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int uid,
+            int flags, KeyCharacteristics outCharacteristics) {
         try {
-            return mBinder.generateKey(alias, args, uid, flags, outCharacteristics);
+            return mBinder.generateKey(alias, args, entropy, uid, flags, outCharacteristics);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return SYSTEM_ERROR;
         }
     }
 
-    public int generateKey(String alias, KeymasterArguments args, int flags,
+    public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int flags,
             KeyCharacteristics outCharacteristics) {
-        return generateKey(alias, args, UID_SELF, flags, outCharacteristics);
+        return generateKey(alias, args, entropy, UID_SELF, flags, outCharacteristics);
     }
 
-    public int getKeyCharacteristics(String alias, byte[] clientId, byte[] appId,
+    public int getKeyCharacteristics(String alias, KeymasterBlob clientId, KeymasterBlob appId,
             KeyCharacteristics outCharacteristics) {
         try {
             return mBinder.getKeyCharacteristics(alias, clientId, appId, outCharacteristics);
@@ -429,7 +430,8 @@
         return importKey(alias, args, format, keyData, UID_SELF, flags, outCharacteristics);
     }
 
-    public ExportResult exportKey(String alias, int format, byte[] clientId, byte[] appId) {
+    public ExportResult exportKey(String alias, int format, KeymasterBlob clientId,
+            KeymasterBlob appId) {
         try {
             return mBinder.exportKey(alias, format, clientId, appId);
         } catch (RemoteException e) {
@@ -439,9 +441,9 @@
     }
 
     public OperationResult begin(String alias, int purpose, boolean pruneable,
-            KeymasterArguments args, KeymasterArguments outArgs) {
+            KeymasterArguments args, byte[] entropy, KeymasterArguments outArgs) {
         try {
-            return mBinder.begin(getToken(), alias, purpose, pruneable, args, outArgs);
+            return mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, outArgs);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return null;
@@ -474,4 +476,34 @@
             return SYSTEM_ERROR;
         }
     }
+
+    /**
+     * Check if the operation referenced by {@code token} is currently authorized.
+     *
+     * @param token An operation token returned by a call to {@link KeyStore.begin}.
+     */
+    public boolean isOperationAuthorized(IBinder token) {
+        try {
+            return mBinder.isOperationAuthorized(token);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return false;
+        }
+    }
+
+    /**
+     * Add an authentication record to the keystore authorization table.
+     *
+     * @param authToken The packed bytes of a hw_auth_token_t to be provided to keymaster.
+     * @return {@code KeyStore.NO_ERROR} on success, otherwise an error value corresponding to
+     * a {@code KeymasterDefs.KM_ERROR_} value or {@code KeyStore} ResponseCode.
+     */
+    public int addAuthToken(byte[] authToken) {
+        try {
+            return mBinder.addAuthToken(authToken);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return SYSTEM_ERROR;
+        }
+    }
 }
diff --git a/keystore/java/android/security/KeyStoreCipherSpi.java b/keystore/java/android/security/KeyStoreCipherSpi.java
new file mode 100644
index 0000000..afb5e36
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreCipherSpi.java
@@ -0,0 +1,573 @@
+/*
+ * 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 android.security;
+
+import android.os.IBinder;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keymaster.OperationResult;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.util.Arrays;
+
+import javax.crypto.AEADBadTagException;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.IvParameterSpec;
+
+/**
+ * Base class for {@link CipherSpi} providing Android KeyStore backed ciphers.
+ *
+ * @hide
+ */
+public abstract class KeyStoreCipherSpi extends CipherSpi implements KeyStoreCryptoOperation {
+
+    public abstract static class AES extends KeyStoreCipherSpi {
+        protected AES(@KeyStoreKeyConstraints.BlockModeEnum int blockMode,
+                @KeyStoreKeyConstraints.PaddingEnum int padding, boolean ivUsed) {
+            super(KeyStoreKeyConstraints.Algorithm.AES,
+                    blockMode,
+                    padding,
+                    16,
+                    ivUsed);
+        }
+
+        public abstract static class ECB extends AES {
+            protected ECB(@KeyStoreKeyConstraints.PaddingEnum int padding) {
+                super(KeyStoreKeyConstraints.BlockMode.ECB, padding, false);
+            }
+
+            public static class NoPadding extends ECB {
+                public NoPadding() {
+                    super(KeyStoreKeyConstraints.Padding.NONE);
+                }
+            }
+
+            public static class PKCS7Padding extends ECB {
+                public PKCS7Padding() {
+                    super(KeyStoreKeyConstraints.Padding.PKCS7);
+                }
+            }
+        }
+
+        public abstract static class CBC extends AES {
+            protected CBC(@KeyStoreKeyConstraints.BlockModeEnum int padding) {
+                super(KeyStoreKeyConstraints.BlockMode.CBC, padding, true);
+            }
+
+            public static class NoPadding extends CBC {
+                public NoPadding() {
+                    super(KeyStoreKeyConstraints.Padding.NONE);
+                }
+            }
+
+            public static class PKCS7Padding extends CBC {
+                public PKCS7Padding() {
+                    super(KeyStoreKeyConstraints.Padding.PKCS7);
+                }
+            }
+        }
+
+        public abstract static class CTR extends AES {
+            protected CTR(@KeyStoreKeyConstraints.BlockModeEnum int padding) {
+                super(KeyStoreKeyConstraints.BlockMode.CTR, padding, true);
+            }
+
+            public static class NoPadding extends CTR {
+                public NoPadding() {
+                    super(KeyStoreKeyConstraints.Padding.NONE);
+                }
+            }
+        }
+    }
+
+    private final KeyStore mKeyStore;
+    private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm;
+    private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockMode;
+    private final @KeyStoreKeyConstraints.PaddingEnum int mPadding;
+    private final int mBlockSizeBytes;
+    private final boolean mIvUsed;
+
+    // Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after
+    // doFinal finishes.
+    protected boolean mEncrypting;
+    private KeyStoreSecretKey mKey;
+    private SecureRandom mRng;
+    private boolean mFirstOperationInitiated;
+    byte[] mIv;
+
+    // Fields below must be reset
+    private byte[] mAdditionalEntropyForBegin;
+    /**
+     * Token referencing this operation inside keystore service. It is initialized by
+     * {@code engineInit} and is invalidated when {@code engineDoFinal} succeeds and one some
+     * error conditions in between.
+     */
+    private IBinder mOperationToken;
+    private Long mOperationHandle;
+    private KeyStoreCryptoOperationChunkedStreamer mMainDataStreamer;
+
+    protected KeyStoreCipherSpi(
+            @KeyStoreKeyConstraints.AlgorithmEnum int algorithm,
+            @KeyStoreKeyConstraints.BlockModeEnum int blockMode,
+            @KeyStoreKeyConstraints.PaddingEnum int padding,
+            int blockSizeBytes,
+            boolean ivUsed) {
+        mKeyStore = KeyStore.getInstance();
+        mAlgorithm = algorithm;
+        mBlockMode = blockMode;
+        mPadding = padding;
+        mBlockSizeBytes = blockSizeBytes;
+        mIvUsed = ivUsed;
+    }
+
+    @Override
+    protected void engineInit(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
+        init(opmode, key, random);
+        initAlgorithmSpecificParameters();
+        ensureKeystoreOperationInitialized();
+    }
+
+    @Override
+    protected void engineInit(int opmode, Key key, AlgorithmParameters params, SecureRandom random)
+            throws InvalidKeyException, InvalidAlgorithmParameterException {
+        init(opmode, key, random);
+        initAlgorithmSpecificParameters(params);
+        ensureKeystoreOperationInitialized();
+    }
+
+    @Override
+    protected void engineInit(int opmode, Key key, AlgorithmParameterSpec params,
+            SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException {
+        init(opmode, key, random);
+        initAlgorithmSpecificParameters(params);
+        ensureKeystoreOperationInitialized();
+    }
+
+    private void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
+        reset();
+        if (!(key instanceof KeyStoreSecretKey)) {
+            throw new InvalidKeyException(
+                    "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null"));
+        }
+        mKey = (KeyStoreSecretKey) key;
+        mRng = random;
+        mIv = null;
+        mFirstOperationInitiated = false;
+
+        if ((opmode != Cipher.ENCRYPT_MODE) && (opmode != Cipher.DECRYPT_MODE)) {
+            throw new UnsupportedOperationException(
+                    "Only ENCRYPT and DECRYPT modes supported. Mode: " + opmode);
+        }
+        mEncrypting = opmode == Cipher.ENCRYPT_MODE;
+    }
+
+    private void reset() {
+        IBinder operationToken = mOperationToken;
+        if (operationToken != null) {
+            mOperationToken = null;
+            mKeyStore.abort(operationToken);
+        }
+        mOperationHandle = null;
+        mMainDataStreamer = null;
+        mAdditionalEntropyForBegin = null;
+    }
+
+    private void ensureKeystoreOperationInitialized() {
+        if (mMainDataStreamer != null) {
+            return;
+        }
+        if (mKey == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+
+        KeymasterArguments keymasterInputArgs = new KeymasterArguments();
+        keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, mAlgorithm);
+        keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, mBlockMode);
+        keymasterInputArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mPadding);
+        addAlgorithmSpecificParametersToBegin(keymasterInputArgs);
+
+        KeymasterArguments keymasterOutputArgs = new KeymasterArguments();
+        OperationResult opResult = mKeyStore.begin(
+                mKey.getAlias(),
+                mEncrypting ? KeymasterDefs.KM_PURPOSE_ENCRYPT : KeymasterDefs.KM_PURPOSE_DECRYPT,
+                true, // permit aborting this operation if keystore runs out of resources
+                keymasterInputArgs,
+                mAdditionalEntropyForBegin,
+                keymasterOutputArgs);
+        mAdditionalEntropyForBegin = null;
+        if (opResult == null) {
+            throw new KeyStoreConnectException();
+        } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+            throw KeymasterUtils.getCryptoOperationException(opResult.resultCode);
+        }
+
+        if (opResult.token == null) {
+            throw new CryptoOperationException("Keystore returned null operation token");
+        }
+        mOperationToken = opResult.token;
+        mOperationHandle = opResult.operationHandle;
+        loadAlgorithmSpecificParametersFromBeginResult(keymasterOutputArgs);
+        mFirstOperationInitiated = true;
+        mMainDataStreamer = new KeyStoreCryptoOperationChunkedStreamer(
+                new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
+                        mKeyStore, opResult.token));
+    }
+
+    @Override
+    protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) {
+        ensureKeystoreOperationInitialized();
+
+        if (inputLen == 0) {
+            return null;
+        }
+
+        byte[] output;
+        try {
+            output = mMainDataStreamer.update(input, inputOffset, inputLen);
+        } catch (KeymasterException e) {
+            throw KeymasterUtils.getCryptoOperationException(e);
+        }
+
+        if (output.length == 0) {
+            return null;
+        }
+
+        return output;
+    }
+
+    @Override
+    protected int engineUpdate(byte[] input, int inputOffset, int inputLen, byte[] output,
+            int outputOffset) throws ShortBufferException {
+        byte[] outputCopy = engineUpdate(input, inputOffset, inputLen);
+        if (outputCopy == null) {
+            return 0;
+        }
+        int outputAvailable = output.length - outputOffset;
+        if (outputCopy.length > outputAvailable) {
+            throw new ShortBufferException("Output buffer too short. Produced: "
+                    + outputCopy.length + ", available: " + outputAvailable);
+        }
+        System.arraycopy(outputCopy, 0, output, outputOffset, outputCopy.length);
+        return outputCopy.length;
+    }
+
+    @Override
+    protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen)
+            throws IllegalBlockSizeException, BadPaddingException {
+        ensureKeystoreOperationInitialized();
+
+        byte[] output;
+        try {
+            output = mMainDataStreamer.doFinal(input, inputOffset, inputLen);
+        } catch (KeymasterException e) {
+            switch (e.getErrorCode()) {
+                case KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH:
+                    throw new IllegalBlockSizeException();
+                case KeymasterDefs.KM_ERROR_INVALID_ARGUMENT:
+                    throw new BadPaddingException();
+                case KeymasterDefs.KM_ERROR_VERIFICATION_FAILED:
+                    throw new AEADBadTagException();
+                default:
+                    throw KeymasterUtils.getCryptoOperationException(e);
+            }
+        }
+
+        reset();
+        return output;
+    }
+
+    @Override
+    protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
+            int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
+            BadPaddingException {
+        byte[] outputCopy = engineDoFinal(input, inputOffset, inputLen);
+        if (outputCopy == null) {
+            return 0;
+        }
+        int outputAvailable = output.length - outputOffset;
+        if (outputCopy.length > outputAvailable) {
+            throw new ShortBufferException("Output buffer too short. Produced: "
+                    + outputCopy.length + ", available: " + outputAvailable);
+        }
+        System.arraycopy(outputCopy, 0, output, outputOffset, outputCopy.length);
+        return outputCopy.length;
+    }
+
+    @Override
+    protected int engineGetBlockSize() {
+        return mBlockSizeBytes;
+    }
+
+    @Override
+    protected byte[] engineGetIV() {
+        return (mIv != null) ? mIv.clone() : null;
+    }
+
+    @Override
+    protected int engineGetOutputSize(int inputLen) {
+        return inputLen + 3 * engineGetBlockSize();
+    }
+
+    @Override
+    protected void engineSetMode(String mode) throws NoSuchAlgorithmException {
+        // This should never be invoked because all algorithms registered with the AndroidKeyStore
+        // provide explicitly specify block mode.
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected void engineSetPadding(String arg0) throws NoSuchPaddingException {
+        // This should never be invoked because all algorithms registered with the AndroidKeyStore
+        // provide explicitly specify padding mode.
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void finalize() throws Throwable {
+        try {
+            IBinder operationToken = mOperationToken;
+            if (operationToken != null) {
+                mKeyStore.abort(operationToken);
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    @Override
+    public Long getOperationHandle() {
+        return mOperationHandle;
+    }
+
+    // The methods below may need to be overridden by subclasses that use algorithm-specific
+    // parameters.
+
+    /**
+     * Returns algorithm-specific parameters used by this {@code CipherSpi} instance or {@code null}
+     * if no algorithm-specific parameters are used.
+     *
+     * <p>This implementation only handles the IV parameter.
+     */
+    @Override
+    protected AlgorithmParameters engineGetParameters() {
+        if (!mIvUsed) {
+            return null;
+        }
+        if ((mIv != null) && (mIv.length > 0)) {
+            try {
+                AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
+                params.init(new IvParameterSpec(mIv));
+                return params;
+            } catch (NoSuchAlgorithmException e) {
+                throw new RuntimeException("Failed to obtain AES AlgorithmParameters", e);
+            } catch (InvalidParameterSpecException e) {
+                throw new RuntimeException(
+                        "Failed to initialize AES AlgorithmParameters with an IV", e);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Invoked by {@code engineInit} to initialize algorithm-specific parameters. These parameters
+     * may need to be stored to be reused after {@code doFinal}.
+     *
+     * <p>The default implementation only handles the IV parameters.
+     *
+     * @param params algorithm parameters.
+     *
+     * @throws InvalidAlgorithmParameterException if some/all of the parameters cannot be
+     *         automatically configured and thus {@code Cipher.init} needs to be invoked with
+     *         explicitly provided parameters.
+     */
+    protected void initAlgorithmSpecificParameters(AlgorithmParameterSpec params)
+            throws InvalidAlgorithmParameterException {
+        if (!mIvUsed) {
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
+            }
+            return;
+        }
+
+        // IV is used
+        if (params == null) {
+            if (!mEncrypting) {
+                // IV must be provided by the caller
+                throw new InvalidAlgorithmParameterException(
+                        "IvParameterSpec must be provided when decrypting");
+            }
+            return;
+        }
+        if (!(params instanceof IvParameterSpec)) {
+            throw new InvalidAlgorithmParameterException("Only IvParameterSpec supported");
+        }
+        mIv = ((IvParameterSpec) params).getIV();
+        if (mIv == null) {
+            throw new InvalidAlgorithmParameterException("Null IV in IvParameterSpec");
+        }
+    }
+
+    /**
+     * Invoked by {@code engineInit} to initialize algorithm-specific parameters. These parameters
+     * may need to be stored to be reused after {@code doFinal}.
+     *
+     * <p>The default implementation only handles the IV parameters.
+     *
+     * @param params algorithm parameters.
+     *
+     * @throws InvalidAlgorithmParameterException if some/all of the parameters cannot be
+     *         automatically configured and thus {@code Cipher.init} needs to be invoked with
+     *         explicitly provided parameters.
+     */
+    protected void initAlgorithmSpecificParameters(AlgorithmParameters params)
+            throws InvalidAlgorithmParameterException {
+        if (!mIvUsed) {
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
+            }
+            return;
+        }
+
+        // IV is used
+        if (params == null) {
+            if (!mEncrypting) {
+                // IV must be provided by the caller
+                throw new InvalidAlgorithmParameterException("IV required when decrypting"
+                        + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
+            }
+            return;
+        }
+
+        IvParameterSpec ivSpec;
+        try {
+            ivSpec = params.getParameterSpec(IvParameterSpec.class);
+        } catch (InvalidParameterSpecException e) {
+            if (!mEncrypting) {
+                // IV must be provided by the caller
+                throw new InvalidAlgorithmParameterException("IV required when decrypting"
+                        + ", but not found in parameters: " + params, e);
+            }
+            mIv = null;
+            return;
+        }
+        mIv = ivSpec.getIV();
+        if (mIv == null) {
+            throw new InvalidAlgorithmParameterException("Null IV in AlgorithmParameters");
+        }
+    }
+
+    /**
+     * Invoked by {@code engineInit} to initialize algorithm-specific parameters. These parameters
+     * may need to be stored to be reused after {@code doFinal}.
+     *
+     * <p>The default implementation only handles the IV parameter.
+     *
+     * @throws InvalidKeyException if some/all of the parameters cannot be automatically configured
+     *         and thus {@code Cipher.init} needs to be invoked with explicitly provided parameters.
+     */
+    protected void initAlgorithmSpecificParameters() throws InvalidKeyException {
+        if (!mIvUsed) {
+            return;
+        }
+
+        // IV is used
+        if (!mEncrypting) {
+            throw new InvalidKeyException("IV required when decrypting"
+                    + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
+        }
+    }
+
+    /**
+     * Invoked to add algorithm-specific parameters for the KeyStore's {@code begin} operation.
+     *
+     * <p>The default implementation takes care of the IV.
+     *
+     * @param keymasterArgs keystore/keymaster arguments to be populated with algorithm-specific
+     *        parameters.
+     */
+    protected void addAlgorithmSpecificParametersToBegin(KeymasterArguments keymasterArgs) {
+        if (!mFirstOperationInitiated) {
+            // First begin operation -- see if we need to provide additional entropy for IV
+            // generation.
+            if (mIvUsed) {
+                // IV is needed
+                if ((mIv == null) && (mEncrypting)) {
+                    // TODO: Switch to keymaster-generated IV code below once keymaster supports
+                    // that.
+                    // IV is needed but was not provided by the caller -- generate an IV.
+                    mIv = new byte[mBlockSizeBytes];
+                    SecureRandom rng = (mRng != null) ? mRng : new SecureRandom();
+                    rng.nextBytes(mIv);
+//                    // IV was not provided by the caller and thus will be generated by keymaster.
+//                    // Mix in some additional entropy from the provided SecureRandom.
+//                    if (mRng != null) {
+//                        mAdditionalEntropyForBegin = new byte[mBlockSizeBytes];
+//                        mRng.nextBytes(mAdditionalEntropyForBegin);
+//                    }
+                }
+            }
+        }
+
+        if ((mIvUsed) && (mIv != null)) {
+            keymasterArgs.addBlob(KeymasterDefs.KM_TAG_NONCE, mIv);
+        }
+    }
+
+    /**
+     * Invoked by {@code engineInit} to obtain algorithm-specific parameters from the result of the
+     * Keymaster's {@code begin} operation. Some of these parameters may need to be reused after
+     * {@code doFinal} by {@link #addAlgorithmSpecificParametersToBegin(KeymasterArguments)}.
+     *
+     * <p>The default implementation only takes care of the IV.
+     *
+     * @param keymasterArgs keystore/keymaster arguments returned by KeyStore {@code begin}
+     *        operation.
+     */
+    protected void loadAlgorithmSpecificParametersFromBeginResult(
+            KeymasterArguments keymasterArgs) {
+        // NOTE: Keymaster doesn't always return an IV, even if it's used.
+        byte[] returnedIv = keymasterArgs.getBlob(KeymasterDefs.KM_TAG_NONCE, null);
+        if ((returnedIv != null) && (returnedIv.length == 0)) {
+            returnedIv = null;
+        }
+
+        if (mIvUsed) {
+            if (mIv == null) {
+                mIv = returnedIv;
+            } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
+                throw new CryptoOperationException("IV in use differs from provided IV");
+            }
+        } else {
+            if (returnedIv != null) {
+                throw new CryptoOperationException(
+                        "IV in use despite IV not being used by this transformation");
+            }
+        }
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreConnectException.java b/keystore/java/android/security/KeyStoreConnectException.java
new file mode 100644
index 0000000..8ed6e04
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreConnectException.java
@@ -0,0 +1,28 @@
+/*
+ * 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 android.security;
+
+/**
+ * Indicates a communications error with keystore service.
+ *
+ * @hide
+ */
+public class KeyStoreConnectException extends CryptoOperationException {
+    public KeyStoreConnectException() {
+        super("Failed to communicate with keystore service");
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreCryptoOperation.java b/keystore/java/android/security/KeyStoreCryptoOperation.java
new file mode 100644
index 0000000..19abd05
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreCryptoOperation.java
@@ -0,0 +1,31 @@
+/*
+ * 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 android.security;
+
+/**
+ * Cryptographic operation backed by {@link KeyStore}.
+ *
+ * @hide
+ */
+public interface KeyStoreCryptoOperation {
+    /**
+     * Gets the KeyStore operation handle of this crypto operation.
+     *
+     * @return handle or {@code null} if the KeyStore operation is not in progress.
+     */
+    Long getOperationHandle();
+}
diff --git a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
new file mode 100644
index 0000000..993614b
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
@@ -0,0 +1,309 @@
+/*
+ * 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 android.security;
+
+import android.os.IBinder;
+import android.security.keymaster.OperationResult;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
+ * {@code update} and {@code finish} operations.
+ *
+ * <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's
+ * update and finish operations. Firstly, KeyStore's update operation can consume only a limited
+ * amount of data in one go because the operations are marshalled via Binder. Secondly, the update
+ * operation may consume less data than provided, in which case the caller has to buffer the
+ * remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and
+ * {@link #doFinal(byte[], int, int) doFinal} operations which can be used to conveniently implement
+ * various JCA crypto primitives.
+ *
+ * <p>Bidirectional chunked streaming of data via a KeyStore crypto operation is abstracted away as
+ * a {@link Stream} to avoid having this class deal with operation tokens and occasional additional
+ * parameters to {@code update} and {@code final} operations.
+ *
+ * @hide
+ */
+public class KeyStoreCryptoOperationChunkedStreamer {
+
+    /**
+     * Bidirectional chunked data stream over a KeyStore crypto operation.
+     */
+    public interface Stream {
+        /**
+         * Returns the result of the KeyStore {@code update} operation or null if keystore couldn't
+         * be reached.
+         */
+        OperationResult update(byte[] input);
+
+        /**
+         * Returns the result of the KeyStore {@code finish} operation or null if keystore couldn't
+         * be reached.
+         */
+        OperationResult finish();
+    }
+
+    // Binder buffer is about 1MB, but it's shared between all active transactions of the process.
+    // Thus, it's safer to use a much smaller upper bound.
+    private static final int DEFAULT_MAX_CHUNK_SIZE = 64 * 1024;
+    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+
+    private final Stream mKeyStoreStream;
+    private final int mMaxChunkSize;
+
+    private byte[] mBuffered = EMPTY_BYTE_ARRAY;
+    private int mBufferedOffset;
+    private int mBufferedLength;
+
+    public KeyStoreCryptoOperationChunkedStreamer(Stream operation) {
+        this(operation, DEFAULT_MAX_CHUNK_SIZE);
+    }
+
+    public KeyStoreCryptoOperationChunkedStreamer(Stream operation, int maxChunkSize) {
+        mKeyStoreStream = operation;
+        mMaxChunkSize = maxChunkSize;
+    }
+
+    public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeymasterException {
+        if (inputLength == 0) {
+            // No input provided
+            return EMPTY_BYTE_ARRAY;
+        }
+
+        ByteArrayOutputStream bufferedOutput = null;
+
+        while (inputLength > 0) {
+            byte[] chunk;
+            int inputBytesInChunk;
+            if ((mBufferedLength + inputLength) > mMaxChunkSize) {
+                // Too much input for one chunk -- extract one max-sized chunk and feed it into the
+                // update operation.
+                inputBytesInChunk = mMaxChunkSize - mBufferedLength;
+                chunk = concat(mBuffered, mBufferedOffset, mBufferedLength,
+                        input, inputOffset, inputBytesInChunk);
+            } else {
+                // All of available input fits into one chunk.
+                if ((mBufferedLength == 0) && (inputOffset == 0)
+                        && (inputLength == input.length)) {
+                    // Nothing buffered and all of input array needs to be fed into the update
+                    // operation.
+                    chunk = input;
+                    inputBytesInChunk = input.length;
+                } else {
+                    // Need to combine buffered data with input data into one array.
+                    inputBytesInChunk = inputLength;
+                    chunk = concat(mBuffered, mBufferedOffset, mBufferedLength,
+                            input, inputOffset, inputBytesInChunk);
+                }
+            }
+            // Update input array references to reflect that some of its bytes are now in mBuffered.
+            inputOffset += inputBytesInChunk;
+            inputLength -= inputBytesInChunk;
+
+            OperationResult opResult = mKeyStoreStream.update(chunk);
+            if (opResult == null) {
+                throw new KeyStoreConnectException();
+            } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+                throw KeymasterUtils.getKeymasterException(opResult.resultCode);
+            }
+
+            if (opResult.inputConsumed == chunk.length) {
+                // The whole chunk was consumed
+                mBuffered = EMPTY_BYTE_ARRAY;
+                mBufferedOffset = 0;
+                mBufferedLength = 0;
+            } else if (opResult.inputConsumed == 0) {
+                // Nothing was consumed. More input needed.
+                if (inputLength > 0) {
+                    // More input is available, but it wasn't included into the previous chunk
+                    // because the chunk reached its maximum permitted size.
+                    // Shouldn't have happened.
+                    throw new CryptoOperationException("Nothing consumed from max-sized chunk: "
+                            + chunk.length + " bytes");
+                }
+                mBuffered = chunk;
+                mBufferedOffset = 0;
+                mBufferedLength = chunk.length;
+            } else if (opResult.inputConsumed < chunk.length) {
+                // The chunk was consumed only partially -- buffer the rest of the chunk
+                mBuffered = chunk;
+                mBufferedOffset = opResult.inputConsumed;
+                mBufferedLength = chunk.length - opResult.inputConsumed;
+            } else {
+                throw new CryptoOperationException("Consumed more than provided: "
+                        + opResult.inputConsumed + ", provided: " + chunk.length);
+            }
+
+            if ((opResult.output != null) && (opResult.output.length > 0)) {
+                if (inputLength > 0) {
+                    // More output might be produced in this loop -- buffer the current output
+                    if (bufferedOutput == null) {
+                        bufferedOutput = new ByteArrayOutputStream();
+                        try {
+                            bufferedOutput.write(opResult.output);
+                        } catch (IOException e) {
+                            throw new CryptoOperationException("Failed to buffer output", e);
+                        }
+                    }
+                } else {
+                    // No more output will be produced in this loop
+                    if (bufferedOutput == null) {
+                        // No previously buffered output
+                        return opResult.output;
+                    } else {
+                        // There was some previously buffered output
+                        try {
+                            bufferedOutput.write(opResult.output);
+                        } catch (IOException e) {
+                            throw new CryptoOperationException("Failed to buffer output", e);
+                        }
+                        return bufferedOutput.toByteArray();
+                    }
+                }
+            }
+        }
+
+        if (bufferedOutput == null) {
+            // No output produced
+            return EMPTY_BYTE_ARRAY;
+        } else {
+            return bufferedOutput.toByteArray();
+        }
+    }
+
+    public byte[] doFinal(byte[] input, int inputOffset, int inputLength)
+            throws KeymasterException {
+        if (inputLength == 0) {
+            // No input provided -- simplify the rest of the code
+            input = EMPTY_BYTE_ARRAY;
+            inputOffset = 0;
+        }
+
+        // Flush all buffered input and provided input into keystore/keymaster.
+        byte[] output = update(input, inputOffset, inputLength);
+        output = concat(output, flush());
+
+        OperationResult opResult = mKeyStoreStream.finish();
+        if (opResult == null) {
+            throw new KeyStoreConnectException();
+        } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+            throw KeymasterUtils.getKeymasterException(opResult.resultCode);
+        }
+
+        return concat(output, opResult.output);
+    }
+
+    /**
+     * Passes all of buffered input into the the KeyStore operation (via the {@code update}
+     * operation) and returns output.
+     */
+    public byte[] flush() throws KeymasterException {
+        if (mBufferedLength <= 0) {
+            return EMPTY_BYTE_ARRAY;
+        }
+
+        byte[] chunk = subarray(mBuffered, mBufferedOffset, mBufferedLength);
+        mBuffered = EMPTY_BYTE_ARRAY;
+        mBufferedLength = 0;
+        mBufferedOffset = 0;
+
+        OperationResult opResult = mKeyStoreStream.update(chunk);
+        if (opResult == null) {
+            throw new KeyStoreConnectException();
+        } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+            throw KeymasterUtils.getKeymasterException(opResult.resultCode);
+        }
+
+        if (opResult.inputConsumed < chunk.length) {
+            throw new CryptoOperationException("Keystore failed to consume all input. Provided: "
+                    + chunk.length + ", consumed: " + opResult.inputConsumed);
+        } else if (opResult.inputConsumed > chunk.length) {
+            throw new CryptoOperationException("Keystore consumed more input than provided"
+                    + " . Provided: " + chunk.length + ", consumed: " + opResult.inputConsumed);
+        }
+
+        return (opResult.output != null) ? opResult.output : EMPTY_BYTE_ARRAY;
+    }
+
+    private static byte[] concat(byte[] arr1, byte[] arr2) {
+        if ((arr1 == null) || (arr1.length == 0)) {
+            return arr2;
+        } else if ((arr2 == null) || (arr2.length == 0)) {
+            return arr1;
+        } else {
+            byte[] result = new byte[arr1.length + arr2.length];
+            System.arraycopy(arr1, 0, result, 0, arr1.length);
+            System.arraycopy(arr2, 0, result, arr1.length, arr2.length);
+            return result;
+        }
+    }
+
+    private static byte[] concat(byte[] arr1, int offset1, int len1, byte[] arr2, int offset2,
+            int len2) {
+        if (len1 == 0) {
+            return subarray(arr2, offset2, len2);
+        } else if (len2 == 0) {
+            return subarray(arr1, offset1, len1);
+        } else {
+            byte[] result = new byte[len1 + len2];
+            System.arraycopy(arr1, offset1, result, 0, len1);
+            System.arraycopy(arr2, offset2, result, len1, len2);
+            return result;
+        }
+    }
+
+    private static byte[] subarray(byte[] arr, int offset, int len) {
+        if (len == 0) {
+            return EMPTY_BYTE_ARRAY;
+        }
+        if ((offset == 0) && (len == arr.length)) {
+            return arr;
+        }
+        byte[] result = new byte[len];
+        System.arraycopy(arr, offset, result, 0, len);
+        return result;
+    }
+
+    /**
+     * Main data stream via a KeyStore streaming operation.
+     *
+     * <p>For example, for an encryption operation, this is the stream through which plaintext is
+     * provided and ciphertext is obtained.
+     */
+    public static class MainDataStream implements Stream {
+
+        private final KeyStore mKeyStore;
+        private final IBinder mOperationToken;
+
+        public MainDataStream(KeyStore keyStore, IBinder operationToken) {
+            mKeyStore = keyStore;
+            mOperationToken = operationToken;
+        }
+
+        @Override
+        public OperationResult update(byte[] input) {
+            return mKeyStore.update(mOperationToken, null, input);
+        }
+
+        @Override
+        public OperationResult finish() {
+            return mKeyStore.finish(mOperationToken, null, null);
+        }
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java
new file mode 100644
index 0000000..6d0e1ae
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreHmacSpi.java
@@ -0,0 +1,183 @@
+/*
+ * 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 android.security;
+
+import android.os.IBinder;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keymaster.OperationResult;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.MacSpi;
+
+/**
+ * {@link MacSpi} which provides HMAC implementations backed by Android KeyStore.
+ *
+ * @hide
+ */
+public abstract class KeyStoreHmacSpi extends MacSpi implements KeyStoreCryptoOperation {
+
+    public static class HmacSHA256 extends KeyStoreHmacSpi {
+        public HmacSHA256() {
+            super(KeyStoreKeyConstraints.Digest.SHA256, 256 / 8);
+        }
+    }
+
+    private final KeyStore mKeyStore = KeyStore.getInstance();
+    private final @KeyStoreKeyConstraints.DigestEnum int mDigest;
+    private final int mMacSizeBytes;
+
+    private String mKeyAliasInKeyStore;
+
+    // The fields below are reset by the engineReset operation.
+    private KeyStoreCryptoOperationChunkedStreamer mChunkedStreamer;
+    private IBinder mOperationToken;
+    private Long mOperationHandle;
+
+    protected KeyStoreHmacSpi(@KeyStoreKeyConstraints.DigestEnum int digest, int macSizeBytes) {
+        mDigest = digest;
+        mMacSizeBytes = macSizeBytes;
+    }
+
+    @Override
+    protected int engineGetMacLength() {
+        return mMacSizeBytes;
+    }
+
+    @Override
+    protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException,
+            InvalidAlgorithmParameterException {
+        if (key == null) {
+            throw new InvalidKeyException("key == null");
+        } else if (!(key instanceof KeyStoreSecretKey)) {
+            throw new InvalidKeyException(
+                    "Only Android KeyStore secret keys supported. Key: " + key);
+        }
+
+        if (params != null) {
+            throw new InvalidAlgorithmParameterException(
+                    "Unsupported algorithm parameters: " + params);
+        }
+
+        mKeyAliasInKeyStore = ((KeyStoreSecretKey) key).getAlias();
+        if (mKeyAliasInKeyStore == null) {
+            throw new InvalidKeyException("Key's KeyStore alias not known");
+        }
+        engineReset();
+        ensureKeystoreOperationInitialized();
+    }
+
+    @Override
+    protected void engineReset() {
+        IBinder operationToken = mOperationToken;
+        if (operationToken != null) {
+            mOperationToken = null;
+            mKeyStore.abort(operationToken);
+        }
+        mOperationHandle = null;
+        mChunkedStreamer = null;
+    }
+
+    private void ensureKeystoreOperationInitialized() {
+        if (mChunkedStreamer != null) {
+            return;
+        }
+        if (mKeyAliasInKeyStore == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+
+        KeymasterArguments keymasterArgs = new KeymasterArguments();
+        keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeyStoreKeyConstraints.Algorithm.HMAC);
+        keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mDigest);
+
+        OperationResult opResult = mKeyStore.begin(mKeyAliasInKeyStore,
+                KeymasterDefs.KM_PURPOSE_SIGN,
+                true,
+                keymasterArgs,
+                null,
+                new KeymasterArguments());
+        if (opResult == null) {
+            throw new KeyStoreConnectException();
+        } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+            throw KeymasterUtils.getCryptoOperationException(opResult.resultCode);
+        }
+        if (opResult.token == null) {
+            throw new CryptoOperationException("Keystore returned null operation token");
+        }
+        mOperationToken = opResult.token;
+        mOperationHandle = opResult.operationHandle;
+        mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer(
+                new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
+                        mKeyStore, mOperationToken));
+    }
+
+    @Override
+    protected void engineUpdate(byte input) {
+        engineUpdate(new byte[] {input}, 0, 1);
+    }
+
+    @Override
+    protected void engineUpdate(byte[] input, int offset, int len) {
+        ensureKeystoreOperationInitialized();
+
+        byte[] output;
+        try {
+            output = mChunkedStreamer.update(input, offset, len);
+        } catch (KeymasterException e) {
+            throw KeymasterUtils.getCryptoOperationException(e);
+        }
+        if ((output != null) && (output.length != 0)) {
+            throw new CryptoOperationException("Update operation unexpectedly produced output");
+        }
+    }
+
+    @Override
+    protected byte[] engineDoFinal() {
+        ensureKeystoreOperationInitialized();
+
+        byte[] result;
+        try {
+            result = mChunkedStreamer.doFinal(null, 0, 0);
+        } catch (KeymasterException e) {
+            throw KeymasterUtils.getCryptoOperationException(e);
+        }
+
+        engineReset();
+        return result;
+    }
+
+    @Override
+    public void finalize() throws Throwable {
+        try {
+            IBinder operationToken = mOperationToken;
+            if (operationToken != null) {
+                mKeyStore.abort(operationToken);
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    @Override
+    public Long getOperationHandle() {
+        return mOperationHandle;
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreKeyCharacteristics.java b/keystore/java/android/security/KeyStoreKeyCharacteristics.java
new file mode 100644
index 0000000..543b5d8
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreKeyCharacteristics.java
@@ -0,0 +1,68 @@
+/*
+ * 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 android.security;
+
+import android.annotation.IntDef;
+import android.security.keymaster.KeymasterDefs;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Characteristics of {@code AndroidKeyStore} keys.
+ *
+ * @hide
+ */
+public abstract class KeyStoreKeyCharacteristics {
+    private KeyStoreKeyCharacteristics() {}
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({Origin.GENERATED_INSIDE_TEE, Origin.GENERATED_OUTSIDE_OF_TEE, Origin.IMPORTED})
+    public @interface OriginEnum {}
+
+    /**
+     * Origin of the key.
+     */
+    public static abstract class Origin {
+        private Origin() {}
+
+        /** Key was generated inside a TEE. */
+        public static final int GENERATED_INSIDE_TEE = 1;
+
+        /** Key was generated outside of a TEE. */
+        public static final int GENERATED_OUTSIDE_OF_TEE = 2;
+
+        /** Key was imported. */
+        public static final int IMPORTED = 0;
+
+        /**
+         * @hide
+         */
+        public static @OriginEnum int fromKeymaster(int origin) {
+            switch (origin) {
+                case KeymasterDefs.KM_ORIGIN_HARDWARE:
+                    return GENERATED_INSIDE_TEE;
+                case KeymasterDefs.KM_ORIGIN_SOFTWARE:
+                    return GENERATED_OUTSIDE_OF_TEE;
+                case KeymasterDefs.KM_ORIGIN_IMPORTED:
+                    return IMPORTED;
+                default:
+                    throw new IllegalArgumentException("Unknown origin: " + origin);
+            }
+        }
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java
new file mode 100644
index 0000000..75034d1
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreKeyConstraints.java
@@ -0,0 +1,618 @@
+/*
+ * 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 android.security;
+
+import android.annotation.IntDef;
+import android.security.keymaster.KeymasterDefs;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * Constraints for {@code AndroidKeyStore} keys.
+ *
+ * @hide
+ */
+public abstract class KeyStoreKeyConstraints {
+    private KeyStoreKeyConstraints() {}
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag=true, value={Purpose.ENCRYPT, Purpose.DECRYPT, Purpose.SIGN, Purpose.VERIFY})
+    public @interface PurposeEnum {}
+
+    /**
+     * Purpose of key.
+     */
+    public static abstract class Purpose {
+        private Purpose() {}
+
+        /**
+         * Purpose: encryption.
+         */
+        public static final int ENCRYPT = 1 << 0;
+
+        /**
+         * Purpose: decryption.
+         */
+        public static final int DECRYPT = 1 << 1;
+
+        /**
+         * Purpose: signing.
+         */
+        public static final int SIGN = 1 << 2;
+
+        /**
+         * Purpose: signature verification.
+         */
+        public static final int VERIFY = 1 << 3;
+
+        /**
+         * Number of flags defined above. Needs to be kept in sync with the flags above.
+         */
+        private static final int VALUE_COUNT = 4;
+
+        /**
+         * @hide
+         */
+        public static int toKeymaster(@PurposeEnum int purpose) {
+            switch (purpose) {
+                case ENCRYPT:
+                    return KeymasterDefs.KM_PURPOSE_ENCRYPT;
+                case DECRYPT:
+                    return KeymasterDefs.KM_PURPOSE_DECRYPT;
+                case SIGN:
+                    return KeymasterDefs.KM_PURPOSE_SIGN;
+                case VERIFY:
+                    return KeymasterDefs.KM_PURPOSE_VERIFY;
+                default:
+                    throw new IllegalArgumentException("Unknown purpose: " + purpose);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @PurposeEnum int fromKeymaster(int purpose) {
+            switch (purpose) {
+                case KeymasterDefs.KM_PURPOSE_ENCRYPT:
+                    return ENCRYPT;
+                case KeymasterDefs.KM_PURPOSE_DECRYPT:
+                    return DECRYPT;
+                case KeymasterDefs.KM_PURPOSE_SIGN:
+                    return SIGN;
+                case KeymasterDefs.KM_PURPOSE_VERIFY:
+                    return VERIFY;
+                default:
+                    throw new IllegalArgumentException("Unknown purpose: " + purpose);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static int[] allToKeymaster(int purposes) {
+            int[] result = new int[VALUE_COUNT];
+            int resultCount = 0;
+            int purpose = 1;
+            for (int i = 0; i < 32; i++) {
+                if ((purposes & 1) != 0) {
+                    result[resultCount] = toKeymaster(purpose);
+                    resultCount++;
+                }
+                purposes >>>= 1;
+                purpose <<= 1;
+                if (purposes == 0) {
+                    break;
+                }
+            }
+            return Arrays.copyOf(result, resultCount);
+        }
+
+        /**
+         * @hide
+         */
+        public static @PurposeEnum int allFromKeymaster(Collection<Integer> purposes) {
+            @PurposeEnum int result = 0;
+            for (int keymasterPurpose : purposes) {
+                result |= fromKeymaster(keymasterPurpose);
+            }
+            return result;
+        }
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({Algorithm.AES, Algorithm.HMAC})
+    public @interface AlgorithmEnum {}
+
+    /**
+     * Key algorithm.
+     */
+    public static abstract class Algorithm {
+        private Algorithm() {}
+
+        /**
+         * Key algorithm: AES.
+         */
+        public static final int AES = 0;
+
+        /**
+         * Key algorithm: HMAC.
+         */
+        public static final int HMAC = 1;
+
+        /**
+         * @hide
+         */
+        public static int toKeymaster(@AlgorithmEnum int algorithm) {
+            switch (algorithm) {
+                case AES:
+                    return KeymasterDefs.KM_ALGORITHM_AES;
+                case HMAC:
+                    return KeymasterDefs.KM_ALGORITHM_HMAC;
+                default:
+                    throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @AlgorithmEnum int fromKeymaster(int algorithm) {
+            switch (algorithm) {
+                case KeymasterDefs.KM_ALGORITHM_AES:
+                    return AES;
+                case KeymasterDefs.KM_ALGORITHM_HMAC:
+                    return HMAC;
+                default:
+                    throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static String toString(@AlgorithmEnum int algorithm) {
+            switch (algorithm) {
+                case AES:
+                    return "AES";
+                case HMAC:
+                    return "HMAC";
+                default:
+                    throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @AlgorithmEnum int fromJCASecretKeyAlgorithm(String algorithm) {
+            if (algorithm == null) {
+                throw new NullPointerException("algorithm == null");
+            } else  if ("AES".equalsIgnoreCase(algorithm)) {
+                return AES;
+            } else if (algorithm.toLowerCase(Locale.US).startsWith("hmac")) {
+                return HMAC;
+            } else {
+                throw new IllegalArgumentException(
+                        "Unsupported secret key algorithm: " + algorithm);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static String toJCASecretKeyAlgorithm(@AlgorithmEnum int algorithm,
+                @DigestEnum Integer digest) {
+            switch (algorithm) {
+                case AES:
+                    return "AES";
+                case HMAC:
+                    if (digest == null) {
+                        throw new IllegalArgumentException("HMAC digest not specified");
+                    }
+                    switch (digest) {
+                        case Digest.SHA256:
+                            return "HmacSHA256";
+                        default:
+                            throw new IllegalArgumentException(
+                                    "Unsupported HMAC digest: " + digest);
+                    }
+                default:
+                    throw new IllegalArgumentException("Unsupported key algorithm: " + algorithm);
+            }
+        }
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({Padding.NONE, Padding.ZERO, Padding.PKCS7})
+    public @interface PaddingEnum {}
+
+    /**
+     * Padding for signing and encryption.
+     */
+    public static abstract class Padding {
+        private Padding() {}
+
+        /**
+         * No padding.
+         */
+        public static final int NONE = 0;
+
+        /**
+         * Pad with zeros.
+         */
+        public static final int ZERO = 1;
+
+        /**
+         * PKCS#7 padding.
+         */
+        public static final int PKCS7 = 2;
+
+        /**
+         * @hide
+         */
+        public static int toKeymaster(int padding) {
+            switch (padding) {
+                case NONE:
+                    return KeymasterDefs.KM_PAD_NONE;
+                case ZERO:
+                    return KeymasterDefs.KM_PAD_ZERO;
+                case PKCS7:
+                    return KeymasterDefs.KM_PAD_PKCS7;
+                default:
+                    throw new IllegalArgumentException("Unknown padding: " + padding);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @PaddingEnum int fromKeymaster(int padding) {
+            switch (padding) {
+                case KeymasterDefs.KM_PAD_NONE:
+                    return NONE;
+                case KeymasterDefs.KM_PAD_ZERO:
+                    return ZERO;
+                case KeymasterDefs.KM_PAD_PKCS7:
+                    return PKCS7;
+                default:
+                    throw new IllegalArgumentException("Unknown padding: " + padding);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static String toString(@PaddingEnum int padding) {
+            switch (padding) {
+                case NONE:
+                    return "NONE";
+                case ZERO:
+                    return "ZERO";
+                case PKCS7:
+                    return "PKCS#7";
+                default:
+                    throw new IllegalArgumentException("Unknown padding: " + padding);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @PaddingEnum int fromJCAPadding(String padding) {
+            String paddingLower = padding.toLowerCase(Locale.US);
+            if ("nopadding".equals(paddingLower)) {
+                return NONE;
+            } else if ("pkcs7padding".equals(paddingLower)) {
+                return PKCS7;
+            } else {
+                throw new IllegalArgumentException("Unknown padding: " + padding);
+            }
+        }
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({Digest.NONE, Digest.SHA256})
+    public @interface DigestEnum {}
+
+    /**
+     * Digests that can be used with a key when signing or generating Message Authentication
+     * Codes (MACs).
+     */
+    public static abstract class Digest {
+        private Digest() {}
+
+        /**
+         * No digest: sign/authenticate the raw message.
+         */
+        public static final int NONE = 0;
+
+        /**
+         * SHA-256 digest.
+         */
+        public static final int SHA256 = 1;
+
+        /**
+         * @hide
+         */
+        public static String toString(@DigestEnum int digest) {
+            switch (digest) {
+                case NONE:
+                    return "NONE";
+                case SHA256:
+                    return "SHA256";
+                default:
+                    throw new IllegalArgumentException("Unknown digest: " + digest);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static int toKeymaster(@DigestEnum int digest) {
+            switch (digest) {
+                case NONE:
+                    return KeymasterDefs.KM_DIGEST_NONE;
+                case SHA256:
+                    return KeymasterDefs.KM_DIGEST_SHA_2_256;
+                default:
+                    throw new IllegalArgumentException("Unknown digest: " + digest);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @DigestEnum int fromKeymaster(int digest) {
+            switch (digest) {
+                case KeymasterDefs.KM_DIGEST_NONE:
+                    return NONE;
+                case KeymasterDefs.KM_DIGEST_SHA_2_256:
+                    return SHA256;
+                default:
+                    throw new IllegalArgumentException("Unknown digest: " + digest);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @DigestEnum Integer fromJCASecretKeyAlgorithm(String algorithm) {
+            String algorithmLower = algorithm.toLowerCase(Locale.US);
+            if (algorithmLower.startsWith("hmac")) {
+                if ("hmacsha256".equals(algorithmLower)) {
+                    return SHA256;
+                } else {
+                    throw new IllegalArgumentException("Unsupported digest: "
+                            + algorithmLower.substring("hmac".length()));
+                }
+            } else {
+                return null;
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static String toJCASignatureAlgorithmDigest(@DigestEnum int digest) {
+            switch (digest) {
+                case NONE:
+                    return "NONE";
+                case SHA256:
+                    return "SHA256";
+                default:
+                    throw new IllegalArgumentException("Unknown digest: " + digest);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static Integer getOutputSizeBytes(@DigestEnum int digest) {
+            switch (digest) {
+                case NONE:
+                    return null;
+                case SHA256:
+                    return 256 / 8;
+                default:
+                    throw new IllegalArgumentException("Unknown digest: " + digest);
+            }
+        }
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({BlockMode.ECB, BlockMode.CBC, BlockMode.CTR})
+    public @interface BlockModeEnum {}
+
+    /**
+     * Block modes that can be used when encrypting/decrypting using a key.
+     */
+    public static abstract class BlockMode {
+        private BlockMode() {}
+
+        /** Electronic Codebook (ECB) block mode. */
+        public static final int ECB = 0;
+
+        /** Cipher Block Chaining (CBC) block mode. */
+        public static final int CBC = 1;
+
+        /** Counter (CTR) block mode. */
+        public static final int CTR = 2;
+
+        /**
+         * @hide
+         */
+        public static int toKeymaster(@BlockModeEnum int mode) {
+            switch (mode) {
+                case ECB:
+                    return KeymasterDefs.KM_MODE_ECB;
+                case CBC:
+                    return KeymasterDefs.KM_MODE_CBC;
+                case CTR:
+                    return KeymasterDefs.KM_MODE_CTR;
+                default:
+                    throw new IllegalArgumentException("Unknown block mode: " + mode);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @BlockModeEnum int fromKeymaster(int mode) {
+            switch (mode) {
+                case KeymasterDefs.KM_MODE_ECB:
+                    return ECB;
+                case KeymasterDefs.KM_MODE_CBC:
+                    return CBC;
+                case KeymasterDefs.KM_MODE_CTR:
+                    return CTR;
+                default:
+                    throw new IllegalArgumentException("Unknown block mode: " + mode);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static String toString(@BlockModeEnum int mode) {
+            switch (mode) {
+                case ECB:
+                    return "ECB";
+                case CBC:
+                    return "CBC";
+                case CTR:
+                    return "CTR";
+                default:
+                    throw new IllegalArgumentException("Unknown block mode: " + mode);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @BlockModeEnum int fromJCAMode(String mode) {
+            String modeLower = mode.toLowerCase(Locale.US);
+            if ("ecb".equals(modeLower)) {
+                return ECB;
+            } else if ("cbc".equals(modeLower)) {
+                return CBC;
+            } else if ("ctr".equals(modeLower)) {
+                return CTR;
+            } else {
+                throw new IllegalArgumentException("Unknown block mode: " + mode);
+            }
+        }
+    }
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({UserAuthenticator.LOCK_SCREEN})
+    public @interface UserAuthenticatorEnum {}
+
+    /**
+     * User authenticators which can be used to restrict/protect access to keys.
+     */
+    public static abstract class UserAuthenticator {
+        private UserAuthenticator() {}
+
+        /** Lock screen. */
+        public static final int LOCK_SCREEN = 1;
+
+        /** Fingerprint reader/sensor. */
+        public static final int FINGERPRINT_READER = 1 << 1;
+
+        /**
+         * @hide
+         */
+        public static int toKeymaster(@UserAuthenticatorEnum int userAuthenticator) {
+            switch (userAuthenticator) {
+                case LOCK_SCREEN:
+                    return LOCK_SCREEN;
+                case FINGERPRINT_READER:
+                    return FINGERPRINT_READER;
+                default:
+                    throw new IllegalArgumentException(
+                            "Unknown user authenticator: " + userAuthenticator);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static @UserAuthenticatorEnum int fromKeymaster(int userAuthenticator) {
+            switch (userAuthenticator) {
+                case LOCK_SCREEN:
+                    return LOCK_SCREEN;
+                case FINGERPRINT_READER:
+                    return FINGERPRINT_READER;
+                default:
+                    throw new IllegalArgumentException(
+                            "Unknown user authenticator: " + userAuthenticator);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public static int allToKeymaster(Set<Integer> userAuthenticators) {
+            int result = 0;
+            for (@UserAuthenticatorEnum int userAuthenticator : userAuthenticators) {
+                result |= toKeymaster(userAuthenticator);
+            }
+            return result;
+        }
+
+        /**
+         * @hide
+         */
+        public static Set<Integer> allFromKeymaster(int userAuthenticators) {
+            int userAuthenticator = 1;
+            Set<Integer> result = null;
+            while (userAuthenticators != 0) {
+                if ((userAuthenticators & 1) != 0) {
+                    if (result == null) {
+                        result = new HashSet<Integer>();
+                    }
+                    result.add(fromKeymaster(userAuthenticator));
+                }
+                userAuthenticators >>>= 1;
+                userAuthenticator <<= 1;
+            }
+            return (result != null) ? result : Collections.<Integer>emptySet();
+        }
+
+        /**
+         * @hide
+         */
+        public static String toString(@UserAuthenticatorEnum int userAuthenticator) {
+            switch (userAuthenticator) {
+                case LOCK_SCREEN:
+                    return "LOCK_SCREEN";
+                case FINGERPRINT_READER:
+                    return "FINGERPRINT_READER";
+                default:
+                    throw new IllegalArgumentException(
+                            "Unknown user authenticator: " + userAuthenticator);
+            }
+        }
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
new file mode 100644
index 0000000..8b0a3e9
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -0,0 +1,214 @@
+/*
+ * 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 android.security;
+
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.Date;
+
+import javax.crypto.KeyGeneratorSpi;
+import javax.crypto.SecretKey;
+
+/**
+ * {@link KeyGeneratorSpi} backed by Android KeyStore.
+ *
+ * @hide
+ */
+public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
+
+    public static class AES extends KeyStoreKeyGeneratorSpi {
+        public AES() {
+            super(KeyStoreKeyConstraints.Algorithm.AES, 128);
+        }
+    }
+
+    public static class HmacSHA256 extends KeyStoreKeyGeneratorSpi {
+        public HmacSHA256() {
+            super(KeyStoreKeyConstraints.Algorithm.HMAC,
+                    KeyStoreKeyConstraints.Digest.SHA256,
+                    KeyStoreKeyConstraints.Digest.getOutputSizeBytes(
+                            KeyStoreKeyConstraints.Digest.SHA256) * 8);
+        }
+    }
+
+    private final KeyStore mKeyStore = KeyStore.getInstance();
+    private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm;
+    private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+    private final int mDefaultKeySizeBits;
+
+    private KeyGeneratorSpec mSpec;
+    private SecureRandom mRng;
+
+    protected KeyStoreKeyGeneratorSpi(
+            @KeyStoreKeyConstraints.AlgorithmEnum int algorithm,
+            int defaultKeySizeBits) {
+        this(algorithm, null, defaultKeySizeBits);
+    }
+
+    protected KeyStoreKeyGeneratorSpi(
+            @KeyStoreKeyConstraints.AlgorithmEnum int algorithm,
+            @KeyStoreKeyConstraints.DigestEnum Integer digest,
+            int defaultKeySizeBits) {
+        mAlgorithm = algorithm;
+        mDigest = digest;
+        mDefaultKeySizeBits = defaultKeySizeBits;
+    }
+
+    @Override
+    protected SecretKey engineGenerateKey() {
+        KeyGeneratorSpec spec = mSpec;
+        if (spec == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+
+        if ((spec.isEncryptionRequired())
+                && (mKeyStore.state() != KeyStore.State.UNLOCKED)) {
+            throw new IllegalStateException(
+                    "Android KeyStore must be in initialized and unlocked state if encryption is"
+                    + " required");
+        }
+
+        KeymasterArguments args = new KeymasterArguments();
+        args.addInt(KeymasterDefs.KM_TAG_ALGORITHM,
+                KeyStoreKeyConstraints.Algorithm.toKeymaster(mAlgorithm));
+        if (mDigest != null) {
+            args.addInt(KeymasterDefs.KM_TAG_DIGEST,
+                    KeyStoreKeyConstraints.Digest.toKeymaster(mDigest));
+            Integer digestOutputSizeBytes =
+                    KeyStoreKeyConstraints.Digest.getOutputSizeBytes(mDigest);
+            if (digestOutputSizeBytes != null) {
+                // TODO: Remove MAC length constraint once Keymaster API no longer requires it.
+                // TODO: Switch to bits instead of bytes, once this is fixed in Keymaster
+                args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes);
+            }
+        }
+        if (mAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) {
+            if (mDigest == null) {
+                throw new IllegalStateException("Digest algorithm must be specified for key"
+                        + " algorithm " + KeyStoreKeyConstraints.Algorithm.toString(mAlgorithm));
+            }
+        }
+        int keySizeBits = (spec.getKeySize() != null) ? spec.getKeySize() : mDefaultKeySizeBits;
+        args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits);
+        @KeyStoreKeyConstraints.PurposeEnum int purposes = (spec.getPurposes() != null)
+                ? spec.getPurposes()
+                : (KeyStoreKeyConstraints.Purpose.ENCRYPT
+                        | KeyStoreKeyConstraints.Purpose.DECRYPT
+                        | KeyStoreKeyConstraints.Purpose.SIGN
+                        | KeyStoreKeyConstraints.Purpose.VERIFY);
+        for (int keymasterPurpose :
+            KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
+            args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
+        }
+        if (spec.getBlockMode() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE,
+                    KeyStoreKeyConstraints.BlockMode.toKeymaster(spec.getBlockMode()));
+        }
+        if (spec.getPadding() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_PADDING,
+                    KeyStoreKeyConstraints.Padding.toKeymaster(spec.getPadding()));
+        }
+        if (spec.getMaxUsesPerBoot() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT, spec.getMaxUsesPerBoot());
+        }
+        if (spec.getMinSecondsBetweenOperations() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS,
+                    spec.getMinSecondsBetweenOperations());
+        }
+        if (spec.getUserAuthenticators().isEmpty()) {
+            args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
+        } else {
+            args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
+                    KeyStoreKeyConstraints.UserAuthenticator.allToKeymaster(
+                            spec.getUserAuthenticators()));
+        }
+        if (spec.isInvalidatedOnNewFingerprintEnrolled()) {
+            // TODO: Add the invalidate on fingerprint enrolled constraint once Keymaster supports
+            // that.
+        }
+        if (spec.getUserAuthenticationValidityDurationSeconds() != null) {
+            args.addInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
+                    spec.getUserAuthenticationValidityDurationSeconds());
+        }
+        args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
+                (spec.getKeyValidityStart() != null)
+                ? spec.getKeyValidityStart() : new Date(0));
+        args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
+                (spec.getKeyValidityForOriginationEnd() != null)
+                ? spec.getKeyValidityForOriginationEnd() : new Date(Long.MAX_VALUE));
+        args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
+                (spec.getKeyValidityForConsumptionEnd() != null)
+                ? spec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE));
+
+        if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
+            || ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) {
+            // Permit caller-specified IV. This is needed due to the Cipher abstraction.
+            args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
+        }
+
+        byte[] additionalEntropy = null;
+        SecureRandom rng = mRng;
+        if (rng != null) {
+            additionalEntropy = new byte[(keySizeBits + 7) / 8];
+            rng.nextBytes(additionalEntropy);
+        }
+
+        int flags = spec.getFlags();
+        String keyAliasInKeystore = Credentials.USER_SECRET_KEY + spec.getKeystoreAlias();
+        int errorCode = mKeyStore.generateKey(
+                keyAliasInKeystore, args, additionalEntropy, flags, new KeyCharacteristics());
+        if (errorCode != KeyStore.NO_ERROR) {
+            throw KeymasterUtils.getCryptoOperationException(errorCode);
+        }
+        String keyAlgorithmJCA =
+                KeyStoreKeyConstraints.Algorithm.toJCASecretKeyAlgorithm(mAlgorithm, mDigest);
+        return new KeyStoreSecretKey(keyAliasInKeystore, keyAlgorithmJCA);
+    }
+
+    @Override
+    protected void engineInit(SecureRandom random) {
+        throw new UnsupportedOperationException("Cannot initialize without an "
+                + KeyGeneratorSpec.class.getName() + " parameter");
+    }
+
+    @Override
+    protected void engineInit(AlgorithmParameterSpec params, SecureRandom random)
+            throws InvalidAlgorithmParameterException {
+        if ((params == null) || (!(params instanceof KeyGeneratorSpec))) {
+            throw new InvalidAlgorithmParameterException("Cannot initialize without an "
+                    + KeyGeneratorSpec.class.getName() + " parameter");
+        }
+        KeyGeneratorSpec spec = (KeyGeneratorSpec) params;
+        if (spec.getKeystoreAlias() == null) {
+            throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
+        }
+
+        mSpec = spec;
+        mRng = random;
+    }
+
+    @Override
+    protected void engineInit(int keySize, SecureRandom random) {
+        throw new UnsupportedOperationException("Cannot initialize without a "
+                + KeyGeneratorSpec.class.getName() + " parameter");
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java
new file mode 100644
index 0000000..e5e5acc
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreKeySpec.java
@@ -0,0 +1,240 @@
+/*
+ * 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 android.security;
+
+import java.security.spec.KeySpec;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Information about a key from the <a href="{@docRoot}training/articles/keystore.html">Android
+ * KeyStore</a>.
+ *
+ * @hide
+ */
+public class KeyStoreKeySpec implements KeySpec {
+    private final String mKeystoreAlias;
+    private final int mKeySize;
+    private final @KeyStoreKeyCharacteristics.OriginEnum int mOrigin;
+    private final Date mKeyValidityStart;
+    private final Date mKeyValidityForOriginationEnd;
+    private final Date mKeyValidityForConsumptionEnd;
+    private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
+    private final @KeyStoreKeyConstraints.AlgorithmEnum int mAlgorithm;
+    private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+    private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+    private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+    private final Integer mMinSecondsBetweenOperations;
+    private final Integer mMaxUsesPerBoot;
+    private final Set<Integer> mUserAuthenticators;
+    private final Set<Integer> mTeeBackedUserAuthenticators;
+    private final Integer mUserAuthenticationValidityDurationSeconds;
+    private final boolean mInvalidatedOnNewFingerprintEnrolled;
+
+
+    /**
+     * @hide
+     */
+    KeyStoreKeySpec(String keystoreKeyAlias,
+            @KeyStoreKeyCharacteristics.OriginEnum int origin,
+            int keySize, Date keyValidityStart, Date keyValidityForOriginationEnd,
+            Date keyValidityForConsumptionEnd,
+            @KeyStoreKeyConstraints.PurposeEnum int purposes,
+            @KeyStoreKeyConstraints.AlgorithmEnum int algorithm,
+            @KeyStoreKeyConstraints.PaddingEnum Integer padding,
+            @KeyStoreKeyConstraints.DigestEnum Integer digest,
+            @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
+            Integer minSecondsBetweenOperations,
+            Integer maxUsesPerBoot,
+            Set<Integer> userAuthenticators,
+            Set<Integer> teeBackedUserAuthenticators,
+            Integer userAuthenticationValidityDurationSeconds,
+            boolean invalidatedOnNewFingerprintEnrolled) {
+        mKeystoreAlias = keystoreKeyAlias;
+        mOrigin = origin;
+        mKeySize = keySize;
+        mKeyValidityStart = keyValidityStart;
+        mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
+        mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
+        mPurposes = purposes;
+        mAlgorithm = algorithm;
+        mPadding = padding;
+        mDigest = digest;
+        mBlockMode = blockMode;
+        mMinSecondsBetweenOperations = minSecondsBetweenOperations;
+        mMaxUsesPerBoot = maxUsesPerBoot;
+        mUserAuthenticators = (userAuthenticators != null)
+                ? new HashSet<Integer>(userAuthenticators)
+                : Collections.<Integer>emptySet();
+        mTeeBackedUserAuthenticators = (teeBackedUserAuthenticators != null)
+                ? new HashSet<Integer>(teeBackedUserAuthenticators)
+                : Collections.<Integer>emptySet();
+        mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+        mInvalidatedOnNewFingerprintEnrolled = invalidatedOnNewFingerprintEnrolled;
+    }
+
+    /**
+     * Gets the entry alias under which the key is stored in the {@code AndroidKeyStore}.
+     */
+    public String getKeystoreAlias() {
+        return mKeystoreAlias;
+    }
+
+    /**
+     * Gets the origin of the key.
+     */
+    public @KeyStoreKeyCharacteristics.OriginEnum int getOrigin() {
+        return mOrigin;
+    }
+
+    /**
+     * Gets the key's size in bits.
+     */
+    public int getKeySize() {
+        return mKeySize;
+    }
+
+    /**
+     * Gets the time instant before which the key is not yet valid.
+     *
+     * @return instant or {@code null} if not restricted.
+     */
+    public Date getKeyValidityStart() {
+        return mKeyValidityStart;
+    }
+
+    /**
+     * Gets the time instant after which the key is no long valid for decryption and verification.
+     *
+     * @return instant or {@code null} if not restricted.
+     */
+    public Date getKeyValidityForConsumptionEnd() {
+        return mKeyValidityForConsumptionEnd;
+    }
+
+    /**
+     * Gets the time instant after which the key is no long valid for encryption and signing.
+     *
+     * @return instant or {@code null} if not restricted.
+     */
+    public Date getKeyValidityForOriginationEnd() {
+        return mKeyValidityForOriginationEnd;
+    }
+
+    /**
+     * Gets the set of purposes for which the key can be used.
+     */
+    public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() {
+        return mPurposes;
+    }
+
+    /**
+     * Gets the algorithm of the key.
+     */
+    public @KeyStoreKeyConstraints.AlgorithmEnum int getAlgorithm() {
+        return mAlgorithm;
+    }
+
+    /**
+     * Gets the only block mode with which the key can be used.
+     *
+     * @return block mode or {@code null} if the block mode is not restricted.
+     */
+    public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
+        return mBlockMode;
+    }
+
+    /**
+     * Gets the only padding mode with which the key can be used.
+     *
+     * @return padding mode or {@code null} if the padding mode is not restricted.
+     */
+    public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
+        return mPadding;
+    }
+
+    /**
+     * Gets the only digest algorithm with which the key can be used.
+     *
+     * @return digest algorithm or {@code null} if the digest algorithm is not restricted.
+     */
+    public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() {
+        return mDigest;
+    }
+
+    /**
+     * Gets the minimum number of seconds that must expire since the most recent use of the key
+     * before it can be used again.
+     *
+     * @return number of seconds or {@code null} if there is no restriction on how frequently a key
+     *         can be used.
+     */
+    public Integer getMinSecondsBetweenOperations() {
+        return mMinSecondsBetweenOperations;
+    }
+
+    /**
+     * Gets the number of times the key can be used without rebooting the device.
+     *
+     * @return maximum number of times or {@code null} if there is no restriction.
+     */
+    public Integer getMaxUsesPerBoot() {
+        return mMaxUsesPerBoot;
+    }
+
+    /**
+     * Gets the user authenticators which protect access to the key. The key can only be used iff
+     * the user has authenticated to at least one of these user authenticators.
+     *
+     * @return user authenticators or empty set if the key can be used without user authentication.
+     */
+    public Set<Integer> getUserAuthenticators() {
+        return new HashSet<Integer>(mUserAuthenticators);
+    }
+
+    /**
+     * Gets the TEE-backed user authenticators which protect access to the key. This is a subset of
+     * the user authentications returned by {@link #getUserAuthenticators()}.
+     */
+    public Set<Integer> getTeeBackedUserAuthenticators() {
+        return new HashSet<Integer>(mTeeBackedUserAuthenticators);
+    }
+
+    /**
+     * Gets the duration of time (seconds) for which the key can be used after the user
+     * successfully authenticates to one of the associated user authenticators.
+     *
+     * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+     *         is required for every use of the key.
+     */
+    public Integer getUserAuthenticationValidityDurationSeconds() {
+        return mUserAuthenticationValidityDurationSeconds;
+    }
+
+    /**
+     * Returns {@code true} if this key will be permanently invalidated once a new fingerprint is
+     * enrolled. This constraint only has effect if fingerprint reader is one of the user
+     * authenticators protecting access to this key.
+     *
+     * @see #getUserAuthenticators()
+     */
+    public boolean isInvalidatedOnNewFingerprintEnrolled() {
+        return mInvalidatedOnNewFingerprintEnrolled;
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java
index 2eeb6ad..88bd6b4 100644
--- a/keystore/java/android/security/KeyStoreParameter.java
+++ b/keystore/java/android/security/KeyStoreParameter.java
@@ -20,6 +20,10 @@
 
 import java.security.KeyPairGenerator;
 import java.security.KeyStore.ProtectionParameter;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * This provides the optional parameters that can be specified for
@@ -43,9 +47,56 @@
  */
 public final class KeyStoreParameter implements ProtectionParameter {
     private int mFlags;
+    private final Date mKeyValidityStart;
+    private final Date mKeyValidityForOriginationEnd;
+    private final Date mKeyValidityForConsumptionEnd;
+    private final @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+    private final @KeyStoreKeyConstraints.AlgorithmEnum Integer mAlgorithm;
+    private final @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+    private final @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+    private final @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+    private final Integer mMinSecondsBetweenOperations;
+    private final Integer mMaxUsesPerBoot;
+    private final Set<Integer> mUserAuthenticators;
+    private final Integer mUserAuthenticationValidityDurationSeconds;
+    private final boolean mInvalidatedOnNewFingerprintEnrolled;
 
-    private KeyStoreParameter(int flags) {
+    private KeyStoreParameter(int flags,
+            Date keyValidityStart,
+            Date keyValidityForOriginationEnd,
+            Date keyValidityForConsumptionEnd,
+            @KeyStoreKeyConstraints.PurposeEnum Integer purposes,
+            @KeyStoreKeyConstraints.AlgorithmEnum Integer algorithm,
+            @KeyStoreKeyConstraints.PaddingEnum Integer padding,
+            @KeyStoreKeyConstraints.DigestEnum Integer digest,
+            @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode,
+            Integer minSecondsBetweenOperations,
+            Integer maxUsesPerBoot,
+            Set<Integer> userAuthenticators,
+            Integer userAuthenticationValidityDurationSeconds,
+            boolean invalidatedOnNewFingerprintEnrolled) {
+        if ((userAuthenticationValidityDurationSeconds != null)
+                && (userAuthenticationValidityDurationSeconds < 0)) {
+            throw new IllegalArgumentException(
+                    "userAuthenticationValidityDurationSeconds must not be negative");
+        }
+
         mFlags = flags;
+        mKeyValidityStart = keyValidityStart;
+        mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
+        mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
+        mPurposes = purposes;
+        mAlgorithm = algorithm;
+        mPadding = padding;
+        mDigest = digest;
+        mBlockMode = blockMode;
+        mMinSecondsBetweenOperations = minSecondsBetweenOperations;
+        mMaxUsesPerBoot = maxUsesPerBoot;
+        mUserAuthenticators = (userAuthenticators != null)
+                ? new HashSet<Integer>(userAuthenticators)
+                : Collections.<Integer>emptySet();
+        mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+        mInvalidatedOnNewFingerprintEnrolled = invalidatedOnNewFingerprintEnrolled;
     }
 
     /**
@@ -64,6 +115,154 @@
     }
 
     /**
+     * Gets the time instant before which the key is not yet valid.
+     *
+     * @return instant or {@code null} if not restricted.
+     * @hide
+     */
+    public Date getKeyValidityStart() {
+        return mKeyValidityStart;
+    }
+
+    /**
+     * Gets the time instant after which the key is no long valid for decryption and verification.
+     *
+     * @return instant or {@code null} if not restricted.
+     *
+     * @hide
+     */
+    public Date getKeyValidityForConsumptionEnd() {
+        return mKeyValidityForConsumptionEnd;
+    }
+
+    /**
+     * Gets the time instant after which the key is no long valid for encryption and signing.
+     *
+     * @return instant or {@code null} if not restricted.
+     *
+     * @hide
+     */
+    public Date getKeyValidityForOriginationEnd() {
+        return mKeyValidityForOriginationEnd;
+    }
+
+    /**
+     * Gets the set of purposes for which the key can be used to the provided set of purposes.
+     *
+     * @return set of purposes or {@code null} if the key can be used for any purpose.
+     *
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.PurposeEnum Integer getPurposes() {
+        return mPurposes;
+    }
+
+    /**
+     * Gets the algorithm to which the key is restricted.
+     *
+     * @return algorithm or {@code null} if it's not restricted.
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.AlgorithmEnum Integer getAlgorithm() {
+        return mAlgorithm;
+    }
+
+    /**
+     * Gets the padding scheme to which the key is restricted.
+     *
+     * @return padding scheme or {@code null} if the padding scheme is not restricted.
+     *
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.PaddingEnum Integer getPadding() {
+        return mPadding;
+    }
+
+    /**
+     * Gets the digest to which the key is restricted when generating signatures or Message
+     * Authentication Codes (MACs).
+     *
+     * @return digest or {@code null} if the digest is not restricted.
+     *
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.DigestEnum Integer getDigest() {
+        return mDigest;
+    }
+
+    /**
+     * Gets the block mode to which the key is restricted when used for encryption or decryption.
+     *
+     * @return block more or {@code null} if block mode is not restricted.
+     *
+     * @hide
+     */
+    public @KeyStoreKeyConstraints.BlockModeEnum Integer getBlockMode() {
+        return mBlockMode;
+    }
+
+    /**
+     * Gets the minimum number of seconds that must expire since the most recent use of the key
+     * before it can be used again.
+     *
+     * @return number of seconds or {@code null} if there is no restriction on how frequently a key
+     *         can be used.
+     *
+     * @hide
+     */
+    public Integer getMinSecondsBetweenOperations() {
+        return mMinSecondsBetweenOperations;
+    }
+
+    /**
+     * Gets the number of times the key can be used without rebooting the device.
+     *
+     * @return maximum number of times or {@code null} if there is no restriction.
+     * @hide
+     */
+    public Integer getMaxUsesPerBoot() {
+        return mMaxUsesPerBoot;
+    }
+
+    /**
+     * Gets the user authenticators which protect access to this key. The key can only be used iff
+     * the user has authenticated to at least one of these user authenticators.
+     *
+     * @return user authenticators or empty set if the key can be used without user authentication.
+     *
+     * @hide
+     */
+    public Set<Integer> getUserAuthenticators() {
+        return new HashSet<Integer>(mUserAuthenticators);
+    }
+
+    /**
+     * Gets the duration of time (seconds) for which this key can be used after the user
+     * successfully authenticates to one of the associated user authenticators.
+     *
+     * @return duration in seconds or {@code null} if not restricted. {@code 0} means authentication
+     *         is required for every use of the key.
+     *
+     * @hide
+     */
+    public Integer getUserAuthenticationValidityDurationSeconds() {
+        return mUserAuthenticationValidityDurationSeconds;
+    }
+
+    /**
+     * Returns {@code true} if this key must be permanently invalidated once a new fingerprint is
+     * enrolled. This constraint only has effect if fingerprint reader is one of the user
+     * authenticators protecting access to this key.
+     *
+     * @see #getUserAuthenticators()
+     *
+     * @hide
+     */
+    public boolean isInvalidatedOnNewFingerprintEnrolled() {
+        return mInvalidatedOnNewFingerprintEnrolled;
+    }
+
+    /**
      * Builder class for {@link KeyStoreParameter} objects.
      * <p>
      * This will build protection parameters for use with the
@@ -82,6 +281,19 @@
      */
     public final static class Builder {
         private int mFlags;
+        private Date mKeyValidityStart;
+        private Date mKeyValidityForOriginationEnd;
+        private Date mKeyValidityForConsumptionEnd;
+        private @KeyStoreKeyConstraints.PurposeEnum Integer mPurposes;
+        private @KeyStoreKeyConstraints.AlgorithmEnum Integer mAlgorithm;
+        private @KeyStoreKeyConstraints.PaddingEnum Integer mPadding;
+        private @KeyStoreKeyConstraints.DigestEnum Integer mDigest;
+        private @KeyStoreKeyConstraints.BlockModeEnum Integer mBlockMode;
+        private Integer mMinSecondsBetweenOperations;
+        private Integer mMaxUsesPerBoot;
+        private Set<Integer> mUserAuthenticators;
+        private Integer mUserAuthenticationValidityDurationSeconds;
+        private boolean mInvalidatedOnNewFingerprintEnrolled;
 
         /**
          * Creates a new instance of the {@code Builder} with the given
@@ -113,13 +325,233 @@
         }
 
         /**
-         * Builds the instance of the {@code KeyPairGeneratorSpec}.
+         * Sets the time instant before which the key is not yet valid.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityStart(Date startDate) {
+            mKeyValidityStart = startDate;
+            return this;
+        }
+
+        /**
+         * Sets the time instant after which the key is no longer valid.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityStart(Date)
+         * @see #setKeyValidityForConsumptionEnd(Date)
+         * @see #setKeyValidityForOriginationEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityEnd(Date endDate) {
+            setKeyValidityForOriginationEnd(endDate);
+            setKeyValidityForConsumptionEnd(endDate);
+            return this;
+        }
+
+        /**
+         * Sets the time instant after which the key is no longer valid for encryption and signing.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityForConsumptionEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityForOriginationEnd(Date endDate) {
+            mKeyValidityForOriginationEnd = endDate;
+            return this;
+        }
+
+        /**
+         * Sets the time instant after which the key is no longer valid for decryption and
+         * verification.
+         *
+         * <b>By default, the key is valid at any instant.
+         *
+         * @see #setKeyValidityForOriginationEnd(Date)
+         *
+         * @hide
+         */
+        public Builder setKeyValidityForConsumptionEnd(Date endDate) {
+            mKeyValidityForConsumptionEnd = endDate;
+            return this;
+        }
+
+        /**
+         * Restricts the purposes for which the key can be used to the provided set of purposes.
+         *
+         * <p>By default, the key can be used for encryption, decryption, signing, and verification.
+         *
+         * @hide
+         */
+        public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) {
+            mPurposes = purposes;
+            return this;
+        }
+
+        /**
+         * Sets the algorithm of the key.
+         *
+         * <p>The algorithm of symmetric keys can be deduced from the key itself. Thus, explicitly
+         * specifying the algorithm of symmetric keys using this method is not necessary.
+         *
+         * @hide
+         */
+        public Builder setAlgorithm(@KeyStoreKeyConstraints.AlgorithmEnum int algorithm) {
+            mAlgorithm = algorithm;
+            return this;
+        }
+
+        /**
+         * Restricts the key to being used only with the provided padding scheme. Attempts to use
+         * the key with any other padding will be rejected.
+         *
+         * <p>This restriction must be specified for keys which are used for encryption/decryption.
+         *
+         * @hide
+         */
+        public Builder setPadding(@KeyStoreKeyConstraints.PaddingEnum int padding) {
+            mPadding = padding;
+            return this;
+        }
+
+        /**
+         * Restricts the key to being used only with the provided digest when generating signatures
+         * or Message Authentication Codes (MACs). Attempts to use the key with any other digest
+         * will be rejected.
+         *
+         * <p>For MAC keys, the default is to restrict to the digest specified in the key algorithm
+         * name. For asymmetric signing keys this constraint must be specified because there is no
+         * default.
+         *
+         * @see java.security.Key#getAlgorithm()
+         *
+         * @hide
+         */
+        public Builder setDigest(@KeyStoreKeyConstraints.DigestEnum int digest) {
+            mDigest = digest;
+            return this;
+        }
+
+        /**
+         * Restricts the key to being used only with the provided block mode when encrypting or
+         * decrypting. Attempts to use the key with any other block modes will be rejected.
+         *
+         * <p>This restriction must be specified for keys which are used for encryption/decryption.
+         *
+         * @hide
+         */
+        public Builder setBlockMode(@KeyStoreKeyConstraints.BlockModeEnum int blockMode) {
+            mBlockMode = blockMode;
+            return this;
+        }
+
+        /**
+         * Sets the minimum number of seconds that must expire since the most recent use of the key
+         * before it can be used again.
+         *
+         * <p>By default, there is no restriction on how frequently a key can be used.
+         *
+         * @hide
+         */
+        public Builder setMinSecondsBetweenOperations(int seconds) {
+            mMinSecondsBetweenOperations = seconds;
+            return this;
+        }
+
+        /**
+         * Sets the maximum number of times a key can be used without rebooting the device.
+         *
+         * <p>By default, the key can be used for an unlimited number of times.
+         *
+         * @hide
+         */
+        public Builder setMaxUsesPerBoot(int count) {
+            mMaxUsesPerBoot = count;
+            return this;
+        }
+
+        /**
+         * Sets the user authenticators which protect access to this key. The key can only be used
+         * iff the user has authenticated to at least one of these user authenticators.
+         *
+         * <p>By default, the key can be used without user authentication.
+         *
+         * @param userAuthenticators user authenticators or empty list if this key can be accessed
+         *        without user authentication.
+         *
+         * @see #setUserAuthenticationValidityDurationSeconds(int)
+         *
+         * @hide
+         */
+        public Builder setUserAuthenticators(Set<Integer> userAuthenticators) {
+            mUserAuthenticators =
+                    (userAuthenticators != null) ? new HashSet<Integer>(userAuthenticators) : null;
+            return this;
+        }
+
+        /**
+         * Sets the duration of time (seconds) for which this key can be used after the user
+         * successfully authenticates to one of the associated user authenticators.
+         *
+         * <p>By default, the user needs to authenticate for every use of the key.
+         *
+         * @param seconds duration in seconds or {@code 0} if the user needs to authenticate for
+         *        every use of the key.
+         *
+         * @see #setUserAuthenticators(Set)
+         *
+         * @hide
+         */
+        public Builder setUserAuthenticationValidityDurationSeconds(int seconds) {
+            mUserAuthenticationValidityDurationSeconds = seconds;
+            return this;
+        }
+
+        /**
+         * Sets whether this key must be invalidated (permanently) whenever a new fingerprint is
+         * enrolled. This only has effect if fingerprint reader is one of the user authenticators
+         * protecting access to the key.
+         *
+         * <p>By default, enrolling a new fingerprint does not invalidate the key.
+         *
+         * @see #setUserAuthenticators(Set)
+         *
+         * @hide
+         */
+        public Builder setInvalidatedOnNewFingerprintEnrolled(boolean invalidated) {
+            mInvalidatedOnNewFingerprintEnrolled = invalidated;
+            return this;
+        }
+
+        /**
+         * Builds the instance of the {@code KeyStoreParameter}.
          *
          * @throws IllegalArgumentException if a required field is missing
-         * @return built instance of {@code KeyPairGeneratorSpec}
+         * @return built instance of {@code KeyStoreParameter}
          */
         public KeyStoreParameter build() {
-            return new KeyStoreParameter(mFlags);
+            return new KeyStoreParameter(mFlags,
+                    mKeyValidityStart,
+                    mKeyValidityForOriginationEnd,
+                    mKeyValidityForConsumptionEnd,
+                    mPurposes,
+                    mAlgorithm,
+                    mPadding,
+                    mDigest,
+                    mBlockMode,
+                    mMinSecondsBetweenOperations,
+                    mMaxUsesPerBoot,
+                    mUserAuthenticators,
+                    mUserAuthenticationValidityDurationSeconds,
+                    mInvalidatedOnNewFingerprintEnrolled);
         }
     }
 }
diff --git a/keystore/java/android/security/KeyStoreSecretKey.java b/keystore/java/android/security/KeyStoreSecretKey.java
new file mode 100644
index 0000000..7f0e3d3
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreSecretKey.java
@@ -0,0 +1,55 @@
+/*
+ * 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 android.security;
+
+import javax.crypto.SecretKey;
+
+/**
+ * {@link SecretKey} backed by keystore.
+ *
+ * @hide
+ */
+public class KeyStoreSecretKey implements SecretKey {
+    private final String mAlias;
+    private final String mAlgorithm;
+
+    public KeyStoreSecretKey(String alias, String algorithm) {
+        mAlias = alias;
+        mAlgorithm = algorithm;
+    }
+
+    String getAlias() {
+        return mAlias;
+    }
+
+    @Override
+    public String getAlgorithm() {
+        return mAlgorithm;
+    }
+
+    @Override
+    public String getFormat() {
+        // This key does not export its key material
+        return null;
+    }
+
+    @Override
+    public byte[] getEncoded() {
+        // This key does not export its key material
+        return null;
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
new file mode 100644
index 0000000..c205d9d
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
@@ -0,0 +1,179 @@
+/*
+ * 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 android.security;
+
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterDefs;
+
+import java.security.InvalidKeyException;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.util.Date;
+import java.util.Set;
+
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactorySpi;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * {@link SecretKeyFactorySpi} backed by Android KeyStore.
+ *
+ * @hide
+ */
+public class KeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
+
+    private final KeyStore mKeyStore = KeyStore.getInstance();
+
+    @Override
+    protected KeySpec engineGetKeySpec(SecretKey key,
+            @SuppressWarnings("rawtypes") Class keySpecClass) throws InvalidKeySpecException {
+        if (keySpecClass == null) {
+            throw new InvalidKeySpecException("keySpecClass == null");
+        }
+        if (!(key instanceof KeyStoreSecretKey)) {
+            throw new InvalidKeySpecException("Only Android KeyStore secret keys supported: " +
+                    ((key != null) ? key.getClass().getName() : "null"));
+        }
+        if (SecretKeySpec.class.isAssignableFrom(keySpecClass)) {
+            throw new InvalidKeySpecException(
+                    "Key material export of Android KeyStore keys is not supported");
+        }
+        if (!KeyStoreKeySpec.class.equals(keySpecClass)) {
+            throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
+        }
+        String keyAliasInKeystore = ((KeyStoreSecretKey) key).getAlias();
+        String entryAlias;
+        if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
+            entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
+        } else {
+            throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
+        }
+
+        KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
+        int errorCode =
+                mKeyStore.getKeyCharacteristics(keyAliasInKeystore, null, null, keyCharacteristics);
+        if (errorCode != KeyStore.NO_ERROR) {
+            throw new InvalidKeySpecException("Failed to obtain information about key."
+                    + " Keystore error: " + errorCode);
+        }
+
+        @KeyStoreKeyCharacteristics.OriginEnum Integer origin;
+        int keySize;
+        @KeyStoreKeyConstraints.PurposeEnum int purposes;
+        @KeyStoreKeyConstraints.AlgorithmEnum int algorithm;
+        @KeyStoreKeyConstraints.PaddingEnum Integer padding;
+        @KeyStoreKeyConstraints.DigestEnum Integer digest;
+        @KeyStoreKeyConstraints.BlockModeEnum Integer blockMode;
+        try {
+            origin = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_ORIGIN);
+            if (origin == null) {
+                throw new InvalidKeySpecException("Key origin not available");
+            }
+            origin = KeyStoreKeyCharacteristics.Origin.fromKeymaster(origin);
+            Integer keySizeInteger =
+                    KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_KEY_SIZE);
+            if (keySizeInteger == null) {
+                throw new InvalidKeySpecException("Key size not available");
+            }
+            keySize = keySizeInteger;
+            purposes = KeyStoreKeyConstraints.Purpose.allFromKeymaster(
+                    KeymasterUtils.getInts(keyCharacteristics, KeymasterDefs.KM_TAG_PURPOSE));
+            Integer alg = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_ALGORITHM);
+            if (alg == null) {
+                throw new InvalidKeySpecException("Key algorithm not available");
+            }
+            algorithm = KeyStoreKeyConstraints.Algorithm.fromKeymaster(alg);
+            padding = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_PADDING);
+            if (padding != null) {
+                padding = KeyStoreKeyConstraints.Padding.fromKeymaster(padding);
+            }
+            digest = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_DIGEST);
+            if (digest != null) {
+                digest = KeyStoreKeyConstraints.Digest.fromKeymaster(digest);
+            }
+            blockMode = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_BLOCK_MODE);
+            if (blockMode != null) {
+                blockMode = KeyStoreKeyConstraints.BlockMode.fromKeymaster(blockMode);
+            }
+        } catch (IllegalArgumentException e) {
+            throw new InvalidKeySpecException("Unsupported key characteristic", e);
+        }
+
+        Date keyValidityStart =
+                KeymasterUtils.getDate(keyCharacteristics, KeymasterDefs.KM_TAG_ACTIVE_DATETIME);
+        if ((keyValidityStart != null) && (keyValidityStart.getTime() <= 0)) {
+            keyValidityStart = null;
+        }
+        Date keyValidityForOriginationEnd = KeymasterUtils.getDate(keyCharacteristics,
+                KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME);
+        if ((keyValidityForOriginationEnd != null)
+                && (keyValidityForOriginationEnd.getTime() == Long.MAX_VALUE)) {
+            keyValidityForOriginationEnd = null;
+        }
+        Date keyValidityForConsumptionEnd = KeymasterUtils.getDate(keyCharacteristics,
+                KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME);
+        if ((keyValidityForConsumptionEnd != null)
+                && (keyValidityForConsumptionEnd.getTime() == Long.MAX_VALUE)) {
+            keyValidityForConsumptionEnd = null;
+        }
+
+        int swEnforcedUserAuthenticatorIds =
+                keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
+        int hwEnforcedUserAuthenticatorIds =
+                keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
+        int userAuthenticatorIds = swEnforcedUserAuthenticatorIds | hwEnforcedUserAuthenticatorIds;
+        Set<Integer> userAuthenticators =
+                KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster(userAuthenticatorIds);
+        Set<Integer> teeBackedUserAuthenticators =
+                KeyStoreKeyConstraints.UserAuthenticator.allFromKeymaster(
+                        hwEnforcedUserAuthenticatorIds);
+
+        // TODO: Populate the value below from key characteristics once Keymaster is ready.
+        boolean invalidatedOnNewFingerprintEnrolled = false;
+
+        return new KeyStoreKeySpec(entryAlias,
+                origin,
+                keySize,
+                keyValidityStart,
+                keyValidityForOriginationEnd,
+                keyValidityForConsumptionEnd,
+                purposes,
+                algorithm,
+                padding,
+                digest,
+                blockMode,
+                KeymasterUtils.getInt(keyCharacteristics,
+                        KeymasterDefs.KM_TAG_MIN_SECONDS_BETWEEN_OPS),
+                KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_MAX_USES_PER_BOOT),
+                userAuthenticators,
+                teeBackedUserAuthenticators,
+                KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_AUTH_TIMEOUT),
+                invalidatedOnNewFingerprintEnrolled);
+    }
+
+    @Override
+    protected SecretKey engineGenerateSecret(KeySpec keySpec) throws InvalidKeySpecException {
+        throw new UnsupportedOperationException(
+                "Key import into Android KeyStore is not supported");
+    }
+
+    @Override
+    protected SecretKey engineTranslateKey(SecretKey key) throws InvalidKeyException {
+        throw new UnsupportedOperationException(
+                "Key import into Android KeyStore is not supported");
+    }
+}
diff --git a/keystore/java/android/security/KeymasterException.java b/keystore/java/android/security/KeymasterException.java
new file mode 100644
index 0000000..484be12
--- /dev/null
+++ b/keystore/java/android/security/KeymasterException.java
@@ -0,0 +1,36 @@
+/*
+ * 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 android.security;
+
+/**
+ * Keymaster exception.
+ *
+ * @hide
+ */
+public class KeymasterException extends Exception {
+
+    private final int mErrorCode;
+
+    public KeymasterException(int errorCode, String message) {
+        super(message);
+        mErrorCode = errorCode;
+    }
+
+    public int getErrorCode() {
+        return mErrorCode;
+    }
+}
diff --git a/keystore/java/android/security/KeymasterUtils.java b/keystore/java/android/security/KeymasterUtils.java
new file mode 100644
index 0000000..55999dc
--- /dev/null
+++ b/keystore/java/android/security/KeymasterUtils.java
@@ -0,0 +1,97 @@
+/*
+ * 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 android.security;
+
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterDefs;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public abstract class KeymasterUtils {
+    private KeymasterUtils() {}
+
+    public static KeymasterException getKeymasterException(int keymasterErrorCode) {
+        switch (keymasterErrorCode) {
+            case KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT:
+                // The name of this parameter significantly differs between Keymaster and framework
+                // APIs. Use the framework wording to make life easier for developers.
+                return new KeymasterException(keymasterErrorCode,
+                        "Invalid user authentication validity duration");
+            default:
+                return new KeymasterException(keymasterErrorCode,
+                        KeymasterDefs.getErrorMessage(keymasterErrorCode));
+        }
+    }
+
+    public static CryptoOperationException getCryptoOperationException(KeymasterException e) {
+        switch (e.getErrorCode()) {
+            case KeymasterDefs.KM_ERROR_KEY_EXPIRED:
+                return new KeyExpiredException();
+            case KeymasterDefs.KM_ERROR_KEY_NOT_YET_VALID:
+                return new KeyNotYetValidException();
+            case KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED:
+                return new UserNotAuthenticatedException();
+            // TODO: Handle TBD Keymaster error code "invalid key: new fingerprint enrolled"
+            // case KeymasterDefs.KM_ERROR_TBD
+            //     return new NewFingerprintEnrolledException();
+            default:
+                return new CryptoOperationException("Crypto operation failed", e);
+        }
+    }
+
+    public static CryptoOperationException getCryptoOperationException(int keymasterErrorCode) {
+        return getCryptoOperationException(getKeymasterException(keymasterErrorCode));
+    }
+
+    public static Integer getInt(KeyCharacteristics keyCharacteristics, int tag) {
+        if (keyCharacteristics.hwEnforced.containsTag(tag)) {
+            return keyCharacteristics.hwEnforced.getInt(tag, -1);
+        } else if (keyCharacteristics.swEnforced.containsTag(tag)) {
+            return keyCharacteristics.swEnforced.getInt(tag, -1);
+        } else {
+            return null;
+        }
+    }
+
+    public static List<Integer> getInts(KeyCharacteristics keyCharacteristics, int tag) {
+        List<Integer> result = new ArrayList<Integer>();
+        result.addAll(keyCharacteristics.hwEnforced.getInts(tag));
+        result.addAll(keyCharacteristics.swEnforced.getInts(tag));
+        return result;
+    }
+
+    public static Date getDate(KeyCharacteristics keyCharacteristics, int tag) {
+        Date result = keyCharacteristics.hwEnforced.getDate(tag, null);
+        if (result == null) {
+            result = keyCharacteristics.swEnforced.getDate(tag, null);
+        }
+        return result;
+    }
+
+    public static boolean getBoolean(KeyCharacteristics keyCharacteristics, int tag) {
+        if (keyCharacteristics.hwEnforced.containsTag(tag)) {
+            return keyCharacteristics.hwEnforced.getBoolean(tag, false);
+        } else {
+            return keyCharacteristics.swEnforced.getBoolean(tag, false);
+        }
+    }
+}
diff --git a/keystore/java/android/security/NewFingerprintEnrolledException.java b/keystore/java/android/security/NewFingerprintEnrolledException.java
new file mode 100644
index 0000000..6da4a2a
--- /dev/null
+++ b/keystore/java/android/security/NewFingerprintEnrolledException.java
@@ -0,0 +1,41 @@
+/*
+ * 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 android.security;
+
+/**
+ * Indicates that a cryptographic operation could not be performed because the key used by the
+ * operation is permanently invalid because a new fingerprint was enrolled.
+ *
+ * @hide
+ */
+public class NewFingerprintEnrolledException extends CryptoOperationException {
+
+    /**
+     * Constructs a new {@code NewFingerprintEnrolledException} without detail message and cause.
+     */
+    public NewFingerprintEnrolledException() {
+        super("Invalid key: new fingerprint enrolled");
+    }
+
+    /**
+     * Constructs a new {@code NewFingerprintEnrolledException} with the provided detail message and
+     * no cause.
+     */
+    public NewFingerprintEnrolledException(String message) {
+        super(message);
+    }
+}
diff --git a/keystore/java/android/security/UserNotAuthenticatedException.java b/keystore/java/android/security/UserNotAuthenticatedException.java
new file mode 100644
index 0000000..e6342ef
--- /dev/null
+++ b/keystore/java/android/security/UserNotAuthenticatedException.java
@@ -0,0 +1,49 @@
+/*
+ * 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 android.security;
+
+/**
+ * Indicates that a cryptographic operation could not be performed because the user has not been
+ * authenticated recently enough.
+ *
+ * @hide
+ */
+public class UserNotAuthenticatedException extends CryptoOperationException {
+
+    /**
+     * Constructs a new {@code UserNotAuthenticatedException} without detail message and cause.
+     */
+    public UserNotAuthenticatedException() {
+        super("User not authenticated");
+    }
+
+    /**
+     * Constructs a new {@code UserNotAuthenticatedException} with the provided detail message and
+     * no cause.
+     */
+    public UserNotAuthenticatedException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new {@code UserNotAuthenticatedException} with the provided detail message and
+     * cause.
+     */
+    public UserNotAuthenticatedException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/keystore/tests/src/android/security/AndroidKeyStoreTest.java b/keystore/tests/src/android/security/AndroidKeyStoreTest.java
index 9775e64..7a88dee 100644
--- a/keystore/tests/src/android/security/AndroidKeyStoreTest.java
+++ b/keystore/tests/src/android/security/AndroidKeyStoreTest.java
@@ -2127,7 +2127,7 @@
         assertEquals("The keystore size should match expected", 2, mKeyStore.size());
         assertAliases(new String[] { TEST_ALIAS_2, TEST_ALIAS_3 });
 
-        assertTrue(mAndroidKeyStore.delKey(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_3));
+        assertTrue(mAndroidKeyStore.delete(Credentials.USER_PRIVATE_KEY + TEST_ALIAS_3));
 
         assertEquals("The keystore size should match expected", 1, mKeyStore.size());
         assertAliases(new String[] { TEST_ALIAS_2 });
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index f935bb1..7468fb5e 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -25,6 +25,7 @@
 import android.security.keymaster.ExportResult;
 import android.security.keymaster.KeyCharacteristics;
 import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterBlob;
 import android.security.keymaster.KeymasterDefs;
 import android.security.keymaster.OperationResult;
 import android.test.ActivityUnitTestCase;
@@ -36,6 +37,7 @@
 import java.util.Arrays;
 import java.util.Date;
 import java.util.HashSet;
+import java.security.spec.RSAKeyGenParameterSpec;
 
 import android.util.Log;
 import android.util.Base64;
@@ -711,11 +713,11 @@
         args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
         args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
         args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 2048);
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, null);
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null);
+        args.addLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT,
+                RSAKeyGenParameterSpec.F4.longValue());
 
         KeyCharacteristics outCharacteristics = new KeyCharacteristics();
-        int result = mKeyStore.generateKey(name, args, 0, outCharacteristics);
+        int result = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
         assertEquals("generateRsaKey should succeed", KeyStore.NO_ERROR, result);
         return outCharacteristics;
     }
@@ -724,6 +726,24 @@
         generateRsaKey("test");
         mKeyStore.delete("test");
     }
+
+    public void testGenerateRsaWithEntropy() throws Exception {
+        byte[] entropy = new byte[] {1,2,3,4,5};
+        String name = "test";
+        KeymasterArguments args = new KeymasterArguments();
+        args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
+        args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
+        args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+        args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+        args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 2048);
+        args.addLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT,
+                RSAKeyGenParameterSpec.F4.longValue());
+
+        KeyCharacteristics outCharacteristics = new KeyCharacteristics();
+        int result = mKeyStore.generateKey(name, args, entropy, 0, outCharacteristics);
+        assertEquals("generateKey should succeed", KeyStore.NO_ERROR, result);
+    }
+
     public void testGenerateAndDelete() throws Exception {
         generateRsaKey("test");
         assertTrue("delete should succeed", mKeyStore.delete("test"));
@@ -741,6 +761,7 @@
 
     public void testAppId() throws Exception {
         String name = "test";
+        byte[] id = new byte[] {0x01, 0x02, 0x03};
         KeymasterArguments args = new KeymasterArguments();
         args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
         args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
@@ -748,18 +769,19 @@
         args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
         args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 2048);
         args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB);
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, new byte[] {0x01, 0x02, 0x03});
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null);
+        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, id);
+        args.addLong(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT,
+                RSAKeyGenParameterSpec.F4.longValue());
 
         KeyCharacteristics outCharacteristics = new KeyCharacteristics();
-        int result = mKeyStore.generateKey(name, args, 0, outCharacteristics);
+        int result = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
         assertEquals("generateRsaKey should succeed", KeyStore.NO_ERROR, result);
         assertEquals("getKeyCharacteristics should fail without application ID",
                 KeymasterDefs.KM_ERROR_INVALID_KEY_BLOB,
                 mKeyStore.getKeyCharacteristics(name, null, null, outCharacteristics));
         assertEquals("getKeyCharacteristics should succeed with application ID",
                 KeyStore.NO_ERROR,
-                mKeyStore.getKeyCharacteristics(name, new byte[] {0x01, 0x02, 0x03}, null,
+                mKeyStore.getKeyCharacteristics(name, new KeymasterBlob(id), null,
                     outCharacteristics));
     }
 
@@ -784,19 +806,15 @@
         args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB);
         args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096);
         args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16);
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, null);
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null);
 
         KeyCharacteristics outCharacteristics = new KeyCharacteristics();
-        int rc = mKeyStore.generateKey(name, args, 0, outCharacteristics);
+        int rc = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
         assertEquals("Generate should succeed", KeyStore.NO_ERROR, rc);
 
         KeymasterArguments out = new KeymasterArguments();
         args = new KeymasterArguments();
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, null);
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null);
         OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT,
-                true, args, out);
+                true, args, null, out);
         IBinder token = result.token;
         assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
         result = mKeyStore.update(token, null, new byte[] {0x01, 0x02, 0x03, 0x04});
@@ -826,7 +844,7 @@
     private byte[] doOperation(String name, int purpose, byte[] in, KeymasterArguments beginArgs) {
         KeymasterArguments out = new KeymasterArguments();
         OperationResult result = mKeyStore.begin(name, purpose,
-                true, beginArgs, out);
+                true, beginArgs, null, out);
         assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
         IBinder token = result.token;
         result = mKeyStore.update(token, null, in);
@@ -883,24 +901,21 @@
         args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB);
         args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096);
         args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16);
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, null);
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null);
 
         KeyCharacteristics outCharacteristics = new KeyCharacteristics();
-        int rc = mKeyStore.generateKey(name, args, 0, outCharacteristics);
+        int rc = mKeyStore.generateKey(name, args, null, 0, outCharacteristics);
         assertEquals("Generate should succeed", KeyStore.NO_ERROR, rc);
 
         KeymasterArguments out = new KeymasterArguments();
         args = new KeymasterArguments();
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_ID, null);
-        args.addBlob(KeymasterDefs.KM_TAG_APPLICATION_DATA, null);
         OperationResult result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT,
-                true, args, out);
+                true, args, null, out);
         assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
         IBinder first = result.token;
         // Implementation detail: softkeymaster supports 16 concurrent operations
         for (int i = 0; i < 16; i++) {
-            result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, true, args, out);
+            result = mKeyStore.begin(name, KeymasterDefs.KM_PURPOSE_ENCRYPT, true, args, null,
+                    out);
             assertEquals("Begin should succeed", KeyStore.NO_ERROR, result.resultCode);
         }
         // At this point the first operation should be pruned.
diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp
index 227de3b..9300794 100644
--- a/libs/androidfw/BackupHelpers.cpp
+++ b/libs/androidfw/BackupHelpers.cpp
@@ -478,7 +478,8 @@
 }
 
 int write_tarfile(const String8& packageName, const String8& domain,
-        const String8& rootpath, const String8& filepath, BackupDataWriter* writer)
+        const String8& rootpath, const String8& filepath, off_t* outSize,
+        BackupDataWriter* writer)
 {
     // In the output stream everything is stored relative to the root
     const char* relstart = filepath.string() + rootpath.length();
@@ -488,6 +489,7 @@
     // If relpath is empty, it means this is the top of one of the standard named
     // domain directories, so we should just skip it
     if (relpath.length() == 0) {
+        *outSize = 0;
         return 0;
     }
 
@@ -517,12 +519,25 @@
         return err;
     }
 
+    // very large files need a pax extended size header
+    if (s.st_size > 077777777777LL) {
+        needExtended = true;
+    }
+
     String8 fullname;   // for pax later on
     String8 prefix;
 
     const int isdir = S_ISDIR(s.st_mode);
     if (isdir) s.st_size = 0;   // directories get no actual data in the tar stream
 
+    // Report the size, including a rough tar overhead estimation: 512 bytes for the
+    // overall tar file-block header, plus 2 blocks if using the pax extended format,
+    // plus the raw content size rounded up to a multiple of 512.
+    *outSize = 512 + (needExtended ? 1024 : 0) + 512*((s.st_size + 511)/512);
+
+    // Measure case: we've returned the size; now return without moving data
+    if (!writer) return 0;
+
     // !!! TODO: use mmap when possible to avoid churning the buffer cache
     // !!! TODO: this will break with symlinks; need to use readlink(2)
     int fd = open(filepath.string(), O_RDONLY);
@@ -560,10 +575,6 @@
     snprintf(buf + 116, 8, "0%lo", (unsigned long)s.st_gid);
 
     // [ 124 :  12 ] file size in bytes
-    if (s.st_size > 077777777777LL) {
-        // very large files need a pax extended size header
-        needExtended = true;
-    }
     snprintf(buf + 124, 12, "%011llo", (isdir) ? 0LL : s.st_size);
 
     // [ 136 :  12 ] last mod time as a UTC time_t
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp
index 852204a..eb520b4 100644
--- a/libs/hwui/ClipArea.cpp
+++ b/libs/hwui/ClipArea.cpp
@@ -234,7 +234,7 @@
 bool ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
     enterRegionMode();
     mClipRegion.op(region, op);
-    setClipRectToRegionBounds();
+    onClipRegionUpdated();
     return true;
 }
 
@@ -263,6 +263,9 @@
 bool ClipArea::rectangleModeClipRectWithTransform(const Rect& r,
         const mat4* transform, SkRegion::Op op) {
 
+    // TODO: we should be able to handle kReplace_Op efficiently without
+    // going through RegionMode and later falling back into RectangleMode.
+
     if (op != SkRegion::kIntersect_Op) {
         enterRegionMode();
         return regionModeClipRectWithTransform(r, transform, op);
@@ -324,15 +327,16 @@
  */
 
 void ClipArea::enterRegionMode() {
-    if (mMode != kModeRegion) {
-        if (mMode == kModeRectangle) {
+    Mode oldMode = mMode;
+    mMode = kModeRegion;
+    if (oldMode != kModeRegion) {
+        if (oldMode == kModeRectangle) {
             mClipRegion.setRect(mClipRect.left, mClipRect.top,
                     mClipRect.right, mClipRect.bottom);
         } else {
             mClipRegion = mRectangleList.convertToRegion(createViewportRegion());
-            setClipRectToRegionBounds();
+            onClipRegionUpdated();
         }
-        mMode = kModeRegion;
     }
 }
 
@@ -342,7 +346,7 @@
     SkRegion transformedRectRegion;
     regionFromPath(transformedRect, transformedRectRegion);
     mClipRegion.op(transformedRectRegion, op);
-    setClipRectToRegionBounds();
+    onClipRegionUpdated();
     return true;
 }
 
@@ -352,12 +356,13 @@
             transform, op);
 }
 
-void ClipArea::setClipRectToRegionBounds() {
+void ClipArea::onClipRegionUpdated() {
     if (!mClipRegion.isEmpty()) {
         mClipRect.set(mClipRegion.getBounds());
 
         if (mClipRegion.isRect()) {
             mClipRegion.setEmpty();
+            enterRectangleMode();
         }
     } else {
         mClipRect.setEmpty();
diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h
index 16e6df9..e284af0 100644
--- a/libs/hwui/ClipArea.h
+++ b/libs/hwui/ClipArea.h
@@ -144,7 +144,7 @@
             float bottom, const mat4* transform, SkRegion::Op op);
 
     void ensureClipRegion();
-    void setClipRectToRegionBounds();
+    void onClipRegionUpdated();
     bool clipRegionOp(float left, float top, float right, float bottom,
             SkRegion::Op op);
 
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 2a673f4..8757e15 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -484,7 +484,7 @@
 }
 
 void DisplayListRenderer::setDrawFilter(SkDrawFilter* filter) {
-    mDrawFilter.reset(filter);
+    mDrawFilter.reset(SkSafeRef(filter));
 }
 
 void DisplayListRenderer::insertReorderBarrier(bool enableReorder) {
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 48ecd69..53fd1ad 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -296,8 +296,9 @@
         // so that we don't need to modify the paint every time we access it.
         SkTLazy<SkPaint> filteredPaint;
         if (mDrawFilter.get()) {
-            paint = filteredPaint.init();
+            filteredPaint.set(*paint);
             mDrawFilter->filter(filteredPaint.get(), SkDrawFilter::kPaint_Type);
+            paint = filteredPaint.get();
         }
 
         // compute the hash key for the paint and check the cache.
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 7df61f27..65be9e1 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -16,8 +16,12 @@
 #include "JankTracker.h"
 
 #include <algorithm>
+#include <cutils/ashmem.h>
+#include <cutils/log.h>
 #include <cstdio>
+#include <errno.h>
 #include <inttypes.h>
+#include <sys/mman.h>
 
 namespace android {
 namespace uirenderer {
@@ -63,11 +67,114 @@
         = FrameInfoFlags::kWindowLayoutChanged
         | FrameInfoFlags::kSurfaceCanvas;
 
+// The bucketing algorithm controls so to speak
+// If a frame is <= to this it goes in bucket 0
+static const uint32_t kBucketMinThreshold = 7;
+// If a frame is > this, start counting in increments of 2ms
+static const uint32_t kBucket2msIntervals = 32;
+// If a frame is > this, start counting in increments of 4ms
+static const uint32_t kBucket4msIntervals = 48;
+
+// This will be called every frame, performance sensitive
+// Uses bit twiddling to avoid branching while achieving the packing desired
+static uint32_t frameCountIndexForFrameTime(nsecs_t frameTime, uint32_t max) {
+    uint32_t index = static_cast<uint32_t>(ns2ms(frameTime));
+    // If index > kBucketMinThreshold mask will be 0xFFFFFFFF as a result
+    // of negating 1 (twos compliment, yaay) else mask will be 0
+    uint32_t mask = -(index > kBucketMinThreshold);
+    // If index > threshold, this will essentially perform:
+    // amountAboveThreshold = index - threshold;
+    // index = threshold + (amountAboveThreshold / 2)
+    // However if index is <= this will do nothing. It will underflow, do
+    // a right shift by 0 (no-op), then overflow back to the original value
+    index = ((index - kBucket4msIntervals) >> (index > kBucket4msIntervals))
+            + kBucket4msIntervals;
+    index = ((index - kBucket2msIntervals) >> (index > kBucket2msIntervals))
+            + kBucket2msIntervals;
+    // If index was < minThreshold at the start of all this it's going to
+    // be a pretty garbage value right now. However, mask is 0 so we'll end
+    // up with the desired result of 0.
+    index = (index - kBucketMinThreshold) & mask;
+    return index < max ? index : max;
+}
+
+// Only called when dumping stats, less performance sensitive
+static uint32_t frameTimeForFrameCountIndex(uint32_t index) {
+    index = index + kBucketMinThreshold;
+    if (index > kBucket2msIntervals) {
+        index += (index - kBucket2msIntervals);
+    }
+    if (index > kBucket4msIntervals) {
+        // This works because it was already doubled by the above if
+        // 1 is added to shift slightly more towards the middle of the bucket
+        index += (index - kBucket4msIntervals) + 1;
+    }
+    return index;
+}
+
 JankTracker::JankTracker(nsecs_t frameIntervalNanos) {
+    // By default this will use malloc memory. It may be moved later to ashmem
+    // if there is shared space for it and a request comes in to do that.
+    mData = new ProfileData;
     reset();
     setFrameInterval(frameIntervalNanos);
 }
 
+JankTracker::~JankTracker() {
+    freeData();
+}
+
+void JankTracker::freeData() {
+    if (mIsMapped) {
+        munmap(mData, sizeof(ProfileData));
+    } else {
+        delete mData;
+    }
+    mIsMapped = false;
+    mData = nullptr;
+}
+
+void JankTracker::switchStorageToAshmem(int ashmemfd) {
+    int regionSize = ashmem_get_size_region(ashmemfd);
+    if (regionSize < static_cast<int>(sizeof(ProfileData))) {
+        ALOGW("Ashmem region is too small! Received %d, required %u",
+                regionSize, static_cast<unsigned int>(sizeof(ProfileData)));
+        return;
+    }
+    ProfileData* newData = reinterpret_cast<ProfileData*>(
+            mmap(NULL, sizeof(ProfileData), PROT_READ | PROT_WRITE,
+            MAP_SHARED, ashmemfd, 0));
+    if (newData == MAP_FAILED) {
+        int err = errno;
+        ALOGW("Failed to move profile data to ashmem fd %d, error = %d",
+                ashmemfd, err);
+        return;
+    }
+
+    // The new buffer may have historical data that we want to build on top of
+    // But let's make sure we don't overflow Just In Case
+    uint32_t divider = 0;
+    if (newData->totalFrameCount > (1 << 24)) {
+        divider = 4;
+    }
+    for (size_t i = 0; i < mData->jankTypeCounts.size(); i++) {
+        newData->jankTypeCounts[i] >>= divider;
+        newData->jankTypeCounts[i] += mData->jankTypeCounts[i];
+    }
+    for (size_t i = 0; i < mData->frameCounts.size(); i++) {
+        newData->frameCounts[i] >>= divider;
+        newData->frameCounts[i] += mData->frameCounts[i];
+    }
+    newData->jankFrameCount >>= divider;
+    newData->jankFrameCount += mData->jankFrameCount;
+    newData->totalFrameCount >>= divider;
+    newData->totalFrameCount += mData->totalFrameCount;
+
+    freeData();
+    mData = newData;
+    mIsMapped = true;
+}
+
 void JankTracker::setFrameInterval(nsecs_t frameInterval) {
     mFrameInterval = frameInterval;
     mThresholds[kMissedVsync] = 1;
@@ -92,16 +199,15 @@
 }
 
 void JankTracker::addFrame(const FrameInfo& frame) {
-    mTotalFrameCount++;
+    mData->totalFrameCount++;
     // Fast-path for jank-free frames
     int64_t totalDuration =
             frame[FrameInfoIndex::kFrameCompleted] - frame[FrameInfoIndex::kIntendedVsync];
-    uint32_t framebucket = std::min(
-            static_cast<typeof mFrameCounts.size()>(ns2ms(totalDuration)),
-            mFrameCounts.size());
+    uint32_t framebucket = frameCountIndexForFrameTime(
+            totalDuration, mData->frameCounts.size());
     // Keep the fast path as fast as possible.
     if (CC_LIKELY(totalDuration < mFrameInterval)) {
-        mFrameCounts[framebucket]++;
+        mData->frameCounts[framebucket]++;
         return;
     }
 
@@ -109,47 +215,52 @@
         return;
     }
 
-    mFrameCounts[framebucket]++;
-    mJankFrameCount++;
+    mData->frameCounts[framebucket]++;
+    mData->jankFrameCount++;
 
     for (int i = 0; i < NUM_BUCKETS; i++) {
         int64_t delta = frame[COMPARISONS[i].end] - frame[COMPARISONS[i].start];
         if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) {
-            mBuckets[i].count++;
+            mData->jankTypeCounts[i]++;
         }
     }
 }
 
-void JankTracker::dump(int fd) {
-    FILE* file = fdopen(fd, "a");
-    fprintf(file, "\nFrame stats:");
-    fprintf(file, "\n  Total frames rendered: %u", mTotalFrameCount);
-    fprintf(file, "\n  Janky frames: %u (%.2f%%)", mJankFrameCount,
-            (float) mJankFrameCount / (float) mTotalFrameCount * 100.0f);
-    fprintf(file, "\n  90th percentile: %ums", findPercentile(90));
-    fprintf(file, "\n  95th percentile: %ums", findPercentile(95));
-    fprintf(file, "\n  99th percentile: %ums", findPercentile(99));
-    for (int i = 0; i < NUM_BUCKETS; i++) {
-        fprintf(file, "\n   Number %s: %u", JANK_TYPE_NAMES[i], mBuckets[i].count);
+void JankTracker::dumpBuffer(const void* buffer, size_t bufsize, int fd) {
+    if (bufsize < sizeof(ProfileData)) {
+        return;
     }
-    fprintf(file, "\n");
-    fflush(file);
+    const ProfileData* data = reinterpret_cast<const ProfileData*>(buffer);
+    dumpData(data, fd);
+}
+
+void JankTracker::dumpData(const ProfileData* data, int fd) {
+    dprintf(fd, "\nTotal frames rendered: %u", data->totalFrameCount);
+    dprintf(fd, "\nJanky frames: %u (%.2f%%)", data->jankFrameCount,
+            (float) data->jankFrameCount / (float) data->totalFrameCount * 100.0f);
+    dprintf(fd, "\n90th percentile: %ums", findPercentile(data, 90));
+    dprintf(fd, "\n95th percentile: %ums", findPercentile(data, 95));
+    dprintf(fd, "\n99th percentile: %ums", findPercentile(data, 99));
+    for (int i = 0; i < NUM_BUCKETS; i++) {
+        dprintf(fd, "\nNumber %s: %u", JANK_TYPE_NAMES[i], data->jankTypeCounts[i]);
+    }
+    dprintf(fd, "\n");
 }
 
 void JankTracker::reset() {
-    mBuckets.fill({0});
-    mFrameCounts.fill(0);
-    mTotalFrameCount = 0;
-    mJankFrameCount = 0;
+    mData->jankTypeCounts.fill(0);
+    mData->frameCounts.fill(0);
+    mData->totalFrameCount = 0;
+    mData->jankFrameCount = 0;
 }
 
-uint32_t JankTracker::findPercentile(int percentile) {
-    int pos = percentile * mTotalFrameCount / 100;
-    int remaining = mTotalFrameCount - pos;
-    for (int i = mFrameCounts.size() - 1; i >= 0; i--) {
-        remaining -= mFrameCounts[i];
+uint32_t JankTracker::findPercentile(const ProfileData* data, int percentile) {
+    int pos = percentile * data->totalFrameCount / 100;
+    int remaining = data->totalFrameCount - pos;
+    for (int i = data->frameCounts.size() - 1; i >= 0; i--) {
+        remaining -= data->frameCounts[i];
         if (remaining <= 0) {
-            return i;
+            return frameTimeForFrameCountIndex(i);
         }
     }
     return 0;
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index ae339ec..4783001 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -20,6 +20,8 @@
 #include "renderthread/TimeLord.h"
 #include "utils/RingBuffer.h"
 
+#include <cutils/compiler.h>
+
 #include <array>
 #include <memory>
 
@@ -37,33 +39,45 @@
     NUM_BUCKETS,
 };
 
-struct JankBucket {
-    // Number of frames that hit this bucket
-    uint32_t count;
+// Try to keep as small as possible, should match ASHMEM_SIZE in
+// GraphicsStatsService.java
+struct ProfileData {
+    std::array<uint32_t, NUM_BUCKETS> jankTypeCounts;
+    // See comments on kBucket* constants for what this holds
+    std::array<uint32_t, 57> frameCounts;
+
+    uint32_t totalFrameCount;
+    uint32_t jankFrameCount;
 };
 
 // TODO: Replace DrawProfiler with this
 class JankTracker {
 public:
     JankTracker(nsecs_t frameIntervalNanos);
-
-    void setFrameInterval(nsecs_t frameIntervalNanos);
+    ~JankTracker();
 
     void addFrame(const FrameInfo& frame);
 
-    void dump(int fd);
+    void dump(int fd) { dumpData(mData, fd); }
     void reset();
 
+    void switchStorageToAshmem(int ashmemfd);
+
+    uint32_t findPercentile(int p) { return findPercentile(mData, p); }
+
+    ANDROID_API static void dumpBuffer(const void* buffer, size_t bufsize, int fd);
+
 private:
-    uint32_t findPercentile(int p);
+    void freeData();
+    void setFrameInterval(nsecs_t frameIntervalNanos);
 
-    std::array<JankBucket, NUM_BUCKETS> mBuckets;
+    static uint32_t findPercentile(const ProfileData* data, int p);
+    static void dumpData(const ProfileData* data, int fd);
+
     std::array<int64_t, NUM_BUCKETS> mThresholds;
-    std::array<uint32_t, 128> mFrameCounts;
-
     int64_t mFrameInterval;
-    uint32_t mTotalFrameCount;
-    uint32_t mJankFrameCount;
+    ProfileData* mData;
+    bool mIsMapped = false;
 };
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 3781969..02fbd89 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -858,6 +858,8 @@
                 .setModelViewMapUnitToRectOptionalSnap(snap, rect)
                 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
                 .build();
+        renderGlop(glop);
+        return;
     }
 
     float alpha = getLayerAlpha(layer);
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 9509c48..30d3f41 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -197,6 +197,7 @@
             case SkPath::kLine_Verb:
                 arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
                 break;
+            case SkPath::kConic_Verb:
             case SkPath::kQuad_Verb:
                 arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
                 arrayForDirection.add((Vector2){pts[2].x(), pts[2].y()});
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index 66de333..d9d06bf 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -380,6 +380,7 @@
         const Vector3& lightCenter, float lightRadius) {
     ShadowDescription key(casterPerimeter, drawTransform);
 
+    if (mShadowCache.get(key)) return;
     sp<ShadowTask> task = new ShadowTask(drawTransform, localClip, opaque,
             casterPerimeter, transformXY, transformZ, lightCenter, lightRadius);
     if (mShadowProcessor == nullptr) {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 9456073..9237151 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -41,15 +41,10 @@
         RenderNode* rootRenderNode, IContextFactory* contextFactory)
         : mRenderThread(thread)
         , mEglManager(thread.eglManager())
-        , mEglSurface(EGL_NO_SURFACE)
-        , mBufferPreserved(false)
-        , mSwapBehavior(kSwap_default)
         , mOpaque(!translucent)
-        , mCanvas(nullptr)
-        , mHaveNewSurface(false)
         , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
         , mRootRenderNode(rootRenderNode)
-        , mCurrentFrameInfo(nullptr) {
+        , mJankTracker(thread.timeLord().frameIntervalNanos()) {
     mRenderThread.renderState().registerCanvasContext(this);
     mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
 }
@@ -258,6 +253,7 @@
 
     // TODO: Use a fence for real completion?
     mCurrentFrameInfo->markFrameCompleted();
+    mJankTracker.addFrame(*mCurrentFrameInfo);
     mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
     profiler().finishFrame();
 }
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index c3904c2..f5f1f54 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -127,23 +127,24 @@
     RenderThread& mRenderThread;
     EglManager& mEglManager;
     sp<ANativeWindow> mNativeWindow;
-    EGLSurface mEglSurface;
-    bool mBufferPreserved;
-    SwapBehavior mSwapBehavior;
+    EGLSurface mEglSurface = EGL_NO_SURFACE;
+    bool mBufferPreserved = false;
+    SwapBehavior mSwapBehavior = kSwap_default;
 
     bool mOpaque;
-    OpenGLRenderer* mCanvas;
-    bool mHaveNewSurface;
+    OpenGLRenderer* mCanvas = nullptr;
+    bool mHaveNewSurface = false;
     DamageAccumulator mDamageAccumulator;
     std::unique_ptr<AnimationContext> mAnimationContext;
 
     const sp<RenderNode> mRootRenderNode;
 
     DrawProfiler mProfiler;
-    FrameInfo* mCurrentFrameInfo;
+    FrameInfo* mCurrentFrameInfo = nullptr;
     // Ring buffer large enough for 1 second worth of frames
     RingBuffer<FrameInfo, 60> mFrames;
     std::string mName;
+    JankTracker mJankTracker;
 
     std::set<RenderNode*> mPrefetechedLayers;
 };
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index ea4216c..cc87241 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -418,6 +418,7 @@
 }
 
 void RenderProxy::dumpGraphicsMemory(int fd) {
+    if (!RenderThread::hasInstance()) return;
     SETUP_TASK(dumpGraphicsMemory);
     args->fd = fd;
     args->thread = &RenderThread::getInstance();
@@ -440,6 +441,19 @@
     post(task);
 }
 
+CREATE_BRIDGE2(setProcessStatsBuffer, RenderThread* thread, int fd) {
+    args->thread->jankTracker().switchStorageToAshmem(args->fd);
+    close(args->fd);
+    return nullptr;
+}
+
+void RenderProxy::setProcessStatsBuffer(int fd) {
+    SETUP_TASK(setProcessStatsBuffer);
+    args->thread = &mRenderThread;
+    args->fd = dup(fd);
+    post(task);
+}
+
 void RenderProxy::post(RenderTask* task) {
     mRenderThread.queue(task);
 }
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 43cbe07..29c6f08 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -99,6 +99,7 @@
     ANDROID_API static void dumpGraphicsMemory(int fd);
 
     ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size);
+    ANDROID_API void setProcessStatsBuffer(int fd);
 
 private:
     RenderThread& mRenderThread;
diff --git a/libs/hwui/thread/Signal.h b/libs/hwui/thread/Signal.h
index dcf5449..d4cfeeb 100644
--- a/libs/hwui/thread/Signal.h
+++ b/libs/hwui/thread/Signal.h
@@ -30,8 +30,10 @@
     ~Signal() { }
 
     void signal() {
-        Mutex::Autolock l(mLock);
-        mSignaled = true;
+        {
+            Mutex::Autolock l(mLock);
+            mSignaled = true;
+        }
         mCondition.signal(mType);
     }
 
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index c69b2fd..f0ed0bb 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -109,8 +109,11 @@
         return false;
     }
 
-    Mutex::Autolock l(mLock);
-    ssize_t index = mTasks.add(task);
+    ssize_t index;
+    {
+        Mutex::Autolock l(mLock);
+        index = mTasks.add(task);
+    }
     mSignal.signal();
 
     return index >= 0;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 28941b9..cb70e8b 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -134,6 +134,22 @@
     public static final String VOLUME_CHANGED_ACTION = "android.media.VOLUME_CHANGED_ACTION";
 
     /**
+     * @hide Broadcast intent when the devices for a particular stream type changes.
+     * Includes the stream, the new devices and previous devices.
+     * Notes:
+     *  - for internal platform use only, do not make public,
+     *  - never used for "remote" volume changes
+     *
+     * @see #EXTRA_VOLUME_STREAM_TYPE
+     * @see #EXTRA_VOLUME_STREAM_DEVICES
+     * @see #EXTRA_PREV_VOLUME_STREAM_DEVICES
+     * @see #getDevicesForStream
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String STREAM_DEVICES_CHANGED_ACTION =
+        "android.media.STREAM_DEVICES_CHANGED_ACTION";
+
+    /**
      * @hide Broadcast intent when a stream mute state changes.
      * Includes the stream that changed and the new mute state
      *
@@ -196,6 +212,18 @@
         "android.media.EXTRA_PREV_VOLUME_STREAM_VALUE";
 
     /**
+     * @hide The devices associated with the stream for the stream devices changed intent.
+     */
+    public static final String EXTRA_VOLUME_STREAM_DEVICES =
+        "android.media.EXTRA_VOLUME_STREAM_DEVICES";
+
+    /**
+     * @hide The previous devices associated with the stream for the stream devices changed intent.
+     */
+    public static final String EXTRA_PREV_VOLUME_STREAM_DEVICES =
+        "android.media.EXTRA_PREV_VOLUME_STREAM_DEVICES";
+
+    /**
      * @hide The new master volume mute state for the master mute changed intent.
      * Value is boolean
      */
diff --git a/media/java/android/media/AudioPortEventHandler.java b/media/java/android/media/AudioPortEventHandler.java
index c05fd77..c49e8c2 100644
--- a/media/java/android/media/AudioPortEventHandler.java
+++ b/media/java/android/media/AudioPortEventHandler.java
@@ -40,6 +40,12 @@
     private static final int AUDIOPORT_EVENT_SERVICE_DIED = 3;
     private static final int AUDIOPORT_EVENT_NEW_LISTENER = 4;
 
+    /**
+     * Accessed by native methods: JNI Callback context.
+     */
+    @SuppressWarnings("unused")
+    private long mJniCallback;
+
     void init() {
         synchronized (this) {
             if (mHandler != null) {
@@ -63,9 +69,6 @@
                                 listeners = mListeners;
                             }
                         }
-                        if (listeners.isEmpty()) {
-                            return;
-                        }
                         // reset audio port cache if the event corresponds to a change coming
                         // from audio policy service or if mediaserver process died.
                         if (msg.what == AUDIOPORT_EVENT_PORT_LIST_UPDATED ||
@@ -73,6 +76,11 @@
                                 msg.what == AUDIOPORT_EVENT_SERVICE_DIED) {
                             AudioManager.resetAudioPortGeneration();
                         }
+
+                        if (listeners.isEmpty()) {
+                            return;
+                        }
+
                         ArrayList<AudioPort> ports = new ArrayList<AudioPort>();
                         ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
                         if (msg.what != AUDIOPORT_EVENT_SERVICE_DIED) {
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 259fe37..99b7bee 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -20,6 +20,7 @@
 import java.nio.ByteBuffer;
 import java.util.Iterator;
 
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.os.Binder;
 import android.os.Handler;
@@ -313,8 +314,14 @@
 
         audioParamCheck(attributes.getCapturePreset(), rate, encoding);
 
-        mChannelCount = AudioFormat.channelCountFromInChannelMask(format.getChannelMask());
-        mChannelMask = getChannelMaskFromLegacyConfig(format.getChannelMask(), false);
+        int channelMask = AudioFormat.CHANNEL_IN_DEFAULT;
+        if ((format.getPropertySetMask()
+                & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK) != 0)
+        {
+            channelMask = format.getChannelMask();
+        }
+        mChannelCount = AudioFormat.channelCountFromInChannelMask(channelMask);
+        mChannelMask = getChannelMaskFromLegacyConfig(channelMask, false);
 
         audioBuffSizeCheck(bufferSizeInBytes);
 
@@ -335,6 +342,161 @@
         mState = STATE_INITIALIZED;
     }
 
+    /**
+     * Builder class for {@link AudioRecord} objects.
+     * Use this class to configure and create an <code>AudioRecord</code> instance. By setting the
+     * recording preset (a.k.a. recording source) and audio format parameters, you indicate which of
+     *  those vary from the default behavior on the device.
+     * <p> Here is an example where <code>Builder</code> is used to specify all {@link AudioFormat}
+     * parameters, to be used by a new <code>AudioRecord</code> instance:
+     *
+     * <pre class="prettyprint">
+     * AudioRecord recorder = new AudioRecord.Builder()
+     *         .setCapturePreset(MediaRecorder.AudioSource.VOICE_COMMUNICATION)
+     *         .setAudioFormat(new AudioFormat.Builder()
+     *                 .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+     *                 .setSampleRate(32000)
+     *                 .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+     *                 .build())
+     *         .setBufferSize(2*minBuffSize)
+     *         .build();
+     * </pre>
+     * <p>
+     * If the capture preset is not set with {@link #setCapturePreset(int)},
+     * {@link MediaRecorder.AudioSource#DEFAULT} is used.
+     * <br>If the audio format is not specified or is incomplete, its sample rate will be the
+     * default output sample rate of the device (see
+     * {@link AudioManager#PROPERTY_OUTPUT_SAMPLE_RATE}), its channel configuration will be
+     * {@link AudioFormat#CHANNEL_IN_DEFAULT}.
+     * <br>Failing to set an adequate buffer size with {@link #setBufferSizeInBytes(int)} will
+     * prevent the successful creation of an <code>AudioRecord</code> instance.
+     */
+    public static class Builder {
+        private AudioAttributes mAttributes;
+        private AudioFormat mFormat;
+        private int mBufferSizeInBytes;
+        private int mSessionId = AudioManager.AUDIO_SESSION_ID_GENERATE;
+
+        /**
+         * Constructs a new Builder with the default values as described above.
+         */
+        public Builder() {
+        }
+
+        /**
+         * @param preset the capture preset (also referred to as the recording source).
+         * See {@link MediaRecorder.AudioSource} for the supported capture preset definitions.
+         * @return the same Builder instance.
+         * @throws IllegalArgumentException
+         */
+        public Builder setCapturePreset(int preset) throws IllegalArgumentException {
+            if ( (preset < MediaRecorder.AudioSource.DEFAULT) ||
+                    (preset > MediaRecorder.getAudioSourceMax()) ) {
+                throw new IllegalArgumentException("Invalid audio source " + preset);
+            }
+            mAttributes = new AudioAttributes.Builder()
+                    .setInternalCapturePreset(preset)
+                    .build();
+            return this;
+        }
+
+        /**
+         * @hide
+         * To be only used by system components. Allows specifying non-public capture presets
+         * @param attributes a non-null {@link AudioAttributes} instance that contains the capture
+         *     preset to be used.
+         * @return the same Builder instance.
+         * @throws IllegalArgumentException
+         */
+        @SystemApi
+        public Builder setAudioAttributes(@NonNull AudioAttributes attributes)
+                throws IllegalArgumentException {
+            if (attributes == null) {
+                throw new IllegalArgumentException("Illegal null AudioAttributes argument");
+            }
+            if (attributes.getCapturePreset() == MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID) {
+                throw new IllegalArgumentException(
+                        "No valid capture preset in AudioAttributes argument");
+            }
+            // keep reference, we only copy the data when building
+            mAttributes = attributes;
+            return this;
+        }
+
+        /**
+         * Sets the format of the audio data to be captured.
+         * @param format a non-null {@link AudioFormat} instance
+         * @return the same Builder instance.
+         * @throws IllegalArgumentException
+         */
+        public Builder setAudioFormat(@NonNull AudioFormat format) throws IllegalArgumentException {
+            if (format == null) {
+                throw new IllegalArgumentException("Illegal null AudioFormat argument");
+            }
+            // keep reference, we only copy the data when building
+            mFormat = format;
+            return this;
+        }
+
+        /**
+         * Sets the total size (in bytes) of the buffer where audio data is written
+         * during the recording. New audio data can be read from this buffer in smaller chunks
+         * than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum
+         * required buffer size for the successful creation of an AudioRecord instance.
+         * Using values smaller than getMinBufferSize() will result in an initialization failure.
+         * @param bufferSizeInBytes a value strictly greater than 0
+         * @return the same Builder instance.
+         * @throws IllegalArgumentException
+         */
+        public Builder setBufferSizeInBytes(int bufferSizeInBytes) throws IllegalArgumentException {
+            if (bufferSizeInBytes <= 0) {
+                throw new IllegalArgumentException("Invalid buffer size " + bufferSizeInBytes);
+            }
+            mBufferSizeInBytes = bufferSizeInBytes;
+            return this;
+        }
+
+        /**
+         * @hide
+         * To be only used by system components.
+         * @param sessionId ID of audio session the AudioRecord must be attached to, or
+         *     {@link AudioManager#AUDIO_SESSION_ID_GENERATE} if the session isn't known at
+         *     construction time.
+         * @return the same Builder instance.
+         * @throws IllegalArgumentException
+         */
+        @SystemApi
+        public Builder setSessionId(int sessionId) throws IllegalArgumentException {
+            if (sessionId < 0) {
+                throw new IllegalArgumentException("Invalid session ID " + sessionId);
+            }
+            mSessionId = sessionId;
+            return this;
+        }
+
+        /**
+         * @return a new {@link AudioRecord} instance initialized with all the parameters set
+         *     on this <code>Builder</code>
+         * @throws UnsupportedOperationException if the parameters set on the <code>Builder</code>
+         *     were incompatible, or if they are not supported by the device.
+         */
+        public AudioRecord build() throws UnsupportedOperationException {
+            if (mFormat == null) {
+                mFormat = new AudioFormat.Builder().build();
+            }
+            if (mAttributes == null) {
+                mAttributes = new AudioAttributes.Builder()
+                        .setInternalCapturePreset(MediaRecorder.AudioSource.DEFAULT)
+                        .build();
+            }
+            try {
+                return new AudioRecord(mAttributes, mFormat, mBufferSizeInBytes, mSessionId);
+            } catch (IllegalArgumentException e) {
+                throw new UnsupportedOperationException(e.getMessage());
+            }
+        }
+    }
+
     // Convenience method for the constructor's parameter checks.
     // This, getChannelMaskFromLegacyConfig and audioBuffSizeCheck are where constructor
     // IllegalArgumentException-s are thrown
diff --git a/media/java/android/media/MediaDescription.java b/media/java/android/media/MediaDescription.java
index ddbffc2..afc3ca7 100644
--- a/media/java/android/media/MediaDescription.java
+++ b/media/java/android/media/MediaDescription.java
@@ -41,9 +41,13 @@
      * Extras for opaque use by apps/system.
      */
     private final Bundle mExtras;
+    /**
+     * A Uri to identify this content.
+     */
+    private final Uri mMediaUri;
 
     private MediaDescription(String mediaId, CharSequence title, CharSequence subtitle,
-            CharSequence description, Bitmap icon, Uri iconUri, Bundle extras) {
+            CharSequence description, Bitmap icon, Uri iconUri, Bundle extras, Uri mediaUri) {
         mMediaId = mediaId;
         mTitle = title;
         mSubtitle = subtitle;
@@ -51,6 +55,7 @@
         mIcon = icon;
         mIconUri = iconUri;
         mExtras = extras;
+        mMediaUri = mediaUri;
     }
 
     private MediaDescription(Parcel in) {
@@ -61,6 +66,7 @@
         mIcon = in.readParcelable(null);
         mIconUri = in.readParcelable(null);
         mExtras = in.readBundle();
+        mMediaUri = in.readParcelable(null);
     }
 
     /**
@@ -125,6 +131,15 @@
         return mExtras;
     }
 
+    /**
+     * Returns a Uri representing this content or null.
+     *
+     * @return A media Uri or null.
+     */
+    public @Nullable Uri getMediaUri() {
+        return mMediaUri;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -139,6 +154,7 @@
         dest.writeParcelable(mIcon, flags);
         dest.writeParcelable(mIconUri, flags);
         dest.writeBundle(mExtras);
+        dest.writeParcelable(mMediaUri, flags);
     }
 
     @Override
@@ -170,6 +186,7 @@
         private Bitmap mIcon;
         private Uri mIconUri;
         private Bundle mExtras;
+        private Uri mMediaUri;
 
         /**
          * Creates an initially empty builder.
@@ -257,9 +274,20 @@
             return this;
         }
 
+        /**
+         * Sets the media uri.
+         *
+         * @param mediaUri The content's {@link Uri} for the item or null.
+         * @return this
+         */
+        public Builder setMediaUri(@Nullable Uri mediaUri) {
+            mMediaUri = mediaUri;
+            return this;
+        }
+
         public MediaDescription build() {
             return new MediaDescription(mMediaId, mTitle, mSubtitle, mDescription, mIcon, mIconUri,
-                    mExtras);
+                    mExtras, mMediaUri);
         }
     }
 }
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 6b37a34..069f7ff 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -82,6 +82,10 @@
  * encrypted content, the samples returned from the extractor remain encrypted, they
  * are only decrypted when the samples are delivered to the decoder.
  * <p>
+ * MediaDrm methods throw {@link java.lang.IllegalStateException}
+ * when a method is called on a MediaDrm object that is in an invalid or inoperable
+ * state. This is typically due to incorrect application API usage, but may also
+ * be due to an unrecoverable failure in the DRM plugin or security hardware.
  * <a name="Callbacks"></a>
  * <h3>Callbacks</h3>
  * <p>Applications should register for informational events in order
@@ -383,11 +387,27 @@
     public static final int KEY_TYPE_RELEASE = 3;
 
     /**
+     * Key request type is initial license request
+     */
+    public static final int REQUEST_TYPE_INITIAL = 0;
+
+    /**
+     * Key request type is license renewal
+     */
+    public static final int REQUEST_TYPE_RENEWAL = 1;
+
+    /**
+     * Key request type is license release
+     */
+    public static final int REQUEST_TYPE_RELEASE = 2;
+
+    /**
      * Contains the opaque data an app uses to request keys from a license server
      */
     public final static class KeyRequest {
         private byte[] mData;
         private String mDefaultUrl;
+        private int mRequestType;
 
         KeyRequest() {}
 
@@ -402,6 +422,11 @@
          * server URL from other sources.
          */
         public String getDefaultUrl() { return mDefaultUrl; }
+
+        /**
+         * Get the type of the request
+         */
+        public int getRequestType() { return mRequestType; }
     };
 
     /**
@@ -460,7 +485,6 @@
      * reprovisioning is required
      * @throws DeniedByServerException if the response indicates that the
      * server rejected the request
-     * @throws ResourceBusyException if required resources are in use
      */
     public native byte[] provideKeyResponse(byte[] scope, byte[] response)
             throws NotProvisionedException, DeniedByServerException;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index d77fcd8..83954ae 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2602,15 +2602,21 @@
                 return;
 
             case MEDIA_STOPPED:
-                if (mTimeProvider != null) {
-                    mTimeProvider.onStopped();
+                {
+                    TimeProvider timeProvider = mTimeProvider;
+                    if (timeProvider != null) {
+                        timeProvider.onStopped();
+                    }
                 }
                 break;
 
             case MEDIA_STARTED:
             case MEDIA_PAUSED:
-                if (mTimeProvider != null) {
-                    mTimeProvider.onPaused(msg.what == MEDIA_PAUSED);
+                {
+                    TimeProvider timeProvider = mTimeProvider;
+                    if (timeProvider != null) {
+                        timeProvider.onPaused(msg.what == MEDIA_PAUSED);
+                    }
                 }
                 break;
 
@@ -2620,21 +2626,26 @@
                 return;
 
             case MEDIA_SEEK_COMPLETE:
-              if (mOnSeekCompleteListener != null) {
-                  mOnSeekCompleteListener.onSeekComplete(mMediaPlayer);
-              }
-              // fall through
+                if (mOnSeekCompleteListener != null) {
+                    mOnSeekCompleteListener.onSeekComplete(mMediaPlayer);
+                }
+                // fall through
 
             case MEDIA_SKIPPED:
-              if (mTimeProvider != null) {
-                  mTimeProvider.onSeekComplete(mMediaPlayer);
-              }
-              return;
+                {
+                    TimeProvider timeProvider = mTimeProvider;
+                    if (timeProvider != null) {
+                        timeProvider.onSeekComplete(mMediaPlayer);
+                    }
+                }
+                return;
 
             case MEDIA_SET_VIDEO_SIZE:
-              if (mOnVideoSizeChangedListener != null)
-                  mOnVideoSizeChangedListener.onVideoSizeChanged(mMediaPlayer, msg.arg1, msg.arg2);
-              return;
+                if (mOnVideoSizeChangedListener != null) {
+                    mOnVideoSizeChangedListener.onVideoSizeChanged(
+                        mMediaPlayer, msg.arg1, msg.arg2);
+                }
+                return;
 
             case MEDIA_ERROR:
                 Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 58c86f2..058cfd2 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -157,8 +157,11 @@
     }
 
     /**
-     * Defines the audio source. These constants are used with
-     * {@link MediaRecorder#setAudioSource(int)}.
+     * Defines the audio source.
+     * An audio source defines both a default physical source of audio signal, and a recording
+     * configuration; it's also known as a capture preset. These constants are for instance used
+     * in {@link MediaRecorder#setAudioSource(int)} or
+     * {@link AudioRecord.Builder#setCapturePreset(int)}.
      */
     public final class AudioSource {
 
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index b4c612a..c227eb7 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -1500,18 +1500,18 @@
 
         /**
          * The default playback type, "local", indicating the presentation of the media is happening
-         * on the same device (e.g. a phone, a tablet) as where it is controlled from.
+         * on the same device (e&#46;g&#46; a phone, a tablet) as where it is controlled from.
          * @see #getPlaybackType()
          */
         public final static int PLAYBACK_TYPE_LOCAL = 0;
         /**
          * A playback type indicating the presentation of the media is happening on
-         * a different device (i.e. the remote device) than where it is controlled from.
+         * a different device (i&#46;e&#46; the remote device) than where it is controlled from.
          * @see #getPlaybackType()
          */
         public final static int PLAYBACK_TYPE_REMOTE = 1;
         /**
-         * Playback information indicating the playback volume is fixed, i.e. it cannot be
+         * Playback information indicating the playback volume is fixed, i&#46;e&#46; it cannot be
          * controlled from this object. An example of fixed playback volume is a remote player,
          * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
          * than attenuate at the source.
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 1255276..9ea6722 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -553,15 +553,8 @@
                         boolean isimage = MediaFile.isImageFileType(mFileType);
 
                         if (isaudio || isvideo || isimage) {
-                            if (mExternalIsEmulated && path.startsWith(mExternalStoragePath)) {
-                                // try to rewrite the path to bypass the sd card fuse layer
-                                String directPath = Environment.getMediaStorageDirectory() +
-                                        path.substring(mExternalStoragePath.length());
-                                File f = new File(directPath);
-                                if (f.exists()) {
-                                    path = directPath;
-                                }
-                            }
+                            path = Environment.maybeTranslateEmulatedPathToInternal(new File(path))
+                                    .getAbsolutePath();
                         }
 
                         // we only extract metadata for audio and video files
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index db6b38b..88d979e 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -32,7 +32,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemProperties;
 import android.util.AndroidRuntimeException;
 import android.util.Log;
 
@@ -112,7 +111,24 @@
  * resumes.</p>
  */
 public class SoundPool {
-    private final SoundPoolDelegate mImpl;
+    static { System.loadLibrary("soundpool"); }
+
+    // SoundPool messages
+    //
+    // must match SoundPool.h
+    private static final int SAMPLE_LOADED = 1;
+
+    private final static String TAG = "SoundPool";
+    private final static boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private long mNativeContext; // accessed by native methods
+
+    private EventHandler mEventHandler;
+    private SoundPool.OnLoadCompleteListener mOnLoadCompleteListener;
+
+    private final Object mLock;
+    private final AudioAttributes mAttributes;
+    private final IAppOpsService mAppOps;
 
     /**
      * Constructor. Constructs a SoundPool object with the following
@@ -135,10 +151,374 @@
     }
 
     private SoundPool(int maxStreams, AudioAttributes attributes) {
-        if (SystemProperties.getBoolean("config.disable_media", false)) {
-            mImpl = new SoundPoolStub();
+        // do native setup
+        if (native_setup(new WeakReference<SoundPool>(this), maxStreams, attributes) != 0) {
+            throw new RuntimeException("Native setup failed");
+        }
+        mLock = new Object();
+        mAttributes = attributes;
+        IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+        mAppOps = IAppOpsService.Stub.asInterface(b);
+    }
+
+    /**
+     * Release the SoundPool resources.
+     *
+     * Release all memory and native resources used by the SoundPool
+     * object. The SoundPool can no longer be used and the reference
+     * should be set to null.
+     */
+    public native final void release();
+
+    protected void finalize() { release(); }
+
+    /**
+     * Load the sound from the specified path.
+     *
+     * @param path the path to the audio file
+     * @param priority the priority of the sound. Currently has no effect. Use
+     *                 a value of 1 for future compatibility.
+     * @return a sound ID. This value can be used to play or unload the sound.
+     */
+    public int load(String path, int priority) {
+        int id = 0;
+        try {
+            File f = new File(path);
+            ParcelFileDescriptor fd = ParcelFileDescriptor.open(f,
+                    ParcelFileDescriptor.MODE_READ_ONLY);
+            if (fd != null) {
+                id = _load(fd.getFileDescriptor(), 0, f.length(), priority);
+                fd.close();
+            }
+        } catch (java.io.IOException e) {
+            Log.e(TAG, "error loading " + path);
+        }
+        return id;
+    }
+
+    /**
+     * Load the sound from the specified APK resource.
+     *
+     * Note that the extension is dropped. For example, if you want to load
+     * a sound from the raw resource file "explosion.mp3", you would specify
+     * "R.raw.explosion" as the resource ID. Note that this means you cannot
+     * have both an "explosion.wav" and an "explosion.mp3" in the res/raw
+     * directory.
+     * 
+     * @param context the application context
+     * @param resId the resource ID
+     * @param priority the priority of the sound. Currently has no effect. Use
+     *                 a value of 1 for future compatibility.
+     * @return a sound ID. This value can be used to play or unload the sound.
+     */
+    public int load(Context context, int resId, int priority) {
+        AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId);
+        int id = 0;
+        if (afd != null) {
+            id = _load(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength(), priority);
+            try {
+                afd.close();
+            } catch (java.io.IOException ex) {
+                //Log.d(TAG, "close failed:", ex);
+            }
+        }
+        return id;
+    }
+
+    /**
+     * Load the sound from an asset file descriptor.
+     *
+     * @param afd an asset file descriptor
+     * @param priority the priority of the sound. Currently has no effect. Use
+     *                 a value of 1 for future compatibility.
+     * @return a sound ID. This value can be used to play or unload the sound.
+     */
+    public int load(AssetFileDescriptor afd, int priority) {
+        if (afd != null) {
+            long len = afd.getLength();
+            if (len < 0) {
+                throw new AndroidRuntimeException("no length for fd");
+            }
+            return _load(afd.getFileDescriptor(), afd.getStartOffset(), len, priority);
         } else {
-            mImpl = new SoundPoolImpl(this, maxStreams, attributes);
+            return 0;
+        }
+    }
+
+    /**
+     * Load the sound from a FileDescriptor.
+     *
+     * This version is useful if you store multiple sounds in a single
+     * binary. The offset specifies the offset from the start of the file
+     * and the length specifies the length of the sound within the file.
+     *
+     * @param fd a FileDescriptor object
+     * @param offset offset to the start of the sound
+     * @param length length of the sound
+     * @param priority the priority of the sound. Currently has no effect. Use
+     *                 a value of 1 for future compatibility.
+     * @return a sound ID. This value can be used to play or unload the sound.
+     */
+    public int load(FileDescriptor fd, long offset, long length, int priority) {
+        return _load(fd, offset, length, priority);
+    }
+
+    /**
+     * Unload a sound from a sound ID.
+     *
+     * Unloads the sound specified by the soundID. This is the value
+     * returned by the load() function. Returns true if the sound is
+     * successfully unloaded, false if the sound was already unloaded.
+     *
+     * @param soundID a soundID returned by the load() function
+     * @return true if just unloaded, false if previously unloaded
+     */
+    public native final boolean unload(int soundID);
+
+    /**
+     * Play a sound from a sound ID.
+     *
+     * Play the sound specified by the soundID. This is the value 
+     * returned by the load() function. Returns a non-zero streamID
+     * if successful, zero if it fails. The streamID can be used to
+     * further control playback. Note that calling play() may cause
+     * another sound to stop playing if the maximum number of active
+     * streams is exceeded. A loop value of -1 means loop forever,
+     * a value of 0 means don't loop, other values indicate the
+     * number of repeats, e.g. a value of 1 plays the audio twice.
+     * The playback rate allows the application to vary the playback
+     * rate (pitch) of the sound. A value of 1.0 means play back at
+     * the original frequency. A value of 2.0 means play back twice
+     * as fast, and a value of 0.5 means playback at half speed.
+     *
+     * @param soundID a soundID returned by the load() function
+     * @param leftVolume left volume value (range = 0.0 to 1.0)
+     * @param rightVolume right volume value (range = 0.0 to 1.0)
+     * @param priority stream priority (0 = lowest priority)
+     * @param loop loop mode (0 = no loop, -1 = loop forever)
+     * @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0)
+     * @return non-zero streamID if successful, zero if failed
+     */
+    public final int play(int soundID, float leftVolume, float rightVolume,
+            int priority, int loop, float rate) {
+        if (isRestricted()) {
+            leftVolume = rightVolume = 0;
+        }
+        return _play(soundID, leftVolume, rightVolume, priority, loop, rate);
+    }
+
+    /**
+     * Pause a playback stream.
+     *
+     * Pause the stream specified by the streamID. This is the
+     * value returned by the play() function. If the stream is
+     * playing, it will be paused. If the stream is not playing
+     * (e.g. is stopped or was previously paused), calling this
+     * function will have no effect.
+     *
+     * @param streamID a streamID returned by the play() function
+     */
+    public native final void pause(int streamID);
+
+    /**
+     * Resume a playback stream.
+     *
+     * Resume the stream specified by the streamID. This
+     * is the value returned by the play() function. If the stream
+     * is paused, this will resume playback. If the stream was not
+     * previously paused, calling this function will have no effect.
+     *
+     * @param streamID a streamID returned by the play() function
+     */
+    public native final void resume(int streamID);
+
+    /**
+     * Pause all active streams.
+     *
+     * Pause all streams that are currently playing. This function
+     * iterates through all the active streams and pauses any that
+     * are playing. It also sets a flag so that any streams that
+     * are playing can be resumed by calling autoResume().
+     */
+    public native final void autoPause();
+
+    /**
+     * Resume all previously active streams.
+     *
+     * Automatically resumes all streams that were paused in previous
+     * calls to autoPause().
+     */
+    public native final void autoResume();
+
+    /**
+     * Stop a playback stream.
+     *
+     * Stop the stream specified by the streamID. This
+     * is the value returned by the play() function. If the stream
+     * is playing, it will be stopped. It also releases any native
+     * resources associated with this stream. If the stream is not
+     * playing, it will have no effect.
+     *
+     * @param streamID a streamID returned by the play() function
+     */
+    public native final void stop(int streamID);
+
+    /**
+     * Set stream volume.
+     *
+     * Sets the volume on the stream specified by the streamID.
+     * This is the value returned by the play() function. The
+     * value must be in the range of 0.0 to 1.0. If the stream does
+     * not exist, it will have no effect.
+     *
+     * @param streamID a streamID returned by the play() function
+     * @param leftVolume left volume value (range = 0.0 to 1.0)
+     * @param rightVolume right volume value (range = 0.0 to 1.0)
+     */
+    public final void setVolume(int streamID, float leftVolume, float rightVolume) {
+        if (isRestricted()) {
+            return;
+        }
+        _setVolume(streamID, leftVolume, rightVolume);
+    }
+
+    /**
+     * Similar, except set volume of all channels to same value.
+     * @hide
+     */
+    public void setVolume(int streamID, float volume) {
+        setVolume(streamID, volume, volume);
+    }
+
+    /**
+     * Change stream priority.
+     *
+     * Change the priority of the stream specified by the streamID.
+     * This is the value returned by the play() function. Affects the
+     * order in which streams are re-used to play new sounds. If the
+     * stream does not exist, it will have no effect.
+     *
+     * @param streamID a streamID returned by the play() function
+     */
+    public native final void setPriority(int streamID, int priority);
+
+    /**
+     * Set loop mode.
+     *
+     * Change the loop mode. A loop value of -1 means loop forever,
+     * a value of 0 means don't loop, other values indicate the
+     * number of repeats, e.g. a value of 1 plays the audio twice.
+     * If the stream does not exist, it will have no effect.
+     *
+     * @param streamID a streamID returned by the play() function
+     * @param loop loop mode (0 = no loop, -1 = loop forever)
+     */
+    public native final void setLoop(int streamID, int loop);
+
+    /**
+     * Change playback rate.
+     *
+     * The playback rate allows the application to vary the playback
+     * rate (pitch) of the sound. A value of 1.0 means playback at
+     * the original frequency. A value of 2.0 means playback twice
+     * as fast, and a value of 0.5 means playback at half speed.
+     * If the stream does not exist, it will have no effect.
+     *
+     * @param streamID a streamID returned by the play() function
+     * @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0)
+     */
+    public native final void setRate(int streamID, float rate);
+
+    public interface OnLoadCompleteListener {
+        /**
+         * Called when a sound has completed loading.
+         *
+         * @param soundPool SoundPool object from the load() method
+         * @param sampleId the sample ID of the sound loaded.
+         * @param status the status of the load operation (0 = success)
+         */
+        public void onLoadComplete(SoundPool soundPool, int sampleId, int status);
+    }
+
+    /**
+     * Sets the callback hook for the OnLoadCompleteListener.
+     */
+    public void setOnLoadCompleteListener(OnLoadCompleteListener listener) {
+        synchronized(mLock) {
+            if (listener != null) {
+                // setup message handler
+                Looper looper;
+                if ((looper = Looper.myLooper()) != null) {
+                    mEventHandler = new EventHandler(looper);
+                } else if ((looper = Looper.getMainLooper()) != null) {
+                    mEventHandler = new EventHandler(looper);
+                } else {
+                    mEventHandler = null;
+                }
+            } else {
+                mEventHandler = null;
+            }
+            mOnLoadCompleteListener = listener;
+        }
+    }
+
+    private boolean isRestricted() {
+        if ((mAttributes.getFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
+            return false;
+        }
+        try {
+            final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
+                    mAttributes.getUsage(),
+                    Process.myUid(), ActivityThread.currentPackageName());
+            return mode != AppOpsManager.MODE_ALLOWED;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    private native final int _load(FileDescriptor fd, long offset, long length, int priority); 
+
+    private native final int native_setup(Object weakRef, int maxStreams,
+            Object/*AudioAttributes*/ attributes);
+
+    private native final int _play(int soundID, float leftVolume, float rightVolume,
+            int priority, int loop, float rate);
+
+    private native final void _setVolume(int streamID, float leftVolume, float rightVolume);
+
+    // post event from native code to message handler
+    @SuppressWarnings("unchecked")
+    private static void postEventFromNative(Object ref, int msg, int arg1, int arg2, Object obj) {
+        SoundPool soundPool = ((WeakReference<SoundPool>) ref).get();
+        if (soundPool == null)
+            return;
+
+        if (soundPool.mEventHandler != null) {
+            Message m = soundPool.mEventHandler.obtainMessage(msg, arg1, arg2, obj);
+            soundPool.mEventHandler.sendMessage(m);
+        }
+    }
+
+    private final class EventHandler extends Handler {
+        public EventHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+            case SAMPLE_LOADED:
+                if (DEBUG) Log.d(TAG, "Sample " + msg.arg1 + " loaded");
+                synchronized(mLock) {
+                    if (mOnLoadCompleteListener != null) {
+                        mOnLoadCompleteListener.onLoadComplete(SoundPool.this, msg.arg1, msg.arg2);
+                    }
+                }
+                break;
+            default:
+                Log.e(TAG, "Unknown message type " + msg.what);
+                return;
+            }
         }
     }
 
@@ -197,613 +577,4 @@
             return new SoundPool(mMaxStreams, mAudioAttributes);
         }
     }
-
-    /**
-     * Load the sound from the specified path.
-     *
-     * @param path the path to the audio file
-     * @param priority the priority of the sound. Currently has no effect. Use
-     *                 a value of 1 for future compatibility.
-     * @return a sound ID. This value can be used to play or unload the sound.
-     */
-    public int load(String path, int priority) {
-        return mImpl.load(path, priority);
-    }
-
-    /**
-     * Load the sound from the specified APK resource.
-     *
-     * Note that the extension is dropped. For example, if you want to load
-     * a sound from the raw resource file "explosion.mp3", you would specify
-     * "R.raw.explosion" as the resource ID. Note that this means you cannot
-     * have both an "explosion.wav" and an "explosion.mp3" in the res/raw
-     * directory.
-     * 
-     * @param context the application context
-     * @param resId the resource ID
-     * @param priority the priority of the sound. Currently has no effect. Use
-     *                 a value of 1 for future compatibility.
-     * @return a sound ID. This value can be used to play or unload the sound.
-     */
-    public int load(Context context, int resId, int priority) {
-        return mImpl.load(context, resId, priority);
-    }
-
-    /**
-     * Load the sound from an asset file descriptor.
-     *
-     * @param afd an asset file descriptor
-     * @param priority the priority of the sound. Currently has no effect. Use
-     *                 a value of 1 for future compatibility.
-     * @return a sound ID. This value can be used to play or unload the sound.
-     */
-    public int load(AssetFileDescriptor afd, int priority) {
-        return mImpl.load(afd, priority);
-    }
-
-    /**
-     * Load the sound from a FileDescriptor.
-     *
-     * This version is useful if you store multiple sounds in a single
-     * binary. The offset specifies the offset from the start of the file
-     * and the length specifies the length of the sound within the file.
-     *
-     * @param fd a FileDescriptor object
-     * @param offset offset to the start of the sound
-     * @param length length of the sound
-     * @param priority the priority of the sound. Currently has no effect. Use
-     *                 a value of 1 for future compatibility.
-     * @return a sound ID. This value can be used to play or unload the sound.
-     */
-    public int load(FileDescriptor fd, long offset, long length, int priority) {
-        return mImpl.load(fd, offset, length, priority);
-    }
-
-    /**
-     * Unload a sound from a sound ID.
-     *
-     * Unloads the sound specified by the soundID. This is the value
-     * returned by the load() function. Returns true if the sound is
-     * successfully unloaded, false if the sound was already unloaded.
-     *
-     * @param soundID a soundID returned by the load() function
-     * @return true if just unloaded, false if previously unloaded
-     */
-    public final boolean unload(int soundID) {
-        return mImpl.unload(soundID);
-    }
-
-    /**
-     * Play a sound from a sound ID.
-     *
-     * Play the sound specified by the soundID. This is the value 
-     * returned by the load() function. Returns a non-zero streamID
-     * if successful, zero if it fails. The streamID can be used to
-     * further control playback. Note that calling play() may cause
-     * another sound to stop playing if the maximum number of active
-     * streams is exceeded. A loop value of -1 means loop forever,
-     * a value of 0 means don't loop, other values indicate the
-     * number of repeats, e.g. a value of 1 plays the audio twice.
-     * The playback rate allows the application to vary the playback
-     * rate (pitch) of the sound. A value of 1.0 means play back at
-     * the original frequency. A value of 2.0 means play back twice
-     * as fast, and a value of 0.5 means playback at half speed.
-     *
-     * @param soundID a soundID returned by the load() function
-     * @param leftVolume left volume value (range = 0.0 to 1.0)
-     * @param rightVolume right volume value (range = 0.0 to 1.0)
-     * @param priority stream priority (0 = lowest priority)
-     * @param loop loop mode (0 = no loop, -1 = loop forever)
-     * @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0)
-     * @return non-zero streamID if successful, zero if failed
-     */
-    public final int play(int soundID, float leftVolume, float rightVolume,
-            int priority, int loop, float rate) {
-        return mImpl.play(
-            soundID, leftVolume, rightVolume, priority, loop, rate);
-    }
-
-    /**
-     * Pause a playback stream.
-     *
-     * Pause the stream specified by the streamID. This is the
-     * value returned by the play() function. If the stream is
-     * playing, it will be paused. If the stream is not playing
-     * (e.g. is stopped or was previously paused), calling this
-     * function will have no effect.
-     *
-     * @param streamID a streamID returned by the play() function
-     */
-    public final void pause(int streamID) {
-        mImpl.pause(streamID);
-    }
-
-    /**
-     * Resume a playback stream.
-     *
-     * Resume the stream specified by the streamID. This
-     * is the value returned by the play() function. If the stream
-     * is paused, this will resume playback. If the stream was not
-     * previously paused, calling this function will have no effect.
-     *
-     * @param streamID a streamID returned by the play() function
-     */
-    public final void resume(int streamID) {
-        mImpl.resume(streamID);
-    }
-
-    /**
-     * Pause all active streams.
-     *
-     * Pause all streams that are currently playing. This function
-     * iterates through all the active streams and pauses any that
-     * are playing. It also sets a flag so that any streams that
-     * are playing can be resumed by calling autoResume().
-     */
-    public final void autoPause() {
-        mImpl.autoPause();
-    }
-
-    /**
-     * Resume all previously active streams.
-     *
-     * Automatically resumes all streams that were paused in previous
-     * calls to autoPause().
-     */
-    public final void autoResume() {
-        mImpl.autoResume();
-    }
-
-    /**
-     * Stop a playback stream.
-     *
-     * Stop the stream specified by the streamID. This
-     * is the value returned by the play() function. If the stream
-     * is playing, it will be stopped. It also releases any native
-     * resources associated with this stream. If the stream is not
-     * playing, it will have no effect.
-     *
-     * @param streamID a streamID returned by the play() function
-     */
-    public final void stop(int streamID) {
-        mImpl.stop(streamID);
-    }
-
-    /**
-     * Set stream volume.
-     *
-     * Sets the volume on the stream specified by the streamID.
-     * This is the value returned by the play() function. The
-     * value must be in the range of 0.0 to 1.0. If the stream does
-     * not exist, it will have no effect.
-     *
-     * @param streamID a streamID returned by the play() function
-     * @param leftVolume left volume value (range = 0.0 to 1.0)
-     * @param rightVolume right volume value (range = 0.0 to 1.0)
-     */
-    public final void setVolume(int streamID,
-            float leftVolume, float rightVolume) {
-        mImpl.setVolume(streamID, leftVolume, rightVolume);
-    }
-
-    /**
-     * Similar, except set volume of all channels to same value.
-     * @hide
-     */
-    public void setVolume(int streamID, float volume) {
-        setVolume(streamID, volume, volume);
-    }
-
-    /**
-     * Change stream priority.
-     *
-     * Change the priority of the stream specified by the streamID.
-     * This is the value returned by the play() function. Affects the
-     * order in which streams are re-used to play new sounds. If the
-     * stream does not exist, it will have no effect.
-     *
-     * @param streamID a streamID returned by the play() function
-     */
-    public final void setPriority(int streamID, int priority) {
-        mImpl.setPriority(streamID, priority);
-    }
-
-    /**
-     * Set loop mode.
-     *
-     * Change the loop mode. A loop value of -1 means loop forever,
-     * a value of 0 means don't loop, other values indicate the
-     * number of repeats, e.g. a value of 1 plays the audio twice.
-     * If the stream does not exist, it will have no effect.
-     *
-     * @param streamID a streamID returned by the play() function
-     * @param loop loop mode (0 = no loop, -1 = loop forever)
-     */
-    public final void setLoop(int streamID, int loop) {
-        mImpl.setLoop(streamID, loop);
-    }
-
-    /**
-     * Change playback rate.
-     *
-     * The playback rate allows the application to vary the playback
-     * rate (pitch) of the sound. A value of 1.0 means playback at
-     * the original frequency. A value of 2.0 means playback twice
-     * as fast, and a value of 0.5 means playback at half speed.
-     * If the stream does not exist, it will have no effect.
-     *
-     * @param streamID a streamID returned by the play() function
-     * @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0)
-     */
-    public final void setRate(int streamID, float rate) {
-        mImpl.setRate(streamID, rate);
-    }
-
-    public interface OnLoadCompleteListener {
-        /**
-         * Called when a sound has completed loading.
-         *
-         * @param soundPool SoundPool object from the load() method
-         * @param sampleId the sample ID of the sound loaded.
-         * @param status the status of the load operation (0 = success)
-         */
-        public void onLoadComplete(SoundPool soundPool, int sampleId, int status);
-    }
-
-    /**
-     * Sets the callback hook for the OnLoadCompleteListener.
-     */
-    public void setOnLoadCompleteListener(OnLoadCompleteListener listener) {
-        mImpl.setOnLoadCompleteListener(listener);
-    }
-
-    /**
-     * Release the SoundPool resources.
-     *
-     * Release all memory and native resources used by the SoundPool
-     * object. The SoundPool can no longer be used and the reference
-     * should be set to null.
-     */
-    public final void release() {
-        mImpl.release();
-    }
-
-    /**
-     * Interface for SoundPool implementations.
-     * SoundPool is statically referenced and unconditionally called from all
-     * over the framework, so we can't simply omit the class or make it throw
-     * runtime exceptions, as doing so would break the framework. Instead we
-     * now select either a real or no-op impl object based on whether media is
-     * enabled.
-     *
-     * @hide
-     */
-    /* package */ interface SoundPoolDelegate {
-        public int load(String path, int priority);
-        public int load(Context context, int resId, int priority);
-        public int load(AssetFileDescriptor afd, int priority);
-        public int load(
-                FileDescriptor fd, long offset, long length, int priority);
-        public boolean unload(int soundID);
-        public int play(
-                int soundID, float leftVolume, float rightVolume,
-                int priority, int loop, float rate);
-        public void pause(int streamID);
-        public void resume(int streamID);
-        public void autoPause();
-        public void autoResume();
-        public void stop(int streamID);
-        public void setVolume(int streamID, float leftVolume, float rightVolume);
-        public void setVolume(int streamID, float volume);
-        public void setPriority(int streamID, int priority);
-        public void setLoop(int streamID, int loop);
-        public void setRate(int streamID, float rate);
-        public void setOnLoadCompleteListener(OnLoadCompleteListener listener);
-        public void release();
-    }
-
-
-    /**
-     * Real implementation of the delegate interface. This was formerly the
-     * body of SoundPool itself.
-     */
-    /* package */ static class SoundPoolImpl implements SoundPoolDelegate {
-        static { System.loadLibrary("soundpool"); }
-
-        private final static String TAG = "SoundPool";
-        private final static boolean DEBUG = false;
-
-        private long mNativeContext; // accessed by native methods
-
-        private EventHandler mEventHandler;
-        private SoundPool.OnLoadCompleteListener mOnLoadCompleteListener;
-        private SoundPool mProxy;
-
-        private final Object mLock;
-        private final AudioAttributes mAttributes;
-        private final IAppOpsService mAppOps;
-
-        // SoundPool messages
-        //
-        // must match SoundPool.h
-        private static final int SAMPLE_LOADED = 1;
-
-        public SoundPoolImpl(SoundPool proxy, int maxStreams, AudioAttributes attr) {
-
-            // do native setup
-            if (native_setup(new WeakReference(this), maxStreams, attr) != 0) {
-                throw new RuntimeException("Native setup failed");
-            }
-            mLock = new Object();
-            mProxy = proxy;
-            mAttributes = attr;
-            IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
-            mAppOps = IAppOpsService.Stub.asInterface(b);
-        }
-
-        public int load(String path, int priority)
-        {
-            int id = 0;
-            try {
-                File f = new File(path);
-                ParcelFileDescriptor fd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
-                if (fd != null) {
-                    id = _load(fd.getFileDescriptor(), 0, f.length(), priority);
-                    fd.close();
-                }
-            } catch (java.io.IOException e) {
-                Log.e(TAG, "error loading " + path);
-            }
-            return id;
-        }
-
-        @Override
-        public int load(Context context, int resId, int priority) {
-            AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId);
-            int id = 0;
-            if (afd != null) {
-                id = _load(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength(), priority);
-                try {
-                    afd.close();
-                } catch (java.io.IOException ex) {
-                    //Log.d(TAG, "close failed:", ex);
-                }
-            }
-            return id;
-        }
-
-        @Override
-        public int load(AssetFileDescriptor afd, int priority) {
-            if (afd != null) {
-                long len = afd.getLength();
-                if (len < 0) {
-                    throw new AndroidRuntimeException("no length for fd");
-                }
-                return _load(afd.getFileDescriptor(), afd.getStartOffset(), len, priority);
-            } else {
-                return 0;
-            }
-        }
-
-        @Override
-        public int load(FileDescriptor fd, long offset, long length, int priority) {
-            return _load(fd, offset, length, priority);
-        }
-
-        private native final int _load(FileDescriptor fd, long offset, long length, int priority);
-
-        @Override
-        public native final boolean unload(int soundID);
-
-        @Override
-        public final int play(int soundID, float leftVolume, float rightVolume,
-                int priority, int loop, float rate) {
-            if (isRestricted()) {
-                leftVolume = rightVolume = 0;
-            }
-            return _play(soundID, leftVolume, rightVolume, priority, loop, rate);
-        }
-
-        public native final int _play(int soundID, float leftVolume, float rightVolume,
-                int priority, int loop, float rate);
-
-        private boolean isRestricted() {
-            if ((mAttributes.getFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
-                return false;
-            }
-            try {
-                final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
-                        mAttributes.getUsage(),
-                        Process.myUid(), ActivityThread.currentPackageName());
-                return mode != AppOpsManager.MODE_ALLOWED;
-            } catch (RemoteException e) {
-                return false;
-            }
-        }
-
-        @Override
-        public native final void pause(int streamID);
-
-        @Override
-        public native final void resume(int streamID);
-
-        @Override
-        public native final void autoPause();
-
-        @Override
-        public native final void autoResume();
-
-        @Override
-        public native final void stop(int streamID);
-
-        @Override
-        public final void setVolume(int streamID, float leftVolume, float rightVolume) {
-            if (isRestricted()) {
-                return;
-            }
-            _setVolume(streamID, leftVolume, rightVolume);
-        }
-
-        private native final void _setVolume(int streamID, float leftVolume, float rightVolume);
-
-        @Override
-        public void setVolume(int streamID, float volume) {
-            setVolume(streamID, volume, volume);
-        }
-
-        @Override
-        public native final void setPriority(int streamID, int priority);
-
-        @Override
-        public native final void setLoop(int streamID, int loop);
-
-        @Override
-        public native final void setRate(int streamID, float rate);
-
-        @Override
-        public void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener)
-        {
-            synchronized(mLock) {
-                if (listener != null) {
-                    // setup message handler
-                    Looper looper;
-                    if ((looper = Looper.myLooper()) != null) {
-                        mEventHandler = new EventHandler(mProxy, looper);
-                    } else if ((looper = Looper.getMainLooper()) != null) {
-                        mEventHandler = new EventHandler(mProxy, looper);
-                    } else {
-                        mEventHandler = null;
-                    }
-                } else {
-                    mEventHandler = null;
-                }
-                mOnLoadCompleteListener = listener;
-            }
-        }
-
-        private class EventHandler extends Handler
-        {
-            private SoundPool mSoundPool;
-
-            public EventHandler(SoundPool soundPool, Looper looper) {
-                super(looper);
-                mSoundPool = soundPool;
-            }
-
-            @Override
-            public void handleMessage(Message msg) {
-                switch(msg.what) {
-                case SAMPLE_LOADED:
-                    if (DEBUG) Log.d(TAG, "Sample " + msg.arg1 + " loaded");
-                    synchronized(mLock) {
-                        if (mOnLoadCompleteListener != null) {
-                            mOnLoadCompleteListener.onLoadComplete(mSoundPool, msg.arg1, msg.arg2);
-                        }
-                    }
-                    break;
-                default:
-                    Log.e(TAG, "Unknown message type " + msg.what);
-                    return;
-                }
-            }
-        }
-
-        // post event from native code to message handler
-        private static void postEventFromNative(Object weakRef, int msg, int arg1, int arg2, Object obj)
-        {
-            SoundPoolImpl soundPoolImpl = (SoundPoolImpl)((WeakReference)weakRef).get();
-            if (soundPoolImpl == null)
-                return;
-
-            if (soundPoolImpl.mEventHandler != null) {
-                Message m = soundPoolImpl.mEventHandler.obtainMessage(msg, arg1, arg2, obj);
-                soundPoolImpl.mEventHandler.sendMessage(m);
-            }
-        }
-
-        public native final void release();
-
-        private native final int native_setup(Object weakRef, int maxStreams,
-                Object/*AudioAttributes*/ attributes);
-
-        protected void finalize() { release(); }
-    }
-
-    /**
-     * No-op implementation of SoundPool.
-     * Used when media is disabled by the system.
-     * @hide
-     */
-    /* package */ static class SoundPoolStub implements SoundPoolDelegate {
-        public SoundPoolStub() { }
-
-        public int load(String path, int priority) {
-            return 0;
-        }
-
-        @Override
-        public int load(Context context, int resId, int priority) {
-            return 0;
-        }
-
-        @Override
-        public int load(AssetFileDescriptor afd, int priority) {
-            return 0;
-        }
-
-        @Override
-        public int load(FileDescriptor fd, long offset, long length, int priority) {
-            return 0;
-        }
-
-        @Override
-        public final boolean unload(int soundID) {
-            return true;
-        }
-
-        @Override
-        public final int play(int soundID, float leftVolume, float rightVolume,
-                int priority, int loop, float rate) {
-            return 0;
-        }
-
-        @Override
-        public final void pause(int streamID) { }
-
-        @Override
-        public final void resume(int streamID) { }
-
-        @Override
-        public final void autoPause() { }
-
-        @Override
-        public final void autoResume() { }
-
-        @Override
-        public final void stop(int streamID) { }
-
-        @Override
-        public final void setVolume(int streamID,
-                float leftVolume, float rightVolume) { }
-
-        @Override
-        public void setVolume(int streamID, float volume) {
-        }
-
-        @Override
-        public final void setPriority(int streamID, int priority) { }
-
-        @Override
-        public final void setLoop(int streamID, int loop) { }
-
-        @Override
-        public final void setRate(int streamID, float rate) { }
-
-        @Override
-        public void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener) {
-        }
-
-        @Override
-        public final void release() { }
-    }
 }
diff --git a/media/java/android/media/midi/MidiDeviceServer.java b/media/java/android/media/midi/MidiDeviceServer.java
index d27351f..bc85f92 100644
--- a/media/java/android/media/midi/MidiDeviceServer.java
+++ b/media/java/android/media/midi/MidiDeviceServer.java
@@ -24,6 +24,8 @@
 import android.system.OsConstants;
 import android.util.Log;
 
+import com.android.internal.midi.MidiDispatcher;
+
 import dalvik.system.CloseGuard;
 
 import libcore.io.IoUtils;
diff --git a/media/java/android/media/midi/MidiOutputPort.java b/media/java/android/media/midi/MidiOutputPort.java
index b8ed36f..0290a76 100644
--- a/media/java/android/media/midi/MidiOutputPort.java
+++ b/media/java/android/media/midi/MidiOutputPort.java
@@ -21,6 +21,8 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.internal.midi.MidiDispatcher;
+
 import dalvik.system.CloseGuard;
 
 import libcore.io.IoUtils;
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 49087b0..adb6b06 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -15,8 +15,8 @@
 
 package android.media.session;
 
-import android.media.Rating;
 import android.content.Intent;
+import android.media.Rating;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.ResultReceiver;
@@ -30,8 +30,9 @@
 
     // These callbacks are for the TransportPerformer
     void onPlay();
-    void onPlayFromMediaId(String uri, in Bundle extras);
+    void onPlayFromMediaId(String mediaId, in Bundle extras);
     void onPlayFromSearch(String query, in Bundle extras);
+    void onPlayFromUri(in Uri uri, in Bundle extras);
     void onSkipToTrack(long id);
     void onPause();
     void onStop();
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index e2d06d3..8d58a60 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -23,9 +23,9 @@
 import android.media.routing.IMediaRouterDelegate;
 import android.media.routing.IMediaRouterStateCallback;
 import android.media.session.ISessionControllerCallback;
+import android.media.session.MediaSession;
 import android.media.session.ParcelableVolumeInfo;
 import android.media.session.PlaybackState;
-import android.media.session.MediaSession;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.ResultReceiver;
@@ -55,8 +55,9 @@
 
     // These commands are for the TransportControls
     void play();
-    void playFromMediaId(String uri, in Bundle extras);
+    void playFromMediaId(String mediaId, in Bundle extras);
     void playFromSearch(String string, in Bundle extras);
+    void playFromUri(in Uri uri, in Bundle extras);
     void skipToQueueItem(long id);
     void pause();
     void stop();
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index c23a139..dd81a22 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -516,8 +516,8 @@
     }
 
     /**
-     * Callback for receiving updates on from the session. A Callback can be
-     * registered using {@link #registerCallback}
+     * Callback for receiving updates from the session. A Callback can be
+     * registered using {@link #registerCallback}.
      */
     public static abstract class Callback {
         /**
@@ -615,9 +615,9 @@
         }
 
         /**
-         * Request that the player start playback for a specific {@link Uri}.
+         * Request that the player start playback for a specific media id.
          *
-         * @param mediaId The uri of the requested media.
+         * @param mediaId The id of the requested media.
          * @param extras Optional extras that can include extra information about the media item
          *               to be played.
          */
@@ -656,6 +656,25 @@
         }
 
         /**
+         * Request that the player start playback for a specific {@link Uri}.
+         *
+         * @param uri  The URI of the requested media.
+         * @param extras Optional extras that can include extra information about the media item
+         *               to be played.
+         */
+        public void playFromUri(Uri uri, Bundle extras) {
+            if (uri == null || Uri.EMPTY.equals(uri)) {
+                throw new IllegalArgumentException(
+                        "You must specify a non-empty Uri for playFromUri.");
+            }
+            try {
+                mSessionBinder.playFromUri(uri, extras);
+            } catch (RemoteException e) {
+                Log.wtf(TAG, "Error calling play(" + uri + ").", e);
+            }
+        }
+
+        /**
          * Play an item with a specific id in the play queue. If you specify an
          * id that is not in the play queue, the behavior is undefined.
          */
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index cc602c9..cee82b4 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -30,6 +30,7 @@
 import android.media.Rating;
 import android.media.VolumeProvider;
 import android.media.routing.MediaRouter;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -541,6 +542,10 @@
         postToCallback(CallbackMessageHandler.MSG_PLAY_SEARCH, query, extras);
     }
 
+    private void dispatchPlayFromUri(Uri uri, Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_PLAY_URI, uri, extras);
+    }
+
     private void dispatchSkipToItem(long id) {
         postToCallback(CallbackMessageHandler.MSG_SKIP_TO_ITEM, id);
     }
@@ -833,6 +838,12 @@
         }
 
         /**
+         * Override to handle requests to play a specific media item represented by a URI.
+         */
+        public void onPlayFromUri(Uri uri, Bundle extras) {
+        }
+
+        /**
          * Override to handle requests to play an item with a given id from the
          * play queue.
          */
@@ -961,6 +972,14 @@
         }
 
         @Override
+        public void onPlayFromUri(Uri uri, Bundle extras) {
+            MediaSession session = mMediaSession.get();
+            if (session != null) {
+                session.dispatchPlayFromUri(uri, extras);
+            }
+        }
+
+        @Override
         public void onSkipToTrack(long id) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
@@ -1171,6 +1190,7 @@
         private static final int MSG_COMMAND = 15;
         private static final int MSG_ADJUST_VOLUME = 16;
         private static final int MSG_SET_VOLUME = 17;
+        private static final int MSG_PLAY_URI = 18;
 
         private MediaSession.Callback mCallback;
 
@@ -1210,6 +1230,9 @@
                 case MSG_PLAY_SEARCH:
                     mCallback.onPlayFromSearch((String) msg.obj, msg.getData());
                     break;
+                case MSG_PLAY_URI:
+                    mCallback.onPlayFromUri((Uri) msg.obj, msg.getData());
+                    break;
                 case MSG_SKIP_TO_ITEM:
                     mCallback.onSkipToQueueItem((Long) msg.obj);
                     break;
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 6807e7f..bbe04b5 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -126,6 +126,13 @@
     public static final long ACTION_SKIP_TO_QUEUE_ITEM = 1 << 12;
 
     /**
+     * Indicates this session supports the play from URI command.
+     *
+     * @see Builder#setActions(long)
+     */
+    public static final long ACTION_PLAY_FROM_URI = 1 << 13;
+
+    /**
      * This is the default playback state and indicates that no media has been
      * added yet, or the performer has been reset and has no content to play.
      *
@@ -353,6 +360,11 @@
      * <li> {@link PlaybackState#ACTION_SKIP_TO_NEXT}</li>
      * <li> {@link PlaybackState#ACTION_SEEK_TO}</li>
      * <li> {@link PlaybackState#ACTION_SET_RATING}</li>
+     * <li> {@link PlaybackState#ACTION_PLAY_PAUSE}</li>
+     * <li> {@link PlaybackState#ACTION_PLAY_FROM_MEDIA_ID}</li>
+     * <li> {@link PlaybackState#ACTION_PLAY_FROM_SEARCH}</li>
+     * <li> {@link PlaybackState#ACTION_SKIP_TO_QUEUE_ITEM}</li>
+     * <li> {@link PlaybackState#ACTION_PLAY_FROM_URI}</li>
      * </ul>
      */
     public long getActions() {
@@ -868,6 +880,11 @@
          * <li> {@link PlaybackState#ACTION_SKIP_TO_NEXT}</li>
          * <li> {@link PlaybackState#ACTION_SEEK_TO}</li>
          * <li> {@link PlaybackState#ACTION_SET_RATING}</li>
+         * <li> {@link PlaybackState#ACTION_PLAY_PAUSE}</li>
+         * <li> {@link PlaybackState#ACTION_PLAY_FROM_MEDIA_ID}</li>
+         * <li> {@link PlaybackState#ACTION_PLAY_FROM_SEARCH}</li>
+         * <li> {@link PlaybackState#ACTION_SKIP_TO_QUEUE_ITEM}</li>
+         * <li> {@link PlaybackState#ACTION_PLAY_FROM_URI}</li>
          * </ul>
          *
          * @param actions The set of actions allowed.
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 438e767..936762c 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -1054,6 +1054,50 @@
         public static final String COLUMN_INTERNAL_PROVIDER_DATA = "internal_provider_data";
 
         /**
+         * Internal integer flag used by individual TV input services.
+         * <p>
+         * This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         * </p><p>
+         * Type: INTEGER
+         * </p>
+         */
+        public static final String COLUMN_INTERNAL_PROVIDER_FLAG1 = "internal_provider_flag1";
+
+        /**
+         * Internal integer flag used by individual TV input services.
+         * <p>
+         * This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         * </p><p>
+         * Type: INTEGER
+         * </p>
+         */
+        public static final String COLUMN_INTERNAL_PROVIDER_FLAG2 = "internal_provider_flag2";
+
+        /**
+         * Internal integer flag used by individual TV input services.
+         * <p>
+         * This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         * </p><p>
+         * Type: INTEGER
+         * </p>
+         */
+        public static final String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3";
+
+        /**
+         * Internal integer flag used by individual TV input services.
+         * <p>
+         * This is internal to the provider that inserted it, and should not be decoded by other
+         * apps.
+         * </p><p>
+         * Type: INTEGER
+         * </p>
+         */
+        public static final String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4";
+
+        /**
          * The version number of this row entry used by TV input services.
          * <p>
          * This is best used by sync adapters to identify the rows to update. The number can be
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index cf1b441..8ed383a 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1077,12 +1077,19 @@
         int dispatchInputEvent(InputEvent event, InputEventReceiver receiver) {
             if (DEBUG) Log.d(TAG, "dispatchInputEvent(" + event + ")");
             boolean isNavigationKey = false;
+            boolean skipDispatchToOverlayView = false;
             if (event instanceof KeyEvent) {
                 KeyEvent keyEvent = (KeyEvent) event;
-                isNavigationKey = isNavigationKey(keyEvent.getKeyCode());
                 if (keyEvent.dispatch(this, mDispatcherState, this)) {
                     return TvInputManager.Session.DISPATCH_HANDLED;
                 }
+                isNavigationKey = isNavigationKey(keyEvent.getKeyCode());
+                // When media keys and KEYCODE_MEDIA_AUDIO_TRACK are dispatched to ViewRootImpl,
+                // ViewRootImpl always consumes the keys. In this case, an application loses
+                // a chance to handle media keys. Therefore, media keys are not dispatched to
+                // ViewRootImpl.
+                skipDispatchToOverlayView = KeyEvent.isMediaKey(keyEvent.getKeyCode())
+                        || keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK;
             } else if (event instanceof MotionEvent) {
                 MotionEvent motionEvent = (MotionEvent) event;
                 final int source = motionEvent.getSource();
@@ -1100,7 +1107,8 @@
                     }
                 }
             }
-            if (mOverlayViewContainer == null || !mOverlayViewContainer.isAttachedToWindow()) {
+            if (mOverlayViewContainer == null || !mOverlayViewContainer.isAttachedToWindow()
+                    || skipDispatchToOverlayView) {
                 return TvInputManager.Session.DISPATCH_NOT_HANDLED;
             }
             if (!mOverlayViewContainer.hasWindowFocus()) {
@@ -1223,6 +1231,8 @@
                     args.arg2 = mProxySession;
                     args.arg3 = mProxySessionCallback;
                     args.arg4 = session.getToken();
+                    session.tune(TvContract.buildChannelUriForPassthroughInput(
+                            getHardwareInputId()));
                 } else {
                     args.arg1 = null;
                     args.arg2 = null;
@@ -1232,7 +1242,6 @@
                 }
                 mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED, args)
                         .sendToTarget();
-                session.tune(TvContract.buildChannelUriForPassthroughInput(getHardwareInputId()));
             }
 
             @Override
diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java
index e20eabc..7b8102b 100644
--- a/media/java/android/mtp/MtpStorage.java
+++ b/media/java/android/mtp/MtpStorage.java
@@ -59,7 +59,7 @@
      *
      * @return the storage ID
      */
-    public static int getStorageId(int index) {
+    public static int getStorageIdForIndex(int index) {
         // storage ID is 0x00010001 for primary storage,
         // then 0x00020001, 0x00030001, etc. for secondary storages
         return ((index + 1) << 16) + 1;
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 9fc7e8e..708c083 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -431,6 +431,19 @@
             pData = buffer->data;
             dataSize = buffer->stride * buffer->height;
             break;
+        case HAL_PIXEL_FORMAT_RAW12:
+            // Single plane 10bpp bayer data.
+            ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
+            LOG_ALWAYS_FATAL_IF(buffer->width % 4,
+                                "Width is not multiple of 4 %d", buffer->width);
+            LOG_ALWAYS_FATAL_IF(buffer->height % 2,
+                                "Height is not even %d", buffer->height);
+            LOG_ALWAYS_FATAL_IF(buffer->stride < (buffer->width * 12 / 8),
+                                "stride (%d) should be at least %d",
+                                buffer->stride, buffer->width * 12 / 8);
+            pData = buffer->data;
+            dataSize = buffer->stride * buffer->height;
+            break;
         case HAL_PIXEL_FORMAT_RGBA_8888:
         case HAL_PIXEL_FORMAT_RGBX_8888:
             // Single plane, 32bpp.
@@ -492,8 +505,10 @@
             break;
         case HAL_PIXEL_FORMAT_BLOB:
         case HAL_PIXEL_FORMAT_RAW10:
-            // Blob is used for JPEG data, RAW10 is used for 10-bit raw data, they are
-            // single plane, row and pixel strides are 0.
+        case HAL_PIXEL_FORMAT_RAW12:
+            // Blob is used for JPEG data, RAW10 and RAW12 is used for 10-bit and 12-bit raw data,
+            // those are single plane data with pixel stride 0 since they don't really have a
+            // well defined pixel stride
             ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
             pixelStride = 0;
             break;
@@ -549,12 +564,14 @@
             rowStride = (idx == 0) ? buffer->stride : ALIGN(buffer->stride / 2, 16);
             break;
         case HAL_PIXEL_FORMAT_BLOB:
-            // Blob is used for JPEG data, RAW10 is used for 10-bit raw data, they are
-            // single plane, row and pixel strides are 0.
+            // Blob is used for JPEG data. It is single plane and has 0 row stride and
+            // 0 pixel stride
             ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
             rowStride = 0;
             break;
         case HAL_PIXEL_FORMAT_RAW10:
+        case HAL_PIXEL_FORMAT_RAW12:
+            // RAW10 and RAW12 are used for 10-bit and 12-bit raw data, they are single plane
             ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
             rowStride = buffer->stride;
             break;
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index 1433c79..d10df3e 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -435,7 +435,7 @@
     Image_unlockIfLocked(env, image);
 
     // Set timestamp
-    ALOGV("timestamp to be queued: %lld", timestampNs);
+    ALOGV("timestamp to be queued: %" PRId64, timestampNs);
     res = native_window_set_buffers_timestamp(anw.get(), timestampNs);
     if (res != OK) {
         jniThrowRuntimeException(env, "Set timestamp failed");
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 8302a34..96d7133 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -59,6 +59,7 @@
 struct RequestFields {
     jfieldID data;
     jfieldID defaultUrl;
+    jfieldID requestType;
 };
 
 struct ArrayListFields {
@@ -101,6 +102,12 @@
     jint kKeyTypeRelease;
 } gKeyTypes;
 
+struct KeyRequestTypes {
+    jint kKeyRequestTypeInitial;
+    jint kKeyRequestTypeRenewal;
+    jint kKeyRequestTypeRelease;
+} gKeyRequestTypes;
+
 struct CertificateTypes {
     jint kCertificateTypeNone;
     jint kCertificateTypeX509;
@@ -182,7 +189,7 @@
     jint jeventType;
 
     // translate DrmPlugin event types into their java equivalents
-    switch(eventType) {
+    switch (eventType) {
         case DrmPlugin::kDrmPluginEventProvisionRequired:
             jeventType = gEventTypes.kEventProvisionRequired;
             break;
@@ -236,7 +243,7 @@
 
     const char *drmMessage = NULL;
 
-    switch(err) {
+    switch (err) {
     case ERROR_DRM_UNKNOWN:
         drmMessage = "General DRM error";
         break;
@@ -587,6 +594,13 @@
     GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_RELEASE", "I");
     gKeyTypes.kKeyTypeRelease = env->GetStaticIntField(clazz, field);
 
+    GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_INITIAL", "I");
+    gKeyRequestTypes.kKeyRequestTypeInitial = env->GetStaticIntField(clazz, field);
+    GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_RENEWAL", "I");
+    gKeyRequestTypes.kKeyRequestTypeRenewal = env->GetStaticIntField(clazz, field);
+    GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_RELEASE", "I");
+    gKeyRequestTypes.kKeyRequestTypeRelease = env->GetStaticIntField(clazz, field);
+
     GET_STATIC_FIELD_ID(field, clazz, "CERTIFICATE_TYPE_NONE", "I");
     gCertificateTypes.kCertificateTypeNone = env->GetStaticIntField(clazz, field);
     GET_STATIC_FIELD_ID(field, clazz, "CERTIFICATE_TYPE_X509", "I");
@@ -595,6 +609,7 @@
     FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest");
     GET_FIELD_ID(gFields.keyRequest.data, clazz, "mData", "[B");
     GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;");
+    GET_FIELD_ID(gFields.keyRequest.requestType, clazz, "mRequestType", "I");
 
     FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest");
     GET_FIELD_ID(gFields.provisionRequest.data, clazz, "mData", "[B");
@@ -786,9 +801,10 @@
 
     Vector<uint8_t> request;
     String8 defaultUrl;
+    DrmPlugin::KeyRequestType keyRequestType;
 
     status_t err = drm->getKeyRequest(sessionId, initData, mimeType,
-                                          keyType, optParams, request, defaultUrl);
+            keyType, optParams, request, defaultUrl, &keyRequestType);
 
     if (throwExceptionAsNecessary(env, err, "Failed to get key request")) {
         return NULL;
@@ -807,6 +823,25 @@
 
         jstring jdefaultUrl = env->NewStringUTF(defaultUrl.string());
         env->SetObjectField(keyObj, gFields.keyRequest.defaultUrl, jdefaultUrl);
+
+        switch (keyRequestType) {
+            case DrmPlugin::kKeyRequestType_Initial:
+                env->SetIntField(keyObj, gFields.keyRequest.requestType,
+                        gKeyRequestTypes.kKeyRequestTypeInitial);
+                break;
+            case DrmPlugin::kKeyRequestType_Renewal:
+                env->SetIntField(keyObj, gFields.keyRequest.requestType,
+                        gKeyRequestTypes.kKeyRequestTypeRenewal);
+                break;
+            case DrmPlugin::kKeyRequestType_Release:
+                env->SetIntField(keyObj, gFields.keyRequest.requestType,
+                        gKeyRequestTypes.kKeyRequestTypeRelease);
+                break;
+            case DrmPlugin::kKeyRequestType_Unknown:
+                throwStateException(env, "DRM plugin failure: unknown key request type",
+                        ERROR_DRM_UNKNOWN);
+                break;
+        }
     }
 
     return keyObj;
diff --git a/media/jni/soundpool/Android.mk b/media/jni/soundpool/Android.mk
index 71ab013..2476056 100644
--- a/media/jni/soundpool/Android.mk
+++ b/media/jni/soundpool/Android.mk
@@ -2,7 +2,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
-    android_media_SoundPool_SoundPoolImpl.cpp \
+    android_media_SoundPool.cpp \
     SoundPool.cpp \
     SoundPoolThread.cpp
 
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 10233f3..25c6154 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -716,7 +716,15 @@
         }
 #endif
 
-        if (!mAudioTrack.get() || mPrevSampleID != sample->sampleID()) {
+        // check if the existing track has the same sample id.
+        if (mAudioTrack != 0 && mPrevSampleID == sample->sampleID()) {
+            // the sample rate may fail to change if the audio track is a fast track.
+            if (mAudioTrack->setSampleRate(sampleRate) == NO_ERROR) {
+                newTrack = mAudioTrack;
+                ALOGV("reusing track %p for sample %d", mAudioTrack.get(), sample->sampleID());
+            }
+        }
+        if (newTrack == 0) {
             // mToggle toggles each time a track is started on a given channel.
             // The toggle is concatenated with the SoundChannel address and passed to AudioTrack
             // as callback user data. This enables the detection of callbacks received from the old
@@ -746,10 +754,6 @@
             mToggle = toggle;
             mAudioTrack = newTrack;
             ALOGV("using new track %p for sample %d", newTrack.get(), sample->sampleID());
-        } else {
-            newTrack = mAudioTrack;
-            newTrack->setSampleRate(sampleRate);
-            ALOGV("reusing track %p for sample %d", mAudioTrack.get(), sample->sampleID());
         }
         newTrack->setVolume(leftVolume, rightVolume);
         newTrack->setLoop(0, frameCount, loop);
diff --git a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp b/media/jni/soundpool/android_media_SoundPool.cpp
similarity index 74%
rename from media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
rename to media/jni/soundpool/android_media_SoundPool.cpp
index b2333f8..fc4cf05 100644
--- a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
+++ b/media/jni/soundpool/android_media_SoundPool.cpp
@@ -47,10 +47,10 @@
 // ----------------------------------------------------------------------------
 
 static jint
-android_media_SoundPool_SoundPoolImpl_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor,
+android_media_SoundPool_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor,
         jlong offset, jlong length, jint priority)
 {
-    ALOGV("android_media_SoundPool_SoundPoolImpl_load_FD");
+    ALOGV("android_media_SoundPool_load_FD");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return 0;
     return (jint) ap->load(jniGetFDFromFileDescriptor(env, fileDescriptor),
@@ -58,104 +58,104 @@
 }
 
 static jboolean
-android_media_SoundPool_SoundPoolImpl_unload(JNIEnv *env, jobject thiz, jint sampleID) {
-    ALOGV("android_media_SoundPool_SoundPoolImpl_unload\n");
+android_media_SoundPool_unload(JNIEnv *env, jobject thiz, jint sampleID) {
+    ALOGV("android_media_SoundPool_unload\n");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return JNI_FALSE;
     return ap->unload(sampleID) ? JNI_TRUE : JNI_FALSE;
 }
 
 static jint
-android_media_SoundPool_SoundPoolImpl_play(JNIEnv *env, jobject thiz, jint sampleID,
+android_media_SoundPool_play(JNIEnv *env, jobject thiz, jint sampleID,
         jfloat leftVolume, jfloat rightVolume, jint priority, jint loop,
         jfloat rate)
 {
-    ALOGV("android_media_SoundPool_SoundPoolImpl_play\n");
+    ALOGV("android_media_SoundPool_play\n");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return 0;
     return (jint) ap->play(sampleID, leftVolume, rightVolume, priority, loop, rate);
 }
 
 static void
-android_media_SoundPool_SoundPoolImpl_pause(JNIEnv *env, jobject thiz, jint channelID)
+android_media_SoundPool_pause(JNIEnv *env, jobject thiz, jint channelID)
 {
-    ALOGV("android_media_SoundPool_SoundPoolImpl_pause");
+    ALOGV("android_media_SoundPool_pause");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return;
     ap->pause(channelID);
 }
 
 static void
-android_media_SoundPool_SoundPoolImpl_resume(JNIEnv *env, jobject thiz, jint channelID)
+android_media_SoundPool_resume(JNIEnv *env, jobject thiz, jint channelID)
 {
-    ALOGV("android_media_SoundPool_SoundPoolImpl_resume");
+    ALOGV("android_media_SoundPool_resume");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return;
     ap->resume(channelID);
 }
 
 static void
-android_media_SoundPool_SoundPoolImpl_autoPause(JNIEnv *env, jobject thiz)
+android_media_SoundPool_autoPause(JNIEnv *env, jobject thiz)
 {
-    ALOGV("android_media_SoundPool_SoundPoolImpl_autoPause");
+    ALOGV("android_media_SoundPool_autoPause");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return;
     ap->autoPause();
 }
 
 static void
-android_media_SoundPool_SoundPoolImpl_autoResume(JNIEnv *env, jobject thiz)
+android_media_SoundPool_autoResume(JNIEnv *env, jobject thiz)
 {
-    ALOGV("android_media_SoundPool_SoundPoolImpl_autoResume");
+    ALOGV("android_media_SoundPool_autoResume");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return;
     ap->autoResume();
 }
 
 static void
-android_media_SoundPool_SoundPoolImpl_stop(JNIEnv *env, jobject thiz, jint channelID)
+android_media_SoundPool_stop(JNIEnv *env, jobject thiz, jint channelID)
 {
-    ALOGV("android_media_SoundPool_SoundPoolImpl_stop");
+    ALOGV("android_media_SoundPool_stop");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return;
     ap->stop(channelID);
 }
 
 static void
-android_media_SoundPool_SoundPoolImpl_setVolume(JNIEnv *env, jobject thiz, jint channelID,
+android_media_SoundPool_setVolume(JNIEnv *env, jobject thiz, jint channelID,
         jfloat leftVolume, jfloat rightVolume)
 {
-    ALOGV("android_media_SoundPool_SoundPoolImpl_setVolume");
+    ALOGV("android_media_SoundPool_setVolume");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return;
     ap->setVolume(channelID, (float) leftVolume, (float) rightVolume);
 }
 
 static void
-android_media_SoundPool_SoundPoolImpl_setPriority(JNIEnv *env, jobject thiz, jint channelID,
+android_media_SoundPool_setPriority(JNIEnv *env, jobject thiz, jint channelID,
         jint priority)
 {
-    ALOGV("android_media_SoundPool_SoundPoolImpl_setPriority");
+    ALOGV("android_media_SoundPool_setPriority");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return;
     ap->setPriority(channelID, (int) priority);
 }
 
 static void
-android_media_SoundPool_SoundPoolImpl_setLoop(JNIEnv *env, jobject thiz, jint channelID,
+android_media_SoundPool_setLoop(JNIEnv *env, jobject thiz, jint channelID,
         int loop)
 {
-    ALOGV("android_media_SoundPool_SoundPoolImpl_setLoop");
+    ALOGV("android_media_SoundPool_setLoop");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return;
     ap->setLoop(channelID, loop);
 }
 
 static void
-android_media_SoundPool_SoundPoolImpl_setRate(JNIEnv *env, jobject thiz, jint channelID,
+android_media_SoundPool_setRate(JNIEnv *env, jobject thiz, jint channelID,
        jfloat rate)
 {
-    ALOGV("android_media_SoundPool_SoundPoolImpl_setRate");
+    ALOGV("android_media_SoundPool_setRate");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return;
     ap->setRate(channelID, (float) rate);
@@ -169,7 +169,7 @@
 }
 
 static jint
-android_media_SoundPool_SoundPoolImpl_native_setup(JNIEnv *env, jobject thiz, jobject weakRef,
+android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz, jobject weakRef,
         jint maxChannels, jobject jaa)
 {
     if (jaa == 0) {
@@ -191,7 +191,7 @@
             (audio_content_type_t) env->GetIntField(jaa, javaAudioAttrFields.fieldContentType);
     paa->flags = env->GetIntField(jaa, javaAudioAttrFields.fieldFlags);
 
-    ALOGV("android_media_SoundPool_SoundPoolImpl_native_setup");
+    ALOGV("android_media_SoundPool_native_setup");
     SoundPool *ap = new SoundPool(maxChannels, paa);
     if (ap == NULL) {
         return -1;
@@ -211,9 +211,9 @@
 }
 
 static void
-android_media_SoundPool_SoundPoolImpl_release(JNIEnv *env, jobject thiz)
+android_media_SoundPool_release(JNIEnv *env, jobject thiz)
 {
-    ALOGV("android_media_SoundPool_SoundPoolImpl_release");
+    ALOGV("android_media_SoundPool_release");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap != NULL) {
 
@@ -236,63 +236,63 @@
 static JNINativeMethod gMethods[] = {
     {   "_load",
         "(Ljava/io/FileDescriptor;JJI)I",
-        (void *)android_media_SoundPool_SoundPoolImpl_load_FD
+        (void *)android_media_SoundPool_load_FD
     },
     {   "unload",
         "(I)Z",
-        (void *)android_media_SoundPool_SoundPoolImpl_unload
+        (void *)android_media_SoundPool_unload
     },
     {   "_play",
         "(IFFIIF)I",
-        (void *)android_media_SoundPool_SoundPoolImpl_play
+        (void *)android_media_SoundPool_play
     },
     {   "pause",
         "(I)V",
-        (void *)android_media_SoundPool_SoundPoolImpl_pause
+        (void *)android_media_SoundPool_pause
     },
     {   "resume",
         "(I)V",
-        (void *)android_media_SoundPool_SoundPoolImpl_resume
+        (void *)android_media_SoundPool_resume
     },
     {   "autoPause",
         "()V",
-        (void *)android_media_SoundPool_SoundPoolImpl_autoPause
+        (void *)android_media_SoundPool_autoPause
     },
     {   "autoResume",
         "()V",
-        (void *)android_media_SoundPool_SoundPoolImpl_autoResume
+        (void *)android_media_SoundPool_autoResume
     },
     {   "stop",
         "(I)V",
-        (void *)android_media_SoundPool_SoundPoolImpl_stop
+        (void *)android_media_SoundPool_stop
     },
     {   "_setVolume",
         "(IFF)V",
-        (void *)android_media_SoundPool_SoundPoolImpl_setVolume
+        (void *)android_media_SoundPool_setVolume
     },
     {   "setPriority",
         "(II)V",
-        (void *)android_media_SoundPool_SoundPoolImpl_setPriority
+        (void *)android_media_SoundPool_setPriority
     },
     {   "setLoop",
         "(II)V",
-        (void *)android_media_SoundPool_SoundPoolImpl_setLoop
+        (void *)android_media_SoundPool_setLoop
     },
     {   "setRate",
         "(IF)V",
-        (void *)android_media_SoundPool_SoundPoolImpl_setRate
+        (void *)android_media_SoundPool_setRate
     },
     {   "native_setup",
         "(Ljava/lang/Object;ILjava/lang/Object;)I",
-        (void*)android_media_SoundPool_SoundPoolImpl_native_setup
+        (void*)android_media_SoundPool_native_setup
     },
     {   "release",
         "()V",
-        (void*)android_media_SoundPool_SoundPoolImpl_release
+        (void*)android_media_SoundPool_release
     }
 };
 
-static const char* const kClassPathName = "android/media/SoundPool$SoundPoolImpl";
+static const char* const kClassPathName = "android/media/SoundPool";
 
 jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
 {
@@ -314,14 +314,14 @@
 
     fields.mNativeContext = env->GetFieldID(clazz, "mNativeContext", "J");
     if (fields.mNativeContext == NULL) {
-        ALOGE("Can't find SoundPoolImpl.mNativeContext");
+        ALOGE("Can't find SoundPool.mNativeContext");
         return result;
     }
 
     fields.mPostEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
                                                "(Ljava/lang/Object;IIILjava/lang/Object;)V");
     if (fields.mPostEvent == NULL) {
-        ALOGE("Can't find android/media/SoundPoolImpl.postEventFromNative");
+        ALOGE("Can't find android/media/SoundPool.postEventFromNative");
         return result;
     }
 
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index e95e5ec..165b11e 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -42,10 +42,21 @@
             </intent-filter>
         </activity>
 
+        <activity
+            android:name=".StandaloneActivity"
+            android:theme="@style/StandaloneTheme"
+            android:icon="@drawable/ic_doc_text"
+            android:enabled="false">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
         <provider
             android:name=".RecentsProvider"
             android:authorities="com.android.documentsui.recents"
-            android:exported="false" />
+            android:exported="false"/>
 
         <receiver android:name=".PackageReceiver">
             <intent-filter>
diff --git a/packages/DocumentsUI/res/menu/mode_directory.xml b/packages/DocumentsUI/res/menu/mode_directory.xml
index 0a3645f..695060d 100644
--- a/packages/DocumentsUI/res/menu/mode_directory.xml
+++ b/packages/DocumentsUI/res/menu/mode_directory.xml
@@ -29,4 +29,8 @@
         android:icon="@drawable/ic_menu_delete"
         android:title="@string/menu_delete"
         android:showAsAction="always" />
+    <item
+        android:id="@+id/menu_select_all"
+        android:title="@string/menu_select_all"
+        android:showAsAction="never" />
 </menu>
diff --git a/packages/DocumentsUI/res/values-af/strings.xml b/packages/DocumentsUI/res/values-af/strings.xml
index 19fe7a7..98e2b2f 100644
--- a/packages/DocumentsUI/res/values-af/strings.xml
+++ b/packages/DocumentsUI/res/values-af/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Deel"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Vee uit"</string>
     <string name="menu_select" msgid="8711270657353563424">"Kies \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Wys interne berging"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Wys SD-kaart"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Versteek interne berging"</string>
diff --git a/packages/DocumentsUI/res/values-am/strings.xml b/packages/DocumentsUI/res/values-am/strings.xml
index 419d162..88865bc 100644
--- a/packages/DocumentsUI/res/values-am/strings.xml
+++ b/packages/DocumentsUI/res/values-am/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"አጋራ"</string>
     <string name="menu_delete" msgid="8138799623850614177">"ሰርዝ"</string>
     <string name="menu_select" msgid="8711270657353563424">"«<xliff:g id="DIRECTORY">^1</xliff:g>»ን ይምረጡ"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"ውስጣዊ ማከማቻ አሳይ"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD ካርድ አሳይ"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"ውስጣዊ ማከማቻ ደብቅ"</string>
diff --git a/packages/DocumentsUI/res/values-ar/strings.xml b/packages/DocumentsUI/res/values-ar/strings.xml
index 5bccdae..5162241 100644
--- a/packages/DocumentsUI/res/values-ar/strings.xml
+++ b/packages/DocumentsUI/res/values-ar/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"مشاركة"</string>
     <string name="menu_delete" msgid="8138799623850614177">"حذف"</string>
     <string name="menu_select" msgid="8711270657353563424">"تحديد \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"إظهار وحدة التخزين الداخلية"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"‏إظهار بطاقة SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"إخفاء وحدة التخزين الداخلية"</string>
diff --git a/packages/DocumentsUI/res/values-bg/strings.xml b/packages/DocumentsUI/res/values-bg/strings.xml
index 77f6855..14726eb 100644
--- a/packages/DocumentsUI/res/values-bg/strings.xml
+++ b/packages/DocumentsUI/res/values-bg/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Споделяне"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Изтриване"</string>
     <string name="menu_select" msgid="8711270657353563424">"Избиране на „<xliff:g id="DIRECTORY">^1</xliff:g>“"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Вътр. хранилище: Показв."</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD карта: Показване"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Вътр. хранилище: Скрив."</string>
diff --git a/packages/DocumentsUI/res/values-bn-rBD/strings.xml b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
index 603cb96..55fe9c7 100644
--- a/packages/DocumentsUI/res/values-bn-rBD/strings.xml
+++ b/packages/DocumentsUI/res/values-bn-rBD/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"ভাগ করুন"</string>
     <string name="menu_delete" msgid="8138799623850614177">"মুছুন"</string>
     <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\" নির্বাচন করুন"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"অভ্যন্তরীণ সঞ্চয়স্থান দেখান"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD কার্ড দেখান"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"অভ্যন্তরীণ সঞ্চয়স্থান লুকান"</string>
diff --git a/packages/DocumentsUI/res/values-ca/strings.xml b/packages/DocumentsUI/res/values-ca/strings.xml
index d973650..a4d4a51 100644
--- a/packages/DocumentsUI/res/values-ca/strings.xml
+++ b/packages/DocumentsUI/res/values-ca/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Comparteix"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Suprimeix"</string>
     <string name="menu_select" msgid="8711270657353563424">"Selecciona \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostra emmagatz. intern"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Mostra la targeta SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Amaga emmagatz. intern"</string>
diff --git a/packages/DocumentsUI/res/values-cs/strings.xml b/packages/DocumentsUI/res/values-cs/strings.xml
index 36bb72f..7261849 100644
--- a/packages/DocumentsUI/res/values-cs/strings.xml
+++ b/packages/DocumentsUI/res/values-cs/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Sdílet"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Smazat"</string>
     <string name="menu_select" msgid="8711270657353563424">"Vyberte adresář <xliff:g id="DIRECTORY">^1</xliff:g>"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Zobrazit inter. úložiště"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Zobrazit SD kartu"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Skrýt interní úložiště"</string>
diff --git a/packages/DocumentsUI/res/values-da/strings.xml b/packages/DocumentsUI/res/values-da/strings.xml
index 5feb517..f4e59b0 100644
--- a/packages/DocumentsUI/res/values-da/strings.xml
+++ b/packages/DocumentsUI/res/values-da/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Del"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Slet"</string>
     <string name="menu_select" msgid="8711270657353563424">"Vælg \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Vis intern lagerplads"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Vis SD-kort"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Skjul intern lagerplads"</string>
diff --git a/packages/DocumentsUI/res/values-de/strings.xml b/packages/DocumentsUI/res/values-de/strings.xml
index 598862b..b0e4a14 100644
--- a/packages/DocumentsUI/res/values-de/strings.xml
+++ b/packages/DocumentsUI/res/values-de/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Teilen"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Löschen"</string>
     <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\" auswählen"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Int. Speicher anzeigen"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD-Karte anzeigen"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Int. Speicher ausblenden"</string>
diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml
index 37f99cf..2188dc2 100644
--- a/packages/DocumentsUI/res/values-el/strings.xml
+++ b/packages/DocumentsUI/res/values-el/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Κοινή χρήση"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Διαγραφή"</string>
     <string name="menu_select" msgid="8711270657353563424">"Επιλογή \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Εμφ.εσωτ.χώρου αποθήκ."</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Εμφάνιση κάρτας SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Απόκρ.εσωτ.χώρου αποθήκ."</string>
diff --git a/packages/DocumentsUI/res/values-en-rAU/strings.xml b/packages/DocumentsUI/res/values-en-rAU/strings.xml
index 2bd5615..89257a1 100644
--- a/packages/DocumentsUI/res/values-en-rAU/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rAU/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Share"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Delete"</string>
     <string name="menu_select" msgid="8711270657353563424">"Select \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Show internal storage"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Show SD card"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Hide internal storage"</string>
diff --git a/packages/DocumentsUI/res/values-en-rGB/strings.xml b/packages/DocumentsUI/res/values-en-rGB/strings.xml
index 2bd5615..89257a1 100644
--- a/packages/DocumentsUI/res/values-en-rGB/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rGB/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Share"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Delete"</string>
     <string name="menu_select" msgid="8711270657353563424">"Select \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Show internal storage"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Show SD card"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Hide internal storage"</string>
diff --git a/packages/DocumentsUI/res/values-en-rIN/strings.xml b/packages/DocumentsUI/res/values-en-rIN/strings.xml
index 2bd5615..89257a1 100644
--- a/packages/DocumentsUI/res/values-en-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-en-rIN/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Share"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Delete"</string>
     <string name="menu_select" msgid="8711270657353563424">"Select \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Show internal storage"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Show SD card"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Hide internal storage"</string>
diff --git a/packages/DocumentsUI/res/values-es-rUS/strings.xml b/packages/DocumentsUI/res/values-es-rUS/strings.xml
index 4a47b73..693a981 100644
--- a/packages/DocumentsUI/res/values-es-rUS/strings.xml
+++ b/packages/DocumentsUI/res/values-es-rUS/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Compartir"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Eliminar"</string>
     <string name="menu_select" msgid="8711270657353563424">"Seleccionar \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostrar almacen. interno"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Mostrar tarjeta SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Ocultar almacen. interno"</string>
diff --git a/packages/DocumentsUI/res/values-es/strings.xml b/packages/DocumentsUI/res/values-es/strings.xml
index 95cc812..422c47c 100644
--- a/packages/DocumentsUI/res/values-es/strings.xml
+++ b/packages/DocumentsUI/res/values-es/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Compartir"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Eliminar"</string>
     <string name="menu_select" msgid="8711270657353563424">"Selecciona \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostrar almac. interno"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Mostrar tarjeta SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Ocultar almac. interno"</string>
diff --git a/packages/DocumentsUI/res/values-et-rEE/strings.xml b/packages/DocumentsUI/res/values-et-rEE/strings.xml
index c98f0b9..cce47c1 100644
--- a/packages/DocumentsUI/res/values-et-rEE/strings.xml
+++ b/packages/DocumentsUI/res/values-et-rEE/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Jaga"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Kustuta"</string>
     <string name="menu_select" msgid="8711270657353563424">"Kataloogi „<xliff:g id="DIRECTORY">^1</xliff:g>” valimine"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Kuva sis. salvestusruum"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Kuva SD-kaart"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Peida sis. salvestusruum"</string>
diff --git a/packages/DocumentsUI/res/values-eu-rES/strings.xml b/packages/DocumentsUI/res/values-eu-rES/strings.xml
index 1fd7b1d..4e0735a 100644
--- a/packages/DocumentsUI/res/values-eu-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-eu-rES/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Partekatu"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Ezabatu"</string>
     <string name="menu_select" msgid="8711270657353563424">"Hautatu \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Erakutsi barneko memoria"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Erakutsi SD txartela"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Ezkutatu barneko memoria"</string>
diff --git a/packages/DocumentsUI/res/values-fa/strings.xml b/packages/DocumentsUI/res/values-fa/strings.xml
index 6c57211..b8874b8 100644
--- a/packages/DocumentsUI/res/values-fa/strings.xml
+++ b/packages/DocumentsUI/res/values-fa/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"اشتراک‌گذاری"</string>
     <string name="menu_delete" msgid="8138799623850614177">"حذف"</string>
     <string name="menu_select" msgid="8711270657353563424">"انتخاب «<xliff:g id="DIRECTORY">^1</xliff:g>»"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"نمایش فضای ذخیره‌سازی داخلی"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"‏نمایش کارت SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"پنهان کردن فضای ذخیره‌سازی داخلی"</string>
diff --git a/packages/DocumentsUI/res/values-fi/strings.xml b/packages/DocumentsUI/res/values-fi/strings.xml
index 185be47..74a0beb 100644
--- a/packages/DocumentsUI/res/values-fi/strings.xml
+++ b/packages/DocumentsUI/res/values-fi/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Jaa"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Poista"</string>
     <string name="menu_select" msgid="8711270657353563424">"Valitse <xliff:g id="DIRECTORY">^1</xliff:g>"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Näytä sis. tallennustila"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Näytä SD-kortti"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Piilota sis. tallennust."</string>
diff --git a/packages/DocumentsUI/res/values-fr-rCA/strings.xml b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
index 4e4c0ed..00e09a2 100644
--- a/packages/DocumentsUI/res/values-fr-rCA/strings.xml
+++ b/packages/DocumentsUI/res/values-fr-rCA/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Partager"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Supprimer"</string>
     <string name="menu_select" msgid="8711270657353563424">"Sélectionner « <xliff:g id="DIRECTORY">^1</xliff:g> »"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Aff. mém. stock. interne"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Afficher la carte SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Masquer mém. stock. int."</string>
diff --git a/packages/DocumentsUI/res/values-fr/strings.xml b/packages/DocumentsUI/res/values-fr/strings.xml
index b760caa..5b4b2d8 100644
--- a/packages/DocumentsUI/res/values-fr/strings.xml
+++ b/packages/DocumentsUI/res/values-fr/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Partager"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Supprimer"</string>
     <string name="menu_select" msgid="8711270657353563424">"Sélectionner \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Aff. mém. stock. interne"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Afficher la carte SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Masquer mém. stock. int."</string>
diff --git a/packages/DocumentsUI/res/values-gl-rES/strings.xml b/packages/DocumentsUI/res/values-gl-rES/strings.xml
index bc6ee9e..91199e6 100644
--- a/packages/DocumentsUI/res/values-gl-rES/strings.xml
+++ b/packages/DocumentsUI/res/values-gl-rES/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Compartir"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Eliminar"</string>
     <string name="menu_select" msgid="8711270657353563424">"Selecciona \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostrar almacen. interno"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Mostrar tarxeta SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Ocultar almacen. interno"</string>
diff --git a/packages/DocumentsUI/res/values-hi/strings.xml b/packages/DocumentsUI/res/values-hi/strings.xml
index 88f26ed..5f122f8 100644
--- a/packages/DocumentsUI/res/values-hi/strings.xml
+++ b/packages/DocumentsUI/res/values-hi/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"साझा करें"</string>
     <string name="menu_delete" msgid="8138799623850614177">"हटाएं"</string>
     <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\" चुनें"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"आंतरिक मेमोरी दिखाएं"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD कार्ड दिखाएं"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"आंतरिक मेमोरी छिपाएं"</string>
diff --git a/packages/DocumentsUI/res/values-hr/strings.xml b/packages/DocumentsUI/res/values-hr/strings.xml
index 60343d2..35c8508 100644
--- a/packages/DocumentsUI/res/values-hr/strings.xml
+++ b/packages/DocumentsUI/res/values-hr/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Dijeli"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Izbriši"</string>
     <string name="menu_select" msgid="8711270657353563424">"Odaberi \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Pokaži internu pohranu"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Pokaži SD karticu"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Sakrij internu pohranu"</string>
diff --git a/packages/DocumentsUI/res/values-hu/strings.xml b/packages/DocumentsUI/res/values-hu/strings.xml
index a9b5a7c..334e2a0 100644
--- a/packages/DocumentsUI/res/values-hu/strings.xml
+++ b/packages/DocumentsUI/res/values-hu/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Megosztás"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Törlés"</string>
     <string name="menu_select" msgid="8711270657353563424">"A(z) „<xliff:g id="DIRECTORY">^1</xliff:g>” mappa kiválasztása"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Belső tárhely"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD-kártya megjelenítése"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Belső tárhely elrejtése"</string>
diff --git a/packages/DocumentsUI/res/values-hy-rAM/strings.xml b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
index 487f5bd..1040fd2 100644
--- a/packages/DocumentsUI/res/values-hy-rAM/strings.xml
+++ b/packages/DocumentsUI/res/values-hy-rAM/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Համօգտագործել"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Ջնջել"</string>
     <string name="menu_select" msgid="8711270657353563424">"Ընտրել «<xliff:g id="DIRECTORY">^1</xliff:g>»"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Ցույց տալ ներքին պահոցը"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Ցույց տալ SD քարտը"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Թաքցնել ներքին պահոցը"</string>
diff --git a/packages/DocumentsUI/res/values-in/strings.xml b/packages/DocumentsUI/res/values-in/strings.xml
index 1514fc6..a8c62da 100644
--- a/packages/DocumentsUI/res/values-in/strings.xml
+++ b/packages/DocumentsUI/res/values-in/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Bagikan"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Hapus"</string>
     <string name="menu_select" msgid="8711270657353563424">"Pilih \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Tampilkan simpanan internal"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Tampilkan kartu SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Sembunyikan simpanan internal"</string>
diff --git a/packages/DocumentsUI/res/values-is-rIS/strings.xml b/packages/DocumentsUI/res/values-is-rIS/strings.xml
index f324dad..b81b024 100644
--- a/packages/DocumentsUI/res/values-is-rIS/strings.xml
+++ b/packages/DocumentsUI/res/values-is-rIS/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Deila"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Eyða"</string>
     <string name="menu_select" msgid="8711270657353563424">"Velja „<xliff:g id="DIRECTORY">^1</xliff:g>“"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Sýna innbyggða geymslu"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Sýna SD-kort"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Fela innbyggða geymslu"</string>
diff --git a/packages/DocumentsUI/res/values-it/strings.xml b/packages/DocumentsUI/res/values-it/strings.xml
index a073c44..fa5743f 100644
--- a/packages/DocumentsUI/res/values-it/strings.xml
+++ b/packages/DocumentsUI/res/values-it/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Condividi"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Elimina"</string>
     <string name="menu_select" msgid="8711270657353563424">"Seleziona \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostra memoria interna"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Mostra scheda SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Nascondi memoria interna"</string>
diff --git a/packages/DocumentsUI/res/values-iw/strings.xml b/packages/DocumentsUI/res/values-iw/strings.xml
index 6ab2880..92a0bf47cf 100644
--- a/packages/DocumentsUI/res/values-iw/strings.xml
+++ b/packages/DocumentsUI/res/values-iw/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"שתף"</string>
     <string name="menu_delete" msgid="8138799623850614177">"מחק"</string>
     <string name="menu_select" msgid="8711270657353563424">"בחר ב-\"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"הצג אחסון פנימי"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"‏הצג כרטיס SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"הסתר אחסון פנימי"</string>
diff --git a/packages/DocumentsUI/res/values-ja/strings.xml b/packages/DocumentsUI/res/values-ja/strings.xml
index f57a21c..938dfa4 100644
--- a/packages/DocumentsUI/res/values-ja/strings.xml
+++ b/packages/DocumentsUI/res/values-ja/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"共有"</string>
     <string name="menu_delete" msgid="8138799623850614177">"削除"</string>
     <string name="menu_select" msgid="8711270657353563424">"「<xliff:g id="DIRECTORY">^1</xliff:g>」を選択"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"内部ストレージを表示"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SDカードを表示"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"内部ストレージを非表示"</string>
diff --git a/packages/DocumentsUI/res/values-ka-rGE/strings.xml b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
index b1e39b8..7f33e7c 100644
--- a/packages/DocumentsUI/res/values-ka-rGE/strings.xml
+++ b/packages/DocumentsUI/res/values-ka-rGE/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"გაზიარება"</string>
     <string name="menu_delete" msgid="8138799623850614177">"წაშლა"</string>
     <string name="menu_select" msgid="8711270657353563424">"„<xliff:g id="DIRECTORY">^1</xliff:g>“-ის არჩევა"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"შიდა საცავის ჩვენება"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD ბარათის ჩვენება"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"შიდა მეხსიერების დამალვა"</string>
diff --git a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
index c7e207c..0d91781 100644
--- a/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
+++ b/packages/DocumentsUI/res/values-kk-rKZ/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Бөлісу"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Жою"</string>
     <string name="menu_select" msgid="8711270657353563424">"«<xliff:g id="DIRECTORY">^1</xliff:g>» таңдау"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Ішкі жадты көрсету"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD картасын көрсету"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Ішкі жадты жасыру"</string>
diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index 3055001..2b71a57 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក​"</string>
     <string name="menu_delete" msgid="8138799623850614177">"លុប"</string>
     <string name="menu_select" msgid="8711270657353563424">"ជ្រើស \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"បង្ហាញឧបករណ៍ផ្ទុកខាងក្នុង"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"បង្ហាញកាតអេសឌី"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"លាក់​ឧបករណ៍​​ផ្ទុក​ខាងក្នុង"</string>
diff --git a/packages/DocumentsUI/res/values-kn-rIN/strings.xml b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
index 508f0e5..ffedf11 100644
--- a/packages/DocumentsUI/res/values-kn-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-kn-rIN/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"ಹಂಚು"</string>
     <string name="menu_delete" msgid="8138799623850614177">"ಅಳಿಸು"</string>
     <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\" ಆಯ್ಕೆಮಾಡಿ"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"ಆಂತರಿಕ ಸಂಗ್ರಹಣೆಯನ್ನು ತೋರಿಸು"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD ಕಾಡ್‌ ಅನ್ನು ತೋರಿಸು"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"ಆಂತರಿಕ ಸಂಗ್ರಹಣೆಯನ್ನು ಮರೆಮಾಡಿ"</string>
diff --git a/packages/DocumentsUI/res/values-ko/strings.xml b/packages/DocumentsUI/res/values-ko/strings.xml
index d6f0cbd..6b6fc7a 100644
--- a/packages/DocumentsUI/res/values-ko/strings.xml
+++ b/packages/DocumentsUI/res/values-ko/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"공유"</string>
     <string name="menu_delete" msgid="8138799623850614177">"삭제"</string>
     <string name="menu_select" msgid="8711270657353563424">"\'<xliff:g id="DIRECTORY">^1</xliff:g>\' 선택"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"내부 저장소 표시"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD 카드 표시"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"내부 저장소 숨기기"</string>
diff --git a/packages/DocumentsUI/res/values-ky-rKG/strings.xml b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
index a4f718b..da4049b 100644
--- a/packages/DocumentsUI/res/values-ky-rKG/strings.xml
+++ b/packages/DocumentsUI/res/values-ky-rKG/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Бөлүшүү"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Өчүрүү"</string>
     <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\" тандоо"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Ички сактагычты көрсөтүү"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD картаны көрсөтүү"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Ички эстутумду жашыруу"</string>
diff --git a/packages/DocumentsUI/res/values-lo-rLA/strings.xml b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
index 8db69c7..52ae201 100644
--- a/packages/DocumentsUI/res/values-lo-rLA/strings.xml
+++ b/packages/DocumentsUI/res/values-lo-rLA/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"ແບ່ງປັນ"</string>
     <string name="menu_delete" msgid="8138799623850614177">"ລຶບ"</string>
     <string name="menu_select" msgid="8711270657353563424">"ເລືອກ​ \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"ສະແດງ​ໂຕເກັບ​ຂໍ້ມູນພາຍໃນ"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"ສະແດງ SD Card"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"ເຊື່ອງ​ໂຕ​ເກັບຂໍ້ມູນ​ພາຍໃນ"</string>
diff --git a/packages/DocumentsUI/res/values-lt/strings.xml b/packages/DocumentsUI/res/values-lt/strings.xml
index 81412c5..31e510b 100644
--- a/packages/DocumentsUI/res/values-lt/strings.xml
+++ b/packages/DocumentsUI/res/values-lt/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Bendrinti"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Ištrinti"</string>
     <string name="menu_select" msgid="8711270657353563424">"Pasirinkti katalogą „<xliff:g id="DIRECTORY">^1</xliff:g>“"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Rodyti vidinę atmintį"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Rodyti SD kortelę"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Slėpti vidinę atmintį"</string>
diff --git a/packages/DocumentsUI/res/values-lv/strings.xml b/packages/DocumentsUI/res/values-lv/strings.xml
index 0cd5812..93794fa 100644
--- a/packages/DocumentsUI/res/values-lv/strings.xml
+++ b/packages/DocumentsUI/res/values-lv/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Kopīgot"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Dzēst"</string>
     <string name="menu_select" msgid="8711270657353563424">"Atlasīt “<xliff:g id="DIRECTORY">^1</xliff:g>”"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Rādīt iekšējo atmiņu"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Rādīt SD karti"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Paslēpt iekšējo atmiņu"</string>
diff --git a/packages/DocumentsUI/res/values-mk-rMK/strings.xml b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
index d909741..6fe9c863 100644
--- a/packages/DocumentsUI/res/values-mk-rMK/strings.xml
+++ b/packages/DocumentsUI/res/values-mk-rMK/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Сподели"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Избриши"</string>
     <string name="menu_select" msgid="8711270657353563424">"Одберете „<xliff:g id="DIRECTORY">^1</xliff:g>“"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Прикажи внатрешна мемор."</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Прикажи СД-картичка"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Скриј внатрешна меморија"</string>
diff --git a/packages/DocumentsUI/res/values-ml-rIN/strings.xml b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
index 24f4815..052dbd2 100644
--- a/packages/DocumentsUI/res/values-ml-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ml-rIN/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"പങ്കിടുക"</string>
     <string name="menu_delete" msgid="8138799623850614177">"ഇല്ലാതാക്കുക"</string>
     <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\" തിരഞ്ഞെടുക്കുക"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"ആന്തരിക സംഭരണം കാണിക്കുക"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD കാർഡ് കാണിക്കുക"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"ആന്തരിക സംഭരണം മറയ്‌ക്കുക"</string>
diff --git a/packages/DocumentsUI/res/values-mn-rMN/strings.xml b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
index 141032d..737d4b2 100644
--- a/packages/DocumentsUI/res/values-mn-rMN/strings.xml
+++ b/packages/DocumentsUI/res/values-mn-rMN/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Хуваалцах"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Устгах"</string>
     <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\"-г сонгох"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Дотоод санг харуулах"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD картыг харуулах"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Дотоод санг нуух"</string>
diff --git a/packages/DocumentsUI/res/values-mr-rIN/strings.xml b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
index b59ded5..26a65cd 100644
--- a/packages/DocumentsUI/res/values-mr-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-mr-rIN/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"सामायिक करा"</string>
     <string name="menu_delete" msgid="8138799623850614177">"हटवा"</string>
     <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\" निवडा"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"अंतर्गत संचयन दर्शवा"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD कार्ड दर्शवा"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"अंतर्गत संचयन लपवा"</string>
diff --git a/packages/DocumentsUI/res/values-ms-rMY/strings.xml b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
index 5330d92..e9de0e3 100644
--- a/packages/DocumentsUI/res/values-ms-rMY/strings.xml
+++ b/packages/DocumentsUI/res/values-ms-rMY/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Kongsi"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Padam"</string>
     <string name="menu_select" msgid="8711270657353563424">"Pilih \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Papar storan dalaman"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Papar kad SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Sembunyikan storan dlmn"</string>
diff --git a/packages/DocumentsUI/res/values-my-rMM/strings.xml b/packages/DocumentsUI/res/values-my-rMM/strings.xml
index 50f8363..ae38956 100644
--- a/packages/DocumentsUI/res/values-my-rMM/strings.xml
+++ b/packages/DocumentsUI/res/values-my-rMM/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"မျှဝေခြင်း"</string>
     <string name="menu_delete" msgid="8138799623850614177">"ဖျက်ပစ်ရန်"</string>
     <string name="menu_select" msgid="8711270657353563424">"ရွေးရန်\"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"စက်ရှိစတိုရုံ ပြပါ"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD ကဒ် ပြပါ"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"စက်ရှိစတိုရုံ ဖျောက်ထားပါ"</string>
diff --git a/packages/DocumentsUI/res/values-nb/strings.xml b/packages/DocumentsUI/res/values-nb/strings.xml
index beee44f..d10b740 100644
--- a/packages/DocumentsUI/res/values-nb/strings.xml
+++ b/packages/DocumentsUI/res/values-nb/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Del"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Slett"</string>
     <string name="menu_select" msgid="8711270657353563424">"Velg «<xliff:g id="DIRECTORY">^1</xliff:g>»"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Vis den interne lagringen"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Vis SD-kortet"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Skjul den interne lagringen"</string>
diff --git a/packages/DocumentsUI/res/values-ne-rNP/strings.xml b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
index 4251a7c..2b6015a 100644
--- a/packages/DocumentsUI/res/values-ne-rNP/strings.xml
+++ b/packages/DocumentsUI/res/values-ne-rNP/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"साझेदारी गर्नुहोस्"</string>
     <string name="menu_delete" msgid="8138799623850614177">"मेटाउनुहोस्"</string>
     <string name="menu_select" msgid="8711270657353563424">"चयनगर्नुहोस् \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"आन्तरिक भण्डारण देखाउनुहोस्"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD कार्ड देखाउनुहोस्"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"आन्तरिक भण्डारण लुकाउनुहोस्"</string>
diff --git a/packages/DocumentsUI/res/values-nl/strings.xml b/packages/DocumentsUI/res/values-nl/strings.xml
index 1fc7dc1..dc5e250 100644
--- a/packages/DocumentsUI/res/values-nl/strings.xml
+++ b/packages/DocumentsUI/res/values-nl/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Delen"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Verwijderen"</string>
     <string name="menu_select" msgid="8711270657353563424">"<xliff:g id="DIRECTORY">^1</xliff:g> selecteren"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Interne opslag weergeven"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD-kaart weergeven"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Interne opslag verbergen"</string>
diff --git a/packages/DocumentsUI/res/values-pl/strings.xml b/packages/DocumentsUI/res/values-pl/strings.xml
index 5bb7f75..eb84e5d 100644
--- a/packages/DocumentsUI/res/values-pl/strings.xml
+++ b/packages/DocumentsUI/res/values-pl/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Udostępnij"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Usuń"</string>
     <string name="menu_select" msgid="8711270657353563424">"Zaznacz „<xliff:g id="DIRECTORY">^1</xliff:g>”"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Pokaż pamięć wewnętrzną"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Pokaż kartę SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Ukryj pamięć wewnętrzną"</string>
diff --git a/packages/DocumentsUI/res/values-pt-rPT/strings.xml b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
index e32dfc9..b0edad6 100644
--- a/packages/DocumentsUI/res/values-pt-rPT/strings.xml
+++ b/packages/DocumentsUI/res/values-pt-rPT/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Partilhar"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Eliminar"</string>
     <string name="menu_select" msgid="8711270657353563424">"Selecionar \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostrar mem. armaz. int."</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Mostrar cartão SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Ocultar mem. armaz. int."</string>
diff --git a/packages/DocumentsUI/res/values-pt/strings.xml b/packages/DocumentsUI/res/values-pt/strings.xml
index 2aaa4d2..f35bd19 100644
--- a/packages/DocumentsUI/res/values-pt/strings.xml
+++ b/packages/DocumentsUI/res/values-pt/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Compartilhar"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Excluir"</string>
     <string name="menu_select" msgid="8711270657353563424">"Selecionar \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Mostrar armaz. interno"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Mostrar cartão SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Ocultar armaz. interno"</string>
diff --git a/packages/DocumentsUI/res/values-ro/strings.xml b/packages/DocumentsUI/res/values-ro/strings.xml
index c5de856..646d537c 100644
--- a/packages/DocumentsUI/res/values-ro/strings.xml
+++ b/packages/DocumentsUI/res/values-ro/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Distribuiți"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Ștergeți"</string>
     <string name="menu_select" msgid="8711270657353563424">"Selectați „<xliff:g id="DIRECTORY">^1</xliff:g>”"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Afișați stocarea internă"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Afișați cardul SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Ascundeți stocarea internă"</string>
diff --git a/packages/DocumentsUI/res/values-ru/strings.xml b/packages/DocumentsUI/res/values-ru/strings.xml
index 10de6f0..0cc90de 100644
--- a/packages/DocumentsUI/res/values-ru/strings.xml
+++ b/packages/DocumentsUI/res/values-ru/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Поделиться"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Удалить"</string>
     <string name="menu_select" msgid="8711270657353563424">"Выбрать папку \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Внутренняя память"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD-карта"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Скрыть внутреннюю память"</string>
diff --git a/packages/DocumentsUI/res/values-si-rLK/strings.xml b/packages/DocumentsUI/res/values-si-rLK/strings.xml
index 26e2b1d..fe2c66f 100644
--- a/packages/DocumentsUI/res/values-si-rLK/strings.xml
+++ b/packages/DocumentsUI/res/values-si-rLK/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"බෙදාගන්න"</string>
     <string name="menu_delete" msgid="8138799623850614177">"මකන්න"</string>
     <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\" තෝරන්න"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"අභ්‍යන්තර ආචයනය පෙන්වන්න"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD කාඩ් පත පෙන්වන්න"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"අභ්‍යන්තර ආචයනය සඟවන්න"</string>
diff --git a/packages/DocumentsUI/res/values-sk/strings.xml b/packages/DocumentsUI/res/values-sk/strings.xml
index facaf4a..b68a493 100644
--- a/packages/DocumentsUI/res/values-sk/strings.xml
+++ b/packages/DocumentsUI/res/values-sk/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Zdieľať"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Odstrániť"</string>
     <string name="menu_select" msgid="8711270657353563424">"Vyberte adresár <xliff:g id="DIRECTORY">^1</xliff:g>"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Zobraziť interné úložisko"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Zobraziť kartu SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Skryť interné úložisko"</string>
diff --git a/packages/DocumentsUI/res/values-sl/strings.xml b/packages/DocumentsUI/res/values-sl/strings.xml
index 865df8b..f289e64 100644
--- a/packages/DocumentsUI/res/values-sl/strings.xml
+++ b/packages/DocumentsUI/res/values-sl/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Skupna raba"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Izbriši"</string>
     <string name="menu_select" msgid="8711270657353563424">"Izbira mape »<xliff:g id="DIRECTORY">^1</xliff:g>«"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Pokaži notranjo shrambo"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Pokaži kartico SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Skrij notranjo shrambo"</string>
diff --git a/packages/DocumentsUI/res/values-sr/strings.xml b/packages/DocumentsUI/res/values-sr/strings.xml
index fd20722..d18a227 100644
--- a/packages/DocumentsUI/res/values-sr/strings.xml
+++ b/packages/DocumentsUI/res/values-sr/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Дели"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Избриши"</string>
     <string name="menu_select" msgid="8711270657353563424">"Изабери „<xliff:g id="DIRECTORY">^1</xliff:g>“"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Прикажи интерну меморију"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Прикажи SD картицу"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Сакриј интерну меморију"</string>
diff --git a/packages/DocumentsUI/res/values-sv/strings.xml b/packages/DocumentsUI/res/values-sv/strings.xml
index a77b75d..e2d0e2a 100644
--- a/packages/DocumentsUI/res/values-sv/strings.xml
+++ b/packages/DocumentsUI/res/values-sv/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Dela"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Ta bort"</string>
     <string name="menu_select" msgid="8711270657353563424">"Välj <xliff:g id="DIRECTORY">^1</xliff:g>"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Visa internminne"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Visa SD-kort"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Dölj internminne"</string>
diff --git a/packages/DocumentsUI/res/values-sw/strings.xml b/packages/DocumentsUI/res/values-sw/strings.xml
index b46d97f..65fe404 100644
--- a/packages/DocumentsUI/res/values-sw/strings.xml
+++ b/packages/DocumentsUI/res/values-sw/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Shiriki"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Futa"</string>
     <string name="menu_select" msgid="8711270657353563424">"Chagua \" <xliff:g id="DIRECTORY">^1</xliff:g> \""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Onyesha hifadhi ya ndani"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Onyesha kadi ya SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Ficha hifadhi ya ndani"</string>
diff --git a/packages/DocumentsUI/res/values-ta-rIN/strings.xml b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
index 5b09b97..dc20e84 100644
--- a/packages/DocumentsUI/res/values-ta-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-ta-rIN/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"பகிர்"</string>
     <string name="menu_delete" msgid="8138799623850614177">"நீக்கு"</string>
     <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\" ஐத் தேர்ந்தெடு"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"அகச் சேமிப்பகத்தைக் காட்டு"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD கார்டைக் காட்டு"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"அகச் சேமிப்பகத்தை மறை"</string>
diff --git a/packages/DocumentsUI/res/values-te-rIN/strings.xml b/packages/DocumentsUI/res/values-te-rIN/strings.xml
index e04724a..24acc24 100644
--- a/packages/DocumentsUI/res/values-te-rIN/strings.xml
+++ b/packages/DocumentsUI/res/values-te-rIN/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"భాగస్వామ్యం చేయి"</string>
     <string name="menu_delete" msgid="8138799623850614177">"తొలగించు"</string>
     <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\"ని ఎంచుకోండి"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"అంతర్గత నిల్వను చూపు"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD కార్డ్‌ను చూపు"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"అంతర్గత నిల్వను దాచు"</string>
diff --git a/packages/DocumentsUI/res/values-th/strings.xml b/packages/DocumentsUI/res/values-th/strings.xml
index 5410d37..e692cb7 100644
--- a/packages/DocumentsUI/res/values-th/strings.xml
+++ b/packages/DocumentsUI/res/values-th/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"แชร์"</string>
     <string name="menu_delete" msgid="8138799623850614177">"ลบ"</string>
     <string name="menu_select" msgid="8711270657353563424">"เลือก \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"แสดงที่จัดเก็บภายใน"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"แสดงการ์ด SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"ซ่อนที่จัดเก็บภายใน"</string>
diff --git a/packages/DocumentsUI/res/values-tl/strings.xml b/packages/DocumentsUI/res/values-tl/strings.xml
index 3defd6a..e55d552 100644
--- a/packages/DocumentsUI/res/values-tl/strings.xml
+++ b/packages/DocumentsUI/res/values-tl/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Ibahagi"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Tanggalin"</string>
     <string name="menu_select" msgid="8711270657353563424">"Piliin ang \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Ipakita internal storage"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Ipakita ang SD card"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Itago internal storage"</string>
diff --git a/packages/DocumentsUI/res/values-tr/strings.xml b/packages/DocumentsUI/res/values-tr/strings.xml
index 9f0f846..b80904c 100644
--- a/packages/DocumentsUI/res/values-tr/strings.xml
+++ b/packages/DocumentsUI/res/values-tr/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Paylaş"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Sil"</string>
     <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\" dizinini seç"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Dahili depolamayı göster"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD kartı göster"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Dahili depolamayı gizle"</string>
diff --git a/packages/DocumentsUI/res/values-uk/strings.xml b/packages/DocumentsUI/res/values-uk/strings.xml
index 5be1947..77654fd 100644
--- a/packages/DocumentsUI/res/values-uk/strings.xml
+++ b/packages/DocumentsUI/res/values-uk/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Поділитися"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Видалити"</string>
     <string name="menu_select" msgid="8711270657353563424">"Вибрати каталог \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Показати внутр. пам’ять"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Показати карту SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Сховати внутр. пам’ять"</string>
diff --git a/packages/DocumentsUI/res/values-ur-rPK/strings.xml b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
index 729fc7f..d20f14c 100644
--- a/packages/DocumentsUI/res/values-ur-rPK/strings.xml
+++ b/packages/DocumentsUI/res/values-ur-rPK/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"اشتراک کریں"</string>
     <string name="menu_delete" msgid="8138799623850614177">"حذف کریں"</string>
     <string name="menu_select" msgid="8711270657353563424">"\"<xliff:g id="DIRECTORY">^1</xliff:g>\" منتخب کریں"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"داخلی اسٹوریج دکھائیں"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"‏SD کارڈ دکھائیں"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"داخلی اسٹوریج چھپائیں"</string>
diff --git a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
index db35356..96241de 100644
--- a/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
+++ b/packages/DocumentsUI/res/values-uz-rUZ/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Ulashish"</string>
     <string name="menu_delete" msgid="8138799623850614177">"O‘chirish"</string>
     <string name="menu_select" msgid="8711270657353563424">"“<xliff:g id="DIRECTORY">^1</xliff:g>” jildini tanlash"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Ichki xotirani ko‘rsatish"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"SD kartani ko‘rsatish"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Ichki xotirani berkitish"</string>
diff --git a/packages/DocumentsUI/res/values-vi/strings.xml b/packages/DocumentsUI/res/values-vi/strings.xml
index e4bbfda..581e2c9 100644
--- a/packages/DocumentsUI/res/values-vi/strings.xml
+++ b/packages/DocumentsUI/res/values-vi/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Chia sẻ"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Xóa"</string>
     <string name="menu_select" msgid="8711270657353563424">"Chọn \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Hiển thị bộ nhớ trong"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Hiển thị thẻ SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Ẩn bộ nhớ trong"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rCN/strings.xml b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
index 4a44250..1b81ba4 100644
--- a/packages/DocumentsUI/res/values-zh-rCN/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rCN/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"分享"</string>
     <string name="menu_delete" msgid="8138799623850614177">"删除"</string>
     <string name="menu_select" msgid="8711270657353563424">"选择“<xliff:g id="DIRECTORY">^1</xliff:g>”"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"显示内部存储设备"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"显示SD卡"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"隐藏内部存储设备"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rHK/strings.xml b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
index e245c12..19460c1 100644
--- a/packages/DocumentsUI/res/values-zh-rHK/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rHK/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"分享"</string>
     <string name="menu_delete" msgid="8138799623850614177">"刪除"</string>
     <string name="menu_select" msgid="8711270657353563424">"選取「<xliff:g id="DIRECTORY">^1</xliff:g>」"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"顯示內部儲存空間"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"顯示 SD 卡"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"隱藏內部儲存空間"</string>
diff --git a/packages/DocumentsUI/res/values-zh-rTW/strings.xml b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
index 464a13e..ca844c4 100644
--- a/packages/DocumentsUI/res/values-zh-rTW/strings.xml
+++ b/packages/DocumentsUI/res/values-zh-rTW/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"共用"</string>
     <string name="menu_delete" msgid="8138799623850614177">"刪除"</string>
     <string name="menu_select" msgid="8711270657353563424">"選取「<xliff:g id="DIRECTORY">^1</xliff:g>」"</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"顯示內部儲存空間"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"顯示 SD 卡"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"隱藏內部儲存空間"</string>
diff --git a/packages/DocumentsUI/res/values-zu/strings.xml b/packages/DocumentsUI/res/values-zu/strings.xml
index b35da3c..d21f07e3 100644
--- a/packages/DocumentsUI/res/values-zu/strings.xml
+++ b/packages/DocumentsUI/res/values-zu/strings.xml
@@ -30,6 +30,8 @@
     <string name="menu_share" msgid="3075149983979628146">"Yabelana"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Susa"</string>
     <string name="menu_select" msgid="8711270657353563424">"Khetha i-\"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
+    <!-- no translation found for menu_select_all (4320518282375109902) -->
+    <skip />
     <string name="menu_advanced_show" product="nosdcard" msgid="4693652895715631401">"Bonisa isitoreji sangaphakathi"</string>
     <string name="menu_advanced_show" product="default" msgid="5792182900084144261">"Bonisa ikhadi le-SD"</string>
     <string name="menu_advanced_hide" product="nosdcard" msgid="4218809952721972589">"Fihla isitoreji sangaphakathi"</string>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index 268ce18..4ad337de 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -46,6 +46,8 @@
     <string name="menu_delete">Delete</string>
     <!-- Menu item title that selects the current directory [CHAR LIMIT=48] -->
     <string name="menu_select">Select \"<xliff:g id="directory" example="My Directory">^1</xliff:g>\"</string>
+    <!-- Menu item title that selects all documents in the current directory [CHAR LIMIT=24] -->
+    <string name="menu_select_all">Select All</string>
 
     <!-- Menu item that reveals internal storage built into the device [CHAR LIMIT=24] -->
     <string name="menu_advanced_show" product="nosdcard">Show internal storage</string>
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index 04692f6..bf01bf1 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -43,4 +43,22 @@
         <item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
     </style>
 
+    <style name="StandaloneTheme" parent="android:Theme.Light">
+        <item name="android:actionBarWidgetTheme">@null</item>
+        <item name="android:actionBarTheme">@*android:style/ThemeOverlay.Material.Dark.ActionBar</item>
+        <item name="android:actionBarPopupTheme">@*android:style/ThemeOverlay.Material.Light</item>
+
+        <item name="android:colorPrimaryDark">@*android:color/material_blue_grey_900</item>
+        <item name="android:colorPrimary">@*android:color/material_blue_grey_800</item>
+        <item name="android:colorAccent">@*android:color/material_deep_teal_500</item>
+
+        <item name="android:listDivider">@*android:drawable/list_divider_material</item>
+
+        <item name="android:windowActionBar">false</item>
+        <item name="android:windowActionModeOverlay">true</item>
+        <item name="android:windowNoTitle">true</item>
+
+        <item name="android:windowSoftInputMode">stateUnspecified|adjustUnspecified</item>
+    </style>
+
 </resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
new file mode 100644
index 0000000..a8a0c1d
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -0,0 +1,152 @@
+/*
+ * 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.documentsui;
+
+import java.util.HashMap;
+import java.util.List;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.pm.ResolveInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseArray;
+
+import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.DocumentStack;
+import com.android.documentsui.model.DurableUtils;
+import com.android.documentsui.model.RootInfo;
+import com.google.common.collect.Maps;
+
+abstract class BaseActivity extends Activity {
+
+    public abstract State getDisplayState();
+    public abstract RootInfo getCurrentRoot();
+    public abstract void onStateChanged();
+    public abstract void setRootsDrawerOpen(boolean open);
+    public abstract void onDocumentPicked(DocumentInfo doc);
+    public abstract void onDocumentsPicked(List<DocumentInfo> docs);
+    public abstract DocumentInfo getCurrentDirectory();
+    public abstract void setPending(boolean pending);
+    public abstract void onStackPicked(DocumentStack stack);
+    public abstract void onPickRequested(DocumentInfo pickTarget);
+    public abstract void onAppPicked(ResolveInfo info);
+    public abstract void onRootPicked(RootInfo root, boolean closeDrawer);
+    public abstract void onSaveRequested(DocumentInfo replaceTarget);
+    public abstract void onSaveRequested(String mimeType, String displayName);
+
+    public static BaseActivity get(Fragment fragment) {
+        return (BaseActivity) fragment.getActivity();
+    }
+
+    public static class State implements android.os.Parcelable {
+        public int action;
+        public String[] acceptMimes;
+
+        /** Explicit user choice */
+        public int userMode = MODE_UNKNOWN;
+        /** Derived after loader */
+        public int derivedMode = MODE_LIST;
+
+        /** Explicit user choice */
+        public int userSortOrder = SORT_ORDER_UNKNOWN;
+        /** Derived after loader */
+        public int derivedSortOrder = SORT_ORDER_DISPLAY_NAME;
+
+        public boolean allowMultiple = false;
+        public boolean showSize = false;
+        public boolean localOnly = false;
+        public boolean forceAdvanced = false;
+        public boolean showAdvanced = false;
+        public boolean stackTouched = false;
+        public boolean restored = false;
+
+        /** Current user navigation stack; empty implies recents. */
+        public DocumentStack stack = new DocumentStack();
+        /** Currently active search, overriding any stack. */
+        public String currentSearch;
+
+        /** Instance state for every shown directory */
+        public HashMap<String, SparseArray<Parcelable>> dirState = Maps.newHashMap();
+
+        public static final int ACTION_OPEN = 1;
+        public static final int ACTION_CREATE = 2;
+        public static final int ACTION_GET_CONTENT = 3;
+        public static final int ACTION_OPEN_TREE = 4;
+        public static final int ACTION_MANAGE = 5;
+        public static final int ACTION_MANAGE_ALL = 6;
+
+        public static final int MODE_UNKNOWN = 0;
+        public static final int MODE_LIST = 1;
+        public static final int MODE_GRID = 2;
+
+        public static final int SORT_ORDER_UNKNOWN = 0;
+        public static final int SORT_ORDER_DISPLAY_NAME = 1;
+        public static final int SORT_ORDER_LAST_MODIFIED = 2;
+        public static final int SORT_ORDER_SIZE = 3;
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeInt(action);
+            out.writeInt(userMode);
+            out.writeStringArray(acceptMimes);
+            out.writeInt(userSortOrder);
+            out.writeInt(allowMultiple ? 1 : 0);
+            out.writeInt(showSize ? 1 : 0);
+            out.writeInt(localOnly ? 1 : 0);
+            out.writeInt(forceAdvanced ? 1 : 0);
+            out.writeInt(showAdvanced ? 1 : 0);
+            out.writeInt(stackTouched ? 1 : 0);
+            out.writeInt(restored ? 1 : 0);
+            DurableUtils.writeToParcel(out, stack);
+            out.writeString(currentSearch);
+            out.writeMap(dirState);
+        }
+
+        public static final Creator<State> CREATOR = new Creator<State>() {
+            @Override
+            public State createFromParcel(Parcel in) {
+                final State state = new State();
+                state.action = in.readInt();
+                state.userMode = in.readInt();
+                state.acceptMimes = in.readStringArray();
+                state.userSortOrder = in.readInt();
+                state.allowMultiple = in.readInt() != 0;
+                state.showSize = in.readInt() != 0;
+                state.localOnly = in.readInt() != 0;
+                state.forceAdvanced = in.readInt() != 0;
+                state.showAdvanced = in.readInt() != 0;
+                state.stackTouched = in.readInt() != 0;
+                state.restored = in.readInt() != 0;
+                DurableUtils.readFromParcel(in, state.stack);
+                state.currentSearch = in.readString();
+                in.readMap(state.dirState, null);
+                return state;
+            }
+
+            @Override
+            public State[] newArray(int size) {
+                return new State[size];
+            }
+        };
+    }
+}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
index ba8c35f..1a17ee0 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/CreateDirectoryFragment.java
@@ -70,7 +70,7 @@
             public void onClick(DialogInterface dialog, int which) {
                 final String displayName = text1.getText().toString();
 
-                final DocumentsActivity activity = (DocumentsActivity) getActivity();
+                final BaseActivity activity = (BaseActivity) getActivity();
                 final DocumentInfo cwd = activity.getCurrentDirectory();
 
                 new CreateDirectoryTask(activity, cwd, displayName).executeOnExecutor(
@@ -83,12 +83,12 @@
     }
 
     private class CreateDirectoryTask extends AsyncTask<Void, Void, DocumentInfo> {
-        private final DocumentsActivity mActivity;
+        private final BaseActivity mActivity;
         private final DocumentInfo mCwd;
         private final String mDisplayName;
 
         public CreateDirectoryTask(
-                DocumentsActivity activity, DocumentInfo cwd, String displayName) {
+                BaseActivity activity, DocumentInfo cwd, String displayName) {
             mActivity = activity;
             mCwd = cwd;
             mDisplayName = displayName;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index 39c2252..a75dc42 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -17,12 +17,12 @@
 package com.android.documentsui;
 
 import static com.android.documentsui.DocumentsActivity.TAG;
-import static com.android.documentsui.DocumentsActivity.State.ACTION_CREATE;
-import static com.android.documentsui.DocumentsActivity.State.ACTION_MANAGE;
-import static com.android.documentsui.DocumentsActivity.State.MODE_GRID;
-import static com.android.documentsui.DocumentsActivity.State.MODE_LIST;
-import static com.android.documentsui.DocumentsActivity.State.MODE_UNKNOWN;
-import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_UNKNOWN;
+import static com.android.documentsui.BaseActivity.State.ACTION_CREATE;
+import static com.android.documentsui.BaseActivity.State.ACTION_MANAGE;
+import static com.android.documentsui.BaseActivity.State.MODE_GRID;
+import static com.android.documentsui.BaseActivity.State.MODE_LIST;
+import static com.android.documentsui.BaseActivity.State.MODE_UNKNOWN;
+import static com.android.documentsui.BaseActivity.State.SORT_ORDER_UNKNOWN;
 import static com.android.documentsui.model.DocumentInfo.getCursorInt;
 import static com.android.documentsui.model.DocumentInfo.getCursorLong;
 import static com.android.documentsui.model.DocumentInfo.getCursorString;
@@ -76,7 +76,7 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
-import com.android.documentsui.DocumentsActivity.State;
+import com.android.documentsui.BaseActivity.State;
 import com.android.documentsui.ProviderExecutor.Preemptable;
 import com.android.documentsui.RecentsProvider.StateColumns;
 import com.android.documentsui.model.DocumentInfo;
@@ -301,13 +301,13 @@
                     state.derivedMode = result.mode;
                 }
                 state.derivedSortOrder = result.sortOrder;
-                ((DocumentsActivity) context).onStateChanged();
+                ((BaseActivity) context).onStateChanged();
 
                 updateDisplayState();
 
                 // When launched into empty recents, show drawer
                 if (mType == TYPE_RECENT_OPEN && mAdapter.isEmpty() && !state.stackTouched) {
-                    ((DocumentsActivity) context).setRootsDrawerOpen(true);
+                    ((BaseActivity) context).setRootsDrawerOpen(true);
                 }
 
                 // Restore any previous instance state
@@ -386,7 +386,7 @@
         // Mode change is just visual change; no need to kick loader, and
         // deliver change event immediately.
         state.derivedMode = state.userMode;
-        ((DocumentsActivity) getActivity()).onStateChanged();
+        ((BaseActivity) getActivity()).onStateChanged();
 
         updateDisplayState();
     }
@@ -441,7 +441,7 @@
                 final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
                 if (isDocumentEnabled(docMimeType, docFlags)) {
                     final DocumentInfo doc = DocumentInfo.fromDirectoryCursor(cursor);
-                    ((DocumentsActivity) getActivity()).onDocumentPicked(doc);
+                    ((BaseActivity) getActivity()).onDocumentPicked(doc);
                 }
             }
         }
@@ -487,7 +487,7 @@
 
             final int id = item.getItemId();
             if (id == R.id.menu_open) {
-                DocumentsActivity.get(DirectoryFragment.this).onDocumentsPicked(docs);
+                BaseActivity.get(DirectoryFragment.this).onDocumentsPicked(docs);
                 mode.finish();
                 return true;
 
@@ -501,6 +501,14 @@
                 mode.finish();
                 return true;
 
+            } else if (id == R.id.menu_select_all) {
+                int count = mCurrentView.getCount();
+                for (int i = 0; i < count; i++) {
+                    mCurrentView.setItemChecked(i, true);
+                }
+                updateDisplayState();
+                return true;
+
             } else {
                 return false;
             }
@@ -616,7 +624,7 @@
     }
 
     private static State getDisplayState(Fragment fragment) {
-        return ((DocumentsActivity) fragment.getActivity()).getDisplayState();
+        return ((BaseActivity) fragment.getActivity()).getDisplayState();
     }
 
     private static abstract class Footer {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
index 163615d..8e4ec8c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryLoader.java
@@ -17,11 +17,11 @@
 package com.android.documentsui;
 
 import static com.android.documentsui.DocumentsActivity.TAG;
-import static com.android.documentsui.DocumentsActivity.State.MODE_UNKNOWN;
-import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_DISPLAY_NAME;
-import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_LAST_MODIFIED;
-import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_SIZE;
-import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_UNKNOWN;
+import static com.android.documentsui.BaseActivity.State.MODE_UNKNOWN;
+import static com.android.documentsui.BaseActivity.State.SORT_ORDER_DISPLAY_NAME;
+import static com.android.documentsui.BaseActivity.State.SORT_ORDER_LAST_MODIFIED;
+import static com.android.documentsui.BaseActivity.State.SORT_ORDER_SIZE;
+import static com.android.documentsui.BaseActivity.State.SORT_ORDER_UNKNOWN;
 import static com.android.documentsui.model.DocumentInfo.getCursorInt;
 
 import android.content.AsyncTaskLoader;
@@ -36,7 +36,7 @@
 import android.provider.DocumentsContract.Document;
 import android.util.Log;
 
-import com.android.documentsui.DocumentsActivity.State;
+import com.android.documentsui.BaseActivity.State;
 import com.android.documentsui.RecentsProvider.StateColumns;
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.RootInfo;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 8778f11..2245b16 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -20,14 +20,13 @@
 import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
 import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
 import static com.android.documentsui.DirectoryFragment.ANIM_UP;
-import static com.android.documentsui.DocumentsActivity.State.ACTION_CREATE;
-import static com.android.documentsui.DocumentsActivity.State.ACTION_GET_CONTENT;
-import static com.android.documentsui.DocumentsActivity.State.ACTION_MANAGE;
-import static com.android.documentsui.DocumentsActivity.State.ACTION_OPEN;
-import static com.android.documentsui.DocumentsActivity.State.ACTION_OPEN_TREE;
-import static com.android.documentsui.DocumentsActivity.State.MODE_GRID;
-import static com.android.documentsui.DocumentsActivity.State.MODE_LIST;
-
+import static com.android.documentsui.BaseActivity.State.ACTION_CREATE;
+import static com.android.documentsui.BaseActivity.State.ACTION_GET_CONTENT;
+import static com.android.documentsui.BaseActivity.State.ACTION_MANAGE;
+import static com.android.documentsui.BaseActivity.State.ACTION_OPEN;
+import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_TREE;
+import static com.android.documentsui.BaseActivity.State.MODE_GRID;
+import static com.android.documentsui.BaseActivity.State.MODE_LIST;
 import android.app.Activity;
 import android.app.Fragment;
 import android.app.FragmentManager;
@@ -46,15 +45,12 @@
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Root;
 import android.support.v4.app.ActionBarDrawerToggle;
 import android.support.v4.widget.DrawerLayout;
 import android.support.v4.widget.DrawerLayout.DrawerListener;
 import android.util.Log;
-import android.util.SparseArray;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -79,7 +75,6 @@
 import com.android.documentsui.model.DocumentStack;
 import com.android.documentsui.model.DurableUtils;
 import com.android.documentsui.model.RootInfo;
-import com.google.common.collect.Maps;
 
 import libcore.io.IoUtils;
 
@@ -87,11 +82,10 @@
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.List;
 import java.util.concurrent.Executor;
 
-public class DocumentsActivity extends Activity {
+public class DocumentsActivity extends BaseActivity {
     public static final String TAG = "Documents";
 
     private static final String EXTRA_STATE = "state";
@@ -203,8 +197,8 @@
             moreApps.setComponent(null);
             moreApps.setPackage(null);
             RootsFragment.show(getFragmentManager(), moreApps);
-        } else if (mState.action == ACTION_OPEN || mState.action == ACTION_CREATE
-                || mState.action == ACTION_OPEN_TREE) {
+        } else if (mState.action == ACTION_OPEN
+                || mState.action == ACTION_CREATE || mState.action == ACTION_OPEN_TREE) {
             RootsFragment.show(getFragmentManager(), null);
         }
 
@@ -389,6 +383,7 @@
         updateActionBar();
     }
 
+    @Override
     public void setRootsDrawerOpen(boolean open) {
         if (!mShowAsDialog) {
             if (open) {
@@ -667,9 +662,7 @@
         invalidateOptionsMenu();
     }
 
-    /**
-     * Update UI to reflect internal state changes not from user.
-     */
+    @Override
     public void onStateChanged() {
         invalidateOptionsMenu();
     }
@@ -690,6 +683,7 @@
         DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
     }
 
+    @Override
     public void setPending(boolean pending) {
         final SaveFragment save = SaveFragment.get(getFragmentManager());
         if (save != null) {
@@ -808,6 +802,7 @@
         }
     };
 
+    @Override
     public RootInfo getCurrentRoot() {
         if (mState.stack.root != null) {
             return mState.stack.root;
@@ -816,6 +811,7 @@
         }
     }
 
+    @Override
     public DocumentInfo getCurrentDirectory() {
         return mState.stack.peek();
     }
@@ -834,6 +830,7 @@
         }
     }
 
+    @Override
     public State getDisplayState() {
         return mState;
     }
@@ -895,6 +892,7 @@
         dumpStack();
     }
 
+    @Override
     public void onStackPicked(DocumentStack stack) {
         try {
             // Update the restored stack to ensure we have freshest data
@@ -909,6 +907,7 @@
         }
     }
 
+    @Override
     public void onRootPicked(RootInfo root, boolean closeDrawer) {
         // Clear entire backstack and start in new root
         mState.stack.root = root;
@@ -955,6 +954,7 @@
         }
     }
 
+    @Override
     public void onAppPicked(ResolveInfo info) {
         final Intent intent = new Intent(getIntent());
         intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
@@ -985,6 +985,7 @@
         }
     }
 
+    @Override
     public void onDocumentPicked(DocumentInfo doc) {
         final FragmentManager fm = getFragmentManager();
         if (doc.isDirectory()) {
@@ -1020,6 +1021,7 @@
         }
     }
 
+    @Override
     public void onDocumentsPicked(List<DocumentInfo> docs) {
         if (mState.action == ACTION_OPEN || mState.action == ACTION_GET_CONTENT) {
             final int size = docs.size();
@@ -1031,14 +1033,17 @@
         }
     }
 
+    @Override
     public void onSaveRequested(DocumentInfo replaceTarget) {
         new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
     }
 
+    @Override
     public void onSaveRequested(String mimeType, String displayName) {
         new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
     }
 
+    @Override
     public void onPickRequested(DocumentInfo pickTarget) {
         final Uri viaUri = DocumentsContract.buildTreeDocumentUri(pickTarget.authority,
                 pickTarget.documentId);
@@ -1188,102 +1193,6 @@
         }
     }
 
-    public static class State implements android.os.Parcelable {
-        public int action;
-        public String[] acceptMimes;
-
-        /** Explicit user choice */
-        public int userMode = MODE_UNKNOWN;
-        /** Derived after loader */
-        public int derivedMode = MODE_LIST;
-
-        /** Explicit user choice */
-        public int userSortOrder = SORT_ORDER_UNKNOWN;
-        /** Derived after loader */
-        public int derivedSortOrder = SORT_ORDER_DISPLAY_NAME;
-
-        public boolean allowMultiple = false;
-        public boolean showSize = false;
-        public boolean localOnly = false;
-        public boolean forceAdvanced = false;
-        public boolean showAdvanced = false;
-        public boolean stackTouched = false;
-        public boolean restored = false;
-
-        /** Current user navigation stack; empty implies recents. */
-        public DocumentStack stack = new DocumentStack();
-        /** Currently active search, overriding any stack. */
-        public String currentSearch;
-
-        /** Instance state for every shown directory */
-        public HashMap<String, SparseArray<Parcelable>> dirState = Maps.newHashMap();
-
-        public static final int ACTION_OPEN = 1;
-        public static final int ACTION_CREATE = 2;
-        public static final int ACTION_GET_CONTENT = 3;
-        public static final int ACTION_OPEN_TREE = 4;
-        public static final int ACTION_MANAGE = 5;
-
-        public static final int MODE_UNKNOWN = 0;
-        public static final int MODE_LIST = 1;
-        public static final int MODE_GRID = 2;
-
-        public static final int SORT_ORDER_UNKNOWN = 0;
-        public static final int SORT_ORDER_DISPLAY_NAME = 1;
-        public static final int SORT_ORDER_LAST_MODIFIED = 2;
-        public static final int SORT_ORDER_SIZE = 3;
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel out, int flags) {
-            out.writeInt(action);
-            out.writeInt(userMode);
-            out.writeStringArray(acceptMimes);
-            out.writeInt(userSortOrder);
-            out.writeInt(allowMultiple ? 1 : 0);
-            out.writeInt(showSize ? 1 : 0);
-            out.writeInt(localOnly ? 1 : 0);
-            out.writeInt(forceAdvanced ? 1 : 0);
-            out.writeInt(showAdvanced ? 1 : 0);
-            out.writeInt(stackTouched ? 1 : 0);
-            out.writeInt(restored ? 1 : 0);
-            DurableUtils.writeToParcel(out, stack);
-            out.writeString(currentSearch);
-            out.writeMap(dirState);
-        }
-
-        public static final Creator<State> CREATOR = new Creator<State>() {
-            @Override
-            public State createFromParcel(Parcel in) {
-                final State state = new State();
-                state.action = in.readInt();
-                state.userMode = in.readInt();
-                state.acceptMimes = in.readStringArray();
-                state.userSortOrder = in.readInt();
-                state.allowMultiple = in.readInt() != 0;
-                state.showSize = in.readInt() != 0;
-                state.localOnly = in.readInt() != 0;
-                state.forceAdvanced = in.readInt() != 0;
-                state.showAdvanced = in.readInt() != 0;
-                state.stackTouched = in.readInt() != 0;
-                state.restored = in.readInt() != 0;
-                DurableUtils.readFromParcel(in, state.stack);
-                state.currentSearch = in.readString();
-                in.readMap(state.dirState, null);
-                return state;
-            }
-
-            @Override
-            public State[] newArray(int size) {
-                return new State[size];
-            }
-        };
-    }
-
     private void dumpStack() {
         Log.d(TAG, "Current stack: ");
         Log.d(TAG, " * " + mState.stack.root);
@@ -1291,8 +1200,4 @@
             Log.d(TAG, " +-- " + doc);
         }
     }
-
-    public static DocumentsActivity get(Fragment fragment) {
-        return (DocumentsActivity) fragment.getActivity();
-    }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
index 5112c92..4b008ca 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/PickFragment.java
@@ -69,7 +69,7 @@
     private View.OnClickListener mPickListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
-            final DocumentsActivity activity = DocumentsActivity.get(PickFragment.this);
+            final BaseActivity activity = BaseActivity.get(PickFragment.this);
             activity.onPickRequested(mPickTarget);
         }
     };
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
index 34ce42d..f5908c5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java
@@ -17,7 +17,7 @@
 package com.android.documentsui;
 
 import static com.android.documentsui.DocumentsActivity.TAG;
-import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_LAST_MODIFIED;
+import static com.android.documentsui.BaseActivity.State.SORT_ORDER_LAST_MODIFIED;
 
 import android.app.ActivityManager;
 import android.content.AsyncTaskLoader;
@@ -34,7 +34,7 @@
 import android.text.format.DateUtils;
 import android.util.Log;
 
-import com.android.documentsui.DocumentsActivity.State;
+import com.android.documentsui.BaseActivity.State;
 import com.android.documentsui.model.RootInfo;
 import com.google.android.collect.Maps;
 import com.google.common.collect.Lists;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
index dd75dbd..26aecc5 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsCreateFragment.java
@@ -45,7 +45,7 @@
 import android.widget.ListView;
 import android.widget.TextView;
 
-import com.android.documentsui.DocumentsActivity.State;
+import com.android.documentsui.BaseActivity.State;
 import com.android.documentsui.RecentsProvider.RecentColumns;
 import com.android.documentsui.model.DocumentStack;
 import com.android.documentsui.model.DurableUtils;
@@ -95,7 +95,7 @@
         mListView.setAdapter(mAdapter);
 
         final RootsCache roots = DocumentsApplication.getRootsCache(context);
-        final State state = ((DocumentsActivity) getActivity()).getDisplayState();
+        final State state = ((BaseActivity) getActivity()).getDisplayState();
 
         mCallbacks = new LoaderCallbacks<List<DocumentStack>>() {
             @Override
@@ -110,7 +110,7 @@
 
                 // When launched into empty recents, show drawer
                 if (mAdapter.isEmpty() && !state.stackTouched) {
-                    ((DocumentsActivity) context).setRootsDrawerOpen(true);
+                    ((BaseActivity) context).setRootsDrawerOpen(true);
                 }
             }
 
@@ -139,7 +139,7 @@
         @Override
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
             final DocumentStack stack = mAdapter.getItem(position);
-            ((DocumentsActivity) getActivity()).onStackPicked(stack);
+            ((BaseActivity) getActivity()).onStackPicked(stack);
         }
     };
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
index d72db1d..ec71a04 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsCache.java
@@ -36,7 +36,7 @@
 import android.provider.DocumentsContract.Root;
 import android.util.Log;
 
-import com.android.documentsui.DocumentsActivity.State;
+import com.android.documentsui.BaseActivity.State;
 import com.android.documentsui.model.RootInfo;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index 884cf31..ed5e123 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -16,8 +16,6 @@
 
 package com.android.documentsui;
 
-import static com.android.documentsui.DocumentsActivity.State.ACTION_GET_CONTENT;
-
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
@@ -43,7 +41,7 @@
 import android.widget.ListView;
 import android.widget.TextView;
 
-import com.android.documentsui.DocumentsActivity.State;
+import com.android.documentsui.BaseActivity.State;
 import com.android.documentsui.model.DocumentInfo;
 import com.android.documentsui.model.RootInfo;
 import com.google.common.collect.Lists;
@@ -101,7 +99,7 @@
 
         final Context context = getActivity();
         final RootsCache roots = DocumentsApplication.getRootsCache(context);
-        final State state = ((DocumentsActivity) context).getDisplayState();
+        final State state = ((BaseActivity) context).getDisplayState();
 
         mCallbacks = new LoaderCallbacks<Collection<RootInfo>>() {
             @Override
@@ -138,9 +136,9 @@
 
     public void onDisplayStateChanged() {
         final Context context = getActivity();
-        final State state = ((DocumentsActivity) context).getDisplayState();
+        final State state = ((BaseActivity) context).getDisplayState();
 
-        if (state.action == ACTION_GET_CONTENT) {
+        if (state.action == State.ACTION_GET_CONTENT) {
             mList.setOnItemLongClickListener(mItemLongClickListener);
         } else {
             mList.setOnItemLongClickListener(null);
@@ -153,7 +151,7 @@
     public void onCurrentRootChanged() {
         if (mAdapter == null) return;
 
-        final RootInfo root = ((DocumentsActivity) getActivity()).getCurrentRoot();
+        final RootInfo root = ((BaseActivity) getActivity()).getCurrentRoot();
         for (int i = 0; i < mAdapter.getCount(); i++) {
             final Object item = mAdapter.getItem(i);
             if (item instanceof RootItem) {
@@ -176,7 +174,7 @@
     private OnItemClickListener mItemListener = new OnItemClickListener() {
         @Override
         public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-            final DocumentsActivity activity = DocumentsActivity.get(RootsFragment.this);
+            final BaseActivity activity = BaseActivity.get(RootsFragment.this);
             final Item item = mAdapter.getItem(position);
             if (item instanceof RootItem) {
                 activity.onRootPicked(((RootItem) item).root, true);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java
index 8d37cdf..49651b4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsLoader.java
@@ -19,7 +19,7 @@
 import android.content.AsyncTaskLoader;
 import android.content.Context;
 
-import com.android.documentsui.DocumentsActivity.State;
+import com.android.documentsui.BaseActivity.State;
 import com.android.documentsui.model.RootInfo;
 
 import java.util.Collection;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
index ce98db2..a13fccc 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SaveFragment.java
@@ -113,7 +113,7 @@
     private View.OnClickListener mSaveListener = new View.OnClickListener() {
         @Override
         public void onClick(View v) {
-            final DocumentsActivity activity = DocumentsActivity.get(SaveFragment.this);
+            final BaseActivity activity = BaseActivity.get(SaveFragment.this);
             if (mReplaceTarget != null) {
                 activity.onSaveRequested(mReplaceTarget);
             } else {
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
index 6c8ca20..3ec3d1c 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SortingCursorWrapper.java
@@ -16,9 +16,9 @@
 
 package com.android.documentsui;
 
-import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_DISPLAY_NAME;
-import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_LAST_MODIFIED;
-import static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_SIZE;
+import static com.android.documentsui.BaseActivity.State.SORT_ORDER_DISPLAY_NAME;
+import static com.android.documentsui.BaseActivity.State.SORT_ORDER_LAST_MODIFIED;
+import static com.android.documentsui.BaseActivity.State.SORT_ORDER_SIZE;
 import static com.android.documentsui.model.DocumentInfo.getCursorLong;
 import static com.android.documentsui.model.DocumentInfo.getCursorString;
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
new file mode 100644
index 0000000..e01328d
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/StandaloneActivity.java
@@ -0,0 +1,952 @@
+/*
+ * 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.documentsui;
+
+
+import static com.android.documentsui.DirectoryFragment.ANIM_DOWN;
+import static com.android.documentsui.DirectoryFragment.ANIM_NONE;
+import static com.android.documentsui.DirectoryFragment.ANIM_SIDE;
+import static com.android.documentsui.DirectoryFragment.ANIM_UP;
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.content.ActivityNotFoundException;
+import android.content.ClipData;
+import android.content.ComponentName;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Point;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Debug;
+import android.provider.DocumentsContract;
+import android.support.v4.app.ActionBarDrawerToggle;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v4.widget.DrawerLayout.DrawerListener;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MenuItem.OnActionExpandListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.SearchView;
+import android.widget.SearchView.OnQueryTextListener;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.Toolbar;
+
+import com.android.documentsui.RecentsProvider.ResumeColumns;
+import com.android.documentsui.model.DocumentInfo;
+import com.android.documentsui.model.DocumentStack;
+import com.android.documentsui.model.DurableUtils;
+import com.android.documentsui.model.RootInfo;
+
+import libcore.io.IoUtils;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+public class StandaloneActivity extends BaseActivity {
+    public static final String TAG = "StandaloneFileManagement";
+
+    private static final String EXTRA_STATE = "state";
+
+    private static final int CODE_FORWARD = 42;
+
+    private SearchView mSearchView;
+
+    private Toolbar mToolbar;
+    private Spinner mToolbarStack;
+
+    private Toolbar mRootsToolbar;
+
+    private ActionBarDrawerToggle mDrawerToggle;
+
+    private DirectoryContainerView mDirectoryContainer;
+
+    private boolean mIgnoreNextNavigation;
+    private boolean mIgnoreNextClose;
+    private boolean mIgnoreNextCollapse;
+
+    private boolean mSearchExpanded;
+
+    private RootsCache mRoots;
+    private State mState;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        // Debug.waitForDebugger();
+        super.onCreate(icicle);
+
+        mRoots = DocumentsApplication.getRootsCache(this);
+
+        setResult(Activity.RESULT_CANCELED);
+        setContentView(R.layout.activity);
+
+        final Context context = this;
+        final Resources res = getResources();
+
+        // Strongly define our horizontal dimension; we leave vertical as
+        final WindowManager.LayoutParams a = getWindow().getAttributes();
+
+        final Point size = new Point();
+        getWindowManager().getDefaultDisplay().getSize(size);
+        // a.width = (int) res.getFraction(R.dimen.dialog_width, size.x, size.x);
+
+        getWindow().setAttributes(a);
+
+        mDirectoryContainer = (DirectoryContainerView) findViewById(R.id.container_directory);
+
+        if (icicle != null) {
+            mState = icicle.getParcelable(EXTRA_STATE);
+        } else {
+            buildDefaultState();
+        }
+
+        mToolbar = (Toolbar) findViewById(R.id.toolbar);
+        mToolbar.setTitleTextAppearance(context,
+                android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
+
+        mToolbarStack = (Spinner) findViewById(R.id.stack);
+        mToolbarStack.setOnItemSelectedListener(mStackListener);
+
+        mRootsToolbar = (Toolbar) findViewById(R.id.roots_toolbar);
+        if (mRootsToolbar != null) {
+            mRootsToolbar.setTitleTextAppearance(context,
+                    android.R.style.TextAppearance_DeviceDefault_Widget_ActionBar_Title);
+        }
+
+        setActionBar(mToolbar);
+
+        RootsFragment.show(getFragmentManager(), null);
+        if (!mState.restored) {
+            new RestoreStackTask().execute();
+        } else {
+            onCurrentDirectoryChanged(ANIM_NONE);
+        }
+    }
+
+    private void buildDefaultState() {
+        mState = new State();
+
+        final Intent intent = getIntent();
+        mState.action = State.ACTION_MANAGE_ALL;
+        mState.acceptMimes = new String[] { "*/*" };
+        mState.allowMultiple = true;
+        mState.acceptMimes = new String[] { intent.getType() };
+        mState.localOnly = intent.getBooleanExtra(Intent.EXTRA_LOCAL_ONLY, false);
+        mState.forceAdvanced = intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
+        mState.showAdvanced = mState.forceAdvanced
+                | LocalPreferences.getDisplayAdvancedDevices(this);
+        mState.showSize = true;
+    }
+
+    private class RestoreRootTask extends AsyncTask<Void, Void, RootInfo> {
+        private Uri mRootUri;
+
+        public RestoreRootTask(Uri rootUri) {
+            mRootUri = rootUri;
+        }
+
+        @Override
+        protected RootInfo doInBackground(Void... params) {
+            final String rootId = DocumentsContract.getRootId(mRootUri);
+            return mRoots.getRootOneshot(mRootUri.getAuthority(), rootId);
+        }
+
+        @Override
+        protected void onPostExecute(RootInfo root) {
+            if (isDestroyed()) return;
+            mState.restored = true;
+
+            if (root != null) {
+                onRootPicked(root, true);
+            } else {
+                Log.w(TAG, "Failed to find root: " + mRootUri);
+                finish();
+            }
+        }
+    }
+
+    private class RestoreStackTask extends AsyncTask<Void, Void, Void> {
+        private volatile boolean mRestoredStack;
+        private volatile boolean mExternal;
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            // Restore last stack for calling package
+            final String packageName = getCallingPackageMaybeExtra();
+            final Cursor cursor = getContentResolver()
+                    .query(RecentsProvider.buildResume(packageName), null, null, null, null);
+            try {
+                if (cursor.moveToFirst()) {
+                    mExternal = cursor.getInt(cursor.getColumnIndex(ResumeColumns.EXTERNAL)) != 0;
+                    final byte[] rawStack = cursor.getBlob(
+                            cursor.getColumnIndex(ResumeColumns.STACK));
+                    DurableUtils.readFromArray(rawStack, mState.stack);
+                    mRestoredStack = true;
+                }
+            } catch (IOException e) {
+                Log.w(TAG, "Failed to resume: " + e);
+            } finally {
+                IoUtils.closeQuietly(cursor);
+            }
+
+            if (mRestoredStack) {
+                // Update the restored stack to ensure we have freshest data
+                final Collection<RootInfo> matchingRoots = mRoots.getMatchingRootsBlocking(mState);
+                try {
+                    mState.stack.updateRoot(matchingRoots);
+                    mState.stack.updateDocuments(getContentResolver());
+                } catch (FileNotFoundException e) {
+                    Log.w(TAG, "Failed to restore stack: " + e);
+                    mState.stack.reset();
+                    mRestoredStack = false;
+                }
+            }
+
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            if (isDestroyed()) return;
+            mState.restored = true;
+            onCurrentDirectoryChanged(ANIM_NONE);
+        }
+    }
+
+    @Override
+    protected void onPostCreate(Bundle savedInstanceState) {
+        super.onPostCreate(savedInstanceState);
+        if (mDrawerToggle != null) {
+            mDrawerToggle.syncState();
+        }
+        updateActionBar();
+    }
+
+    @Override
+    public void setRootsDrawerOpen(boolean open) {
+        Log.w(TAG, "Trying to change state of roots drawer to > " + (open ? "open" : "closed"));
+      // throw new UnsupportedOperationException();
+    }
+
+    public void updateActionBar() {
+        final RootInfo root = getCurrentRoot();
+        mToolbar.setNavigationIcon(
+                root != null ? root.loadToolbarIcon(mToolbar.getContext()) : null);
+        mToolbar.setNavigationContentDescription(R.string.drawer_open);
+        mToolbar.setNavigationOnClickListener(null);
+
+        if (mSearchExpanded) {
+            mToolbar.setTitle(null);
+            mToolbarStack.setVisibility(View.GONE);
+            mToolbarStack.setAdapter(null);
+        } else {
+            if (mState.stack.size() <= 1) {
+                mToolbar.setTitle(root.title);
+                mToolbarStack.setVisibility(View.GONE);
+                mToolbarStack.setAdapter(null);
+            } else {
+                mToolbar.setTitle(null);
+                mToolbarStack.setVisibility(View.VISIBLE);
+                mToolbarStack.setAdapter(mStackAdapter);
+
+                mIgnoreNextNavigation = true;
+                mToolbarStack.setSelection(mStackAdapter.getCount() - 1);
+            }
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        super.onCreateOptionsMenu(menu);
+        getMenuInflater().inflate(R.menu.activity, menu);
+
+        for (int i = 0; i < menu.size(); i++) {
+            final MenuItem item = menu.getItem(i);
+            switch (item.getItemId()) {
+                case R.id.menu_advanced:
+                case R.id.menu_file_size:
+                    break;
+                default:
+                    item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+            }
+        }
+
+        final MenuItem searchMenu = menu.findItem(R.id.menu_search);
+        mSearchView = (SearchView) searchMenu.getActionView();
+        mSearchView.setOnQueryTextListener(new OnQueryTextListener() {
+            @Override
+            public boolean onQueryTextSubmit(String query) {
+                mSearchExpanded = true;
+                mState.currentSearch = query;
+                mSearchView.clearFocus();
+                onCurrentDirectoryChanged(ANIM_NONE);
+                return true;
+            }
+
+            @Override
+            public boolean onQueryTextChange(String newText) {
+                return false;
+            }
+        });
+
+        searchMenu.setOnActionExpandListener(new OnActionExpandListener() {
+            @Override
+            public boolean onMenuItemActionExpand(MenuItem item) {
+                mSearchExpanded = true;
+                updateActionBar();
+                return true;
+            }
+
+            @Override
+            public boolean onMenuItemActionCollapse(MenuItem item) {
+                mSearchExpanded = false;
+                if (mIgnoreNextCollapse) {
+                    mIgnoreNextCollapse = false;
+                    return true;
+                }
+
+                mState.currentSearch = null;
+                onCurrentDirectoryChanged(ANIM_NONE);
+                return true;
+            }
+        });
+
+        mSearchView.setOnCloseListener(new SearchView.OnCloseListener() {
+            @Override
+            public boolean onClose() {
+                mSearchExpanded = false;
+                if (mIgnoreNextClose) {
+                    mIgnoreNextClose = false;
+                    return false;
+                }
+
+                mState.currentSearch = null;
+                onCurrentDirectoryChanged(ANIM_NONE);
+                return false;
+            }
+        });
+
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        super.onPrepareOptionsMenu(menu);
+
+        final FragmentManager fm = getFragmentManager();
+
+        final RootInfo root = getCurrentRoot();
+        final DocumentInfo cwd = getCurrentDirectory();
+
+        final MenuItem createDir = menu.findItem(R.id.menu_create_dir);
+        final MenuItem search = menu.findItem(R.id.menu_search);
+        final MenuItem sort = menu.findItem(R.id.menu_sort);
+        final MenuItem sortSize = menu.findItem(R.id.menu_sort_size);
+        final MenuItem grid = menu.findItem(R.id.menu_grid);
+        final MenuItem list = menu.findItem(R.id.menu_list);
+        final MenuItem advanced = menu.findItem(R.id.menu_advanced);
+        final MenuItem fileSize = menu.findItem(R.id.menu_file_size);
+
+        sort.setVisible(cwd != null);
+        grid.setVisible(mState.derivedMode != State.MODE_GRID);
+        list.setVisible(mState.derivedMode != State.MODE_LIST);
+
+        if (mState.currentSearch != null) {
+            // Search uses backend ranking; no sorting
+            sort.setVisible(false);
+
+            search.expandActionView();
+
+            mSearchView.setIconified(false);
+            mSearchView.clearFocus();
+            mSearchView.setQuery(mState.currentSearch, false);
+        } else {
+            mIgnoreNextClose = true;
+            mSearchView.setIconified(true);
+            mSearchView.clearFocus();
+
+            mIgnoreNextCollapse = true;
+            search.collapseActionView();
+        }
+
+        // Only sort by size when visible
+        sortSize.setVisible(mState.showSize);
+
+        fileSize.setVisible(true);
+        search.setVisible(true);
+        createDir.setVisible(true);
+        advanced.setVisible(true);
+
+        advanced.setTitle(LocalPreferences.getDisplayAdvancedDevices(this)
+                ? R.string.menu_advanced_hide : R.string.menu_advanced_show);
+        fileSize.setTitle(LocalPreferences.getDisplayFileSize(this)
+                ? R.string.menu_file_size_hide : R.string.menu_file_size_show);
+
+
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (mDrawerToggle != null && mDrawerToggle.onOptionsItemSelected(item)) {
+            return true;
+        }
+
+        final int id = item.getItemId();
+        if (id == android.R.id.home) {
+            onBackPressed();
+            return true;
+        } else if (id == R.id.menu_create_dir) {
+            CreateDirectoryFragment.show(getFragmentManager());
+            return true;
+        } else if (id == R.id.menu_search) {
+            return false;
+        } else if (id == R.id.menu_sort_name) {
+            setUserSortOrder(State.SORT_ORDER_DISPLAY_NAME);
+            return true;
+        } else if (id == R.id.menu_sort_date) {
+            setUserSortOrder(State.SORT_ORDER_LAST_MODIFIED);
+            return true;
+        } else if (id == R.id.menu_sort_size) {
+            setUserSortOrder(State.SORT_ORDER_SIZE);
+            return true;
+        } else if (id == R.id.menu_grid) {
+            setUserMode(State.MODE_GRID);
+            return true;
+        } else if (id == R.id.menu_list) {
+            setUserMode(State.MODE_LIST);
+            return true;
+        } else if (id == R.id.menu_advanced) {
+            setDisplayAdvancedDevices(!LocalPreferences.getDisplayAdvancedDevices(this));
+            return true;
+        } else if (id == R.id.menu_file_size) {
+            setDisplayFileSize(!LocalPreferences.getDisplayFileSize(this));
+            return true;
+        } else {
+            return super.onOptionsItemSelected(item);
+        }
+    }
+
+    private void setDisplayAdvancedDevices(boolean display) {
+        LocalPreferences.setDisplayAdvancedDevices(this, display);
+        mState.showAdvanced = mState.forceAdvanced | display;
+        RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
+        invalidateOptionsMenu();
+    }
+
+    private void setDisplayFileSize(boolean display) {
+        LocalPreferences.setDisplayFileSize(this, display);
+        mState.showSize = display;
+        DirectoryFragment.get(getFragmentManager()).onDisplayStateChanged();
+        invalidateOptionsMenu();
+    }
+
+    @Override
+    public void onStateChanged() {
+        invalidateOptionsMenu();
+    }
+
+    /**
+     * Set state sort order based on explicit user action.
+     */
+    private void setUserSortOrder(int sortOrder) {
+        mState.userSortOrder = sortOrder;
+        DirectoryFragment.get(getFragmentManager()).onUserSortOrderChanged();
+    }
+
+    /**
+     * Set state mode based on explicit user action.
+     */
+    private void setUserMode(int mode) {
+        mState.userMode = mode;
+        DirectoryFragment.get(getFragmentManager()).onUserModeChanged();
+    }
+
+    @Override
+    public void setPending(boolean pending) {
+        final SaveFragment save = SaveFragment.get(getFragmentManager());
+        if (save != null) {
+            save.setPending(pending);
+        }
+    }
+
+    @Override
+    public void onBackPressed() {
+        if (!mState.stackTouched) {
+            super.onBackPressed();
+            return;
+        }
+
+        final int size = mState.stack.size();
+        if (size > 1) {
+            mState.stack.pop();
+            onCurrentDirectoryChanged(ANIM_UP);
+        } else {
+            super.onBackPressed();
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle state) {
+        super.onSaveInstanceState(state);
+        state.putParcelable(EXTRA_STATE, mState);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle state) {
+        super.onRestoreInstanceState(state);
+    }
+
+    private BaseAdapter mStackAdapter = new BaseAdapter() {
+        @Override
+        public int getCount() {
+            return mState.stack.size();
+        }
+
+        @Override
+        public DocumentInfo getItem(int position) {
+            return mState.stack.get(mState.stack.size() - position - 1);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = LayoutInflater.from(parent.getContext())
+                        .inflate(R.layout.item_subdir_title, parent, false);
+            }
+
+            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
+            final DocumentInfo doc = getItem(position);
+
+            if (position == 0) {
+                final RootInfo root = getCurrentRoot();
+                title.setText(root.title);
+            } else {
+                title.setText(doc.displayName);
+            }
+
+            return convertView;
+        }
+
+        @Override
+        public View getDropDownView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = LayoutInflater.from(parent.getContext())
+                        .inflate(R.layout.item_subdir, parent, false);
+            }
+
+            final ImageView subdir = (ImageView) convertView.findViewById(R.id.subdir);
+            final TextView title = (TextView) convertView.findViewById(android.R.id.title);
+            final DocumentInfo doc = getItem(position);
+
+            if (position == 0) {
+                final RootInfo root = getCurrentRoot();
+                title.setText(root.title);
+                subdir.setVisibility(View.GONE);
+            } else {
+                title.setText(doc.displayName);
+                subdir.setVisibility(View.VISIBLE);
+            }
+
+            return convertView;
+        }
+    };
+
+    private OnItemSelectedListener mStackListener = new OnItemSelectedListener() {
+        @Override
+        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+            if (mIgnoreNextNavigation) {
+                mIgnoreNextNavigation = false;
+                return;
+            }
+
+            while (mState.stack.size() > position + 1) {
+                mState.stackTouched = true;
+                mState.stack.pop();
+            }
+            onCurrentDirectoryChanged(ANIM_UP);
+        }
+
+        @Override
+        public void onNothingSelected(AdapterView<?> parent) {
+            // Ignored
+        }
+    };
+
+    @Override
+    public RootInfo getCurrentRoot() {
+        if (mState.stack.root != null) {
+            return mState.stack.root;
+        } else {
+            return mRoots.getRecentsRoot();
+        }
+    }
+
+    public DocumentInfo getCurrentDirectory() {
+        return mState.stack.peek();
+    }
+
+    private String getCallingPackageMaybeExtra() {
+        final String extra = getIntent().getStringExtra(DocumentsContract.EXTRA_PACKAGE_NAME);
+        return (extra != null) ? extra : getCallingPackage();
+    }
+
+    public Executor getCurrentExecutor() {
+        final DocumentInfo cwd = getCurrentDirectory();
+        if (cwd != null && cwd.authority != null) {
+            return ProviderExecutor.forAuthority(cwd.authority);
+        } else {
+            return AsyncTask.THREAD_POOL_EXECUTOR;
+        }
+    }
+
+    @Override
+    public State getDisplayState() {
+        return mState;
+    }
+
+    private void onCurrentDirectoryChanged(int anim) {
+        final FragmentManager fm = getFragmentManager();
+        final RootInfo root = getCurrentRoot();
+        final DocumentInfo cwd = getCurrentDirectory();
+
+        mDirectoryContainer.setDrawDisappearingFirst(anim == ANIM_DOWN);
+
+        if (cwd == null) {
+            DirectoryFragment.showRecentsOpen(fm, anim);
+
+            // Start recents in grid when requesting visual things
+            final boolean visualMimes = MimePredicate.mimeMatches(
+                    MimePredicate.VISUAL_MIMES, mState.acceptMimes);
+            mState.userMode = visualMimes ? State.MODE_GRID : State.MODE_LIST;
+            mState.derivedMode = mState.userMode;
+        } else {
+            if (mState.currentSearch != null) {
+                // Ongoing search
+                DirectoryFragment.showSearch(fm, root, mState.currentSearch, anim);
+            } else {
+                // Normal boring directory
+                DirectoryFragment.showNormal(fm, root, cwd, anim);
+            }
+        }
+
+        final RootsFragment roots = RootsFragment.get(fm);
+        if (roots != null) {
+            roots.onCurrentRootChanged();
+        }
+
+        updateActionBar();
+        invalidateOptionsMenu();
+        dumpStack();
+    }
+
+    @Override
+    public void onStackPicked(DocumentStack stack) {
+        try {
+            // Update the restored stack to ensure we have freshest data
+            stack.updateDocuments(getContentResolver());
+
+            mState.stack = stack;
+            mState.stackTouched = true;
+            onCurrentDirectoryChanged(ANIM_SIDE);
+
+        } catch (FileNotFoundException e) {
+            Log.w(TAG, "Failed to restore stack: " + e);
+        }
+    }
+
+    @Override
+    public void onRootPicked(RootInfo root, boolean closeDrawer) {
+        // Clear entire backstack and start in new root
+        mState.stack.root = root;
+        mState.stack.clear();
+        mState.stackTouched = true;
+
+        if (!mRoots.isRecentsRoot(root)) {
+            new PickRootTask(root).executeOnExecutor(getCurrentExecutor());
+        } else {
+            onCurrentDirectoryChanged(ANIM_SIDE);
+        }
+    }
+
+    private class PickRootTask extends AsyncTask<Void, Void, DocumentInfo> {
+        private RootInfo mRoot;
+
+        public PickRootTask(RootInfo root) {
+            mRoot = root;
+        }
+
+        @Override
+        protected DocumentInfo doInBackground(Void... params) {
+            try {
+                final Uri uri = DocumentsContract.buildDocumentUri(
+                        mRoot.authority, mRoot.documentId);
+                return DocumentInfo.fromUri(getContentResolver(), uri);
+            } catch (FileNotFoundException e) {
+                Log.w(TAG, "Failed to find root", e);
+                return null;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(DocumentInfo result) {
+            if (result != null) {
+                mState.stack.push(result);
+                mState.stackTouched = true;
+                onCurrentDirectoryChanged(ANIM_SIDE);
+            }
+        }
+    }
+
+    @Override
+    public void onAppPicked(ResolveInfo info) {
+        final Intent intent = new Intent(getIntent());
+        intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+        intent.setComponent(new ComponentName(
+                info.activityInfo.applicationInfo.packageName, info.activityInfo.name));
+        startActivityForResult(intent, CODE_FORWARD);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        Log.d(TAG, "onActivityResult() code=" + resultCode);
+
+        // Only relay back results when not canceled; otherwise stick around to
+        // let the user pick another app/backend.
+        if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) {
+
+            // Remember that we last picked via external app
+            final String packageName = getCallingPackageMaybeExtra();
+            final ContentValues values = new ContentValues();
+            values.put(ResumeColumns.EXTERNAL, 1);
+            getContentResolver().insert(RecentsProvider.buildResume(packageName), values);
+
+            // Pass back result to original caller
+            setResult(resultCode, data);
+            finish();
+        } else {
+            super.onActivityResult(requestCode, resultCode, data);
+        }
+    }
+
+    @Override
+    public void onDocumentPicked(DocumentInfo doc) {
+        final FragmentManager fm = getFragmentManager();
+        if (doc.isDirectory()) {
+            mState.stack.push(doc);
+            mState.stackTouched = true;
+            onCurrentDirectoryChanged(ANIM_DOWN);
+        } else {
+            // Fall back to viewing
+            final Intent view = new Intent(Intent.ACTION_VIEW);
+            view.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            view.setData(doc.derivedUri);
+
+            try {
+                startActivity(view);
+            } catch (ActivityNotFoundException ex2) {
+                Toast.makeText(this, R.string.toast_no_application, Toast.LENGTH_SHORT).show();
+            }
+        }
+    }
+
+    public void onDocumentsPicked(List<DocumentInfo> docs) {
+        // TODO
+    }
+
+    @Override
+    public void onSaveRequested(DocumentInfo replaceTarget) {
+        new ExistingFinishTask(replaceTarget.derivedUri).executeOnExecutor(getCurrentExecutor());
+    }
+
+    @Override
+    public void onSaveRequested(String mimeType, String displayName) {
+        new CreateFinishTask(mimeType, displayName).executeOnExecutor(getCurrentExecutor());
+    }
+
+    @Override
+    public void onPickRequested(DocumentInfo pickTarget) {
+        final Uri viaUri = DocumentsContract.buildTreeDocumentUri(pickTarget.authority,
+                pickTarget.documentId);
+        new PickFinishTask(viaUri).executeOnExecutor(getCurrentExecutor());
+    }
+
+    private void saveStackBlocking() {
+        final ContentResolver resolver = getContentResolver();
+        final ContentValues values = new ContentValues();
+
+        final byte[] rawStack = DurableUtils.writeToArrayOrNull(mState.stack);
+
+        // Remember location for next app launch
+        final String packageName = getCallingPackageMaybeExtra();
+        values.clear();
+        values.put(ResumeColumns.STACK, rawStack);
+        values.put(ResumeColumns.EXTERNAL, 0);
+        resolver.insert(RecentsProvider.buildResume(packageName), values);
+    }
+
+    private void onFinished(Uri... uris) {
+        Log.d(TAG, "onFinished() " + Arrays.toString(uris));
+
+        final Intent intent = new Intent();
+        if (uris.length == 1) {
+            intent.setData(uris[0]);
+        } else if (uris.length > 1) {
+            final ClipData clipData = new ClipData(
+                    null, mState.acceptMimes, new ClipData.Item(uris[0]));
+            for (int i = 1; i < uris.length; i++) {
+                clipData.addItem(new ClipData.Item(uris[i]));
+            }
+            intent.setClipData(clipData);
+        }
+
+        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
+
+        setResult(Activity.RESULT_OK, intent);
+        finish();
+    }
+
+    private class CreateFinishTask extends AsyncTask<Void, Void, Uri> {
+        private final String mMimeType;
+        private final String mDisplayName;
+
+        public CreateFinishTask(String mimeType, String displayName) {
+            mMimeType = mimeType;
+            mDisplayName = displayName;
+        }
+
+        @Override
+        protected void onPreExecute() {
+            setPending(true);
+        }
+
+        @Override
+        protected Uri doInBackground(Void... params) {
+            final ContentResolver resolver = getContentResolver();
+            final DocumentInfo cwd = getCurrentDirectory();
+
+            ContentProviderClient client = null;
+            Uri childUri = null;
+            try {
+                client = DocumentsApplication.acquireUnstableProviderOrThrow(
+                        resolver, cwd.derivedUri.getAuthority());
+                childUri = DocumentsContract.createDocument(
+                        client, cwd.derivedUri, mMimeType, mDisplayName);
+            } catch (Exception e) {
+                Log.w(TAG, "Failed to create document", e);
+            } finally {
+                ContentProviderClient.releaseQuietly(client);
+            }
+
+            if (childUri != null) {
+                saveStackBlocking();
+            }
+
+            return childUri;
+        }
+
+        @Override
+        protected void onPostExecute(Uri result) {
+            if (result != null) {
+                onFinished(result);
+            } else {
+                Toast.makeText(StandaloneActivity.this, R.string.save_error, Toast.LENGTH_SHORT)
+                        .show();
+            }
+
+            setPending(false);
+        }
+    }
+
+    private class ExistingFinishTask extends AsyncTask<Void, Void, Void> {
+        private final Uri[] mUris;
+
+        public ExistingFinishTask(Uri... uris) {
+            mUris = uris;
+        }
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            saveStackBlocking();
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            onFinished(mUris);
+        }
+    }
+
+    private class PickFinishTask extends AsyncTask<Void, Void, Void> {
+        private final Uri mUri;
+
+        public PickFinishTask(Uri uri) {
+            mUri = uri;
+        }
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            saveStackBlocking();
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            onFinished(mUri);
+        }
+    }
+
+    private void dumpStack() {
+        Log.d(TAG, "Current stack: ");
+        Log.d(TAG, " * " + mState.stack.root);
+        for (DocumentInfo doc : mState.stack) {
+            Log.d(TAG, " +-- " + doc);
+        }
+    }
+
+    public static BaseActivity get(Fragment fragment) {
+        return (BaseActivity) fragment.getActivity();
+    }
+}
diff --git a/packages/IntentFilterVerifier/Android.mk b/packages/IntentFilterVerifier/Android.mk
new file mode 100644
index 0000000..99feda5
--- /dev/null
+++ b/packages/IntentFilterVerifier/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH:= $(call my-dir)
+
+# Build the IntentFilterVerifier.
+include $(CLEAR_VARS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+        volley \
+
+LOCAL_JAVA_LIBRARIES += org.apache.http.legacy
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := IntentFilterVerifier
+
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_PROGUARD_FLAGS := $(proguard.flags)
+
+include $(BUILD_PACKAGE)
+
+# Build the test package.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/IntentFilterVerifier/AndroidManifest.xml b/packages/IntentFilterVerifier/AndroidManifest.xml
new file mode 100644
index 0000000..3829cc5
--- /dev/null
+++ b/packages/IntentFilterVerifier/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.verifier.intentfilter"
+        coreApp="true">
+    <uses-permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+
+    <application
+            android:label="@string/service_name"
+            android:allowBackup="false">
+
+        <receiver
+                android:name="com.android.verifier.intentfilter.IntentVerificationReceiver"
+                android:permission="android.permission.BIND_INTENT_FILTER_VERIFIER" >
+            <intent-filter
+                    android:priority="-1" >
+                <action android:name="android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION" />
+                <data android:mimeType="application/vnd.android.package-archive" />
+            </intent-filter>
+        </receiver>
+
+        <service android:name=".IntentVerificationService"
+                 android:label="@string/service_name"
+                 android:exported="false"/>
+
+    </application>
+
+</manifest>
diff --git a/packages/IntentFilterVerifier/CleanSpec.mk b/packages/IntentFilterVerifier/CleanSpec.mk
new file mode 100644
index 0000000..e4575ae
--- /dev/null
+++ b/packages/IntentFilterVerifier/CleanSpec.mk
@@ -0,0 +1,49 @@
+# 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# *****************************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THE BANNER
+# *****************************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# ******************************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
+# ******************************************************************
diff --git a/packages/IntentFilterVerifier/proguard.flags b/packages/IntentFilterVerifier/proguard.flags
new file mode 100644
index 0000000..6e4bec3
--- /dev/null
+++ b/packages/IntentFilterVerifier/proguard.flags
@@ -0,0 +1 @@
+-verbose
\ No newline at end of file
diff --git a/packages/IntentFilterVerifier/res/values/strings.xml b/packages/IntentFilterVerifier/res/values/strings.xml
new file mode 100644
index 0000000..22f3cd5
--- /dev/null
+++ b/packages/IntentFilterVerifier/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Package name shown to users when they look at installed applications
+         and running processes. This service verifies packages that are
+         requested to be installed. [CHAR LIMIT=50] -->
+    <string name="service_name">Basic Intent Filter Verification Service</string>
+
+</resources>
diff --git a/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationReceiver.java b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationReceiver.java
new file mode 100644
index 0000000..de25f8c
--- /dev/null
+++ b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationReceiver.java
@@ -0,0 +1,90 @@
+/*
+ * 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.verifier.intentfilter;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class IntentVerificationReceiver extends BroadcastReceiver {
+    static final String TAG = IntentVerificationReceiver.class.getName();
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        if (Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION.equals(action)) {
+            Bundle extras = intent.getExtras();
+            if (extras != null) {
+                int verificationId = extras.getInt(
+                        PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID);
+                String hosts = extras.getString(
+                        PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS);
+
+                Log.d(TAG, "Received IntentFilter verification broadcast with verificationId: "
+                        + verificationId);
+
+                if (canDoVerification(context)) {
+                    Intent serviceIntent = new Intent(context, IntentVerificationService.class);
+                    serviceIntent.fillIn(intent, 0);
+                    serviceIntent.putExtras(intent.getExtras());
+
+                    Slog.d(TAG, "Starting Intent Verification Service.");
+
+                    context.startService(serviceIntent);
+                } else {
+                    sendVerificationFailure(context, verificationId, hosts);
+                }
+            }
+
+        } else {
+            Log.w(TAG, "Unexpected action: " + action);
+        }
+    }
+
+    private void sendVerificationFailure(Context context, int verificationId, String hosts) {
+        List<String> list = Arrays.asList(hosts.split(" "));
+        context.getPackageManager().verifyIntentFilter(
+                verificationId, PackageManager.INTENT_FILTER_VERIFICATION_FAILURE, list);
+
+        Log.d(TAG, "No network! Failing IntentFilter verification with verificationId: " +
+                verificationId + " and hosts: " + hosts);
+    }
+
+    private boolean canDoVerification(Context context) {
+        return hasNetwork(context);
+    }
+
+    public boolean hasNetwork(Context context) {
+        ConnectivityManager cm =
+                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        if (cm != null) {
+            NetworkInfo info = cm.getActiveNetworkInfo();
+            return (info != null) && info.isConnected();
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationRequest.java b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationRequest.java
new file mode 100644
index 0000000..8f9c86f
--- /dev/null
+++ b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationRequest.java
@@ -0,0 +1,29 @@
+/*
+ * 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.verifier.intentfilter;
+
+import com.android.volley.Response;
+import com.android.volley.toolbox.JsonArrayRequest;
+import org.json.JSONArray;
+
+public class IntentVerificationRequest extends JsonArrayRequest {
+
+    public IntentVerificationRequest(String url, Response.Listener<JSONArray> listener,
+            Response.ErrorListener errorListener) {
+        super(url, listener, errorListener);
+    }
+}
diff --git a/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationService.java b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationService.java
new file mode 100644
index 0000000..3e4db6c
--- /dev/null
+++ b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationService.java
@@ -0,0 +1,468 @@
+/*
+ * 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.verifier.intentfilter;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Slog;
+import com.android.volley.RequestQueue;
+import com.android.volley.Response;
+import com.android.volley.VolleyError;
+import com.android.volley.toolbox.Volley;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+public class IntentVerificationService extends Service {
+    private static final String TAG = "IntentVerificationService";
+
+    private static final String WELL_KNOWN_ASSOCIATIONS_JSON = "/.well-known/associations.json";
+    private static final String DEFAULT_SCHEME = "https";
+
+    private static final String JSON_KEY_TARGET = "target";
+    private static final String JSON_KEY_NAMESPACE = "namespace";
+    private static final String JSON_KEY_PACKAGE_NAME = "package_name";
+    private static final String JSON_KEY_CERT_FINGERPRINTS = "sha256_cert_fingerprints";
+
+    private static final String JSON_VAL_ANDROID_APP = "android_app";
+
+    private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            'A', 'B', 'C', 'D', 'E', 'F' };
+
+    private ConnectivityManager mConnectivityManager;
+    private Looper mHandlerLooper;
+    private VerificationHandler mHandler;
+    private RequestQueue mRequestQueue;
+
+    private static class VerificationState {
+        public final int verificationId;
+        public final String hosts;
+        public final String packageName;
+        public final Set<String> fingerprints;
+        public int responseCode = PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS;
+        public int counter;
+        public int numberOfHosts;
+        public ArrayList<String> failedHosts = new ArrayList<>();
+
+        private final Object lock = new Object();
+
+        public VerificationState(int id, String h, String p, Set<String> fps) {
+            verificationId = id;
+            hosts = h;
+            packageName = p;
+            fingerprints = fps;
+            numberOfHosts = hosts.split(" ").length;
+        }
+        public boolean setResponseCodeAndCheckMax(int code) {
+            synchronized (lock) {
+                if (code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) {
+                    responseCode = code;
+                    counter++;
+                } else if (code == PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS) {
+                    counter++;
+                }
+                return (counter == numberOfHosts);
+            }
+        }
+
+        public void addFailedHost(String host) {
+            synchronized (failedHosts) {
+                failedHosts.add(host);
+            }
+        }
+
+        public ArrayList<String> getFailedHosts() {
+            return failedHosts;
+        }
+    }
+
+    private HashMap<Integer, VerificationState> mVerificationMap =
+            new HashMap<Integer, VerificationState>();
+
+    private class VerificationHandler extends Handler {
+        private static final int MSG_STOP_SERVICE = 0;
+        private static final int MSG_VERIFY_INTENT_START = 1;
+        private static final int MSG_VERIFY_INTENT_DONE = 2;
+
+        private static final long SHUTDOWN_DELAY_MILLIS = 8 * 1000;
+
+        private final Context mContext;
+
+        public VerificationHandler(Context context, Looper looper) {
+            super(looper);
+
+            mContext = context;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_VERIFY_INTENT_START:
+                    final Intent intent = (Intent) msg.obj;
+                    Bundle extras = intent.getExtras();
+                    boolean immediate = false;
+
+                    if (extras != null) {
+                        immediate = doVerification(extras);
+                    }
+
+                    // There was no network, so we can stop soon
+                    if (immediate) {
+                        stopDelayed();
+                    }
+                    break;
+
+                case MSG_VERIFY_INTENT_DONE:
+                    VerificationState vs = (VerificationState) msg.obj;
+                    processVerificationDone(mContext, vs);
+                    clearVerificationState(vs);
+                    break;
+
+                case MSG_STOP_SERVICE:
+                    stopSelf();
+                    break;
+
+                default:
+                    Slog.i(TAG, "Unknown message posted " + msg.toString());
+                    break;
+
+            }
+        }
+
+        private void stopDelayed() {
+            removeMessages(MSG_STOP_SERVICE);
+            sendEmptyMessageDelayed(MSG_STOP_SERVICE, SHUTDOWN_DELAY_MILLIS);
+        }
+    }
+
+    private VerificationState getVerificationState(int id, String hosts, String packageName,
+            Set<String> fingerprints) {
+        synchronized (mVerificationMap) {
+            VerificationState vs = mVerificationMap.get(id);
+            if (vs == null) {
+                vs = new VerificationState(id, hosts, packageName, fingerprints);
+            }
+            return vs;
+        }
+    }
+
+    private void clearVerificationState(VerificationState vs) {
+        mVerificationMap.remove(vs);
+    }
+
+    private boolean doVerification(Bundle extras) {
+        String scheme = extras.getString(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME);
+        if (TextUtils.isEmpty(scheme)) {
+            scheme = DEFAULT_SCHEME;
+        }
+
+        int verificationId = extras.getInt(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID);
+        String hosts = extras.getString(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS);
+        String packageName = extras.getString(
+                PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME);
+
+        Set<String> fingerprints = getFingerprints(packageName);
+
+        Log.d(TAG, "Received IntentFilter verification broadcast with verificationId:" +
+                verificationId + " hosts:'" + hosts + "' scheme:" + scheme);
+
+        VerificationState vs = getVerificationState(verificationId, hosts, packageName,
+                fingerprints);
+
+        if (hasNetwork()) {
+            sendNetworkVerifications(scheme, vs);
+            return false;
+        }
+
+        // No network, so fail immediately
+        sendFailureResponseIfNeeded(vs);
+
+        return true;
+    }
+
+    private Set<String> getFingerprints(String packageName) {
+        Context context = getApplicationContext();
+        try {
+            Signature[] signatures = context.getPackageManager().getPackageInfo(packageName,
+                    PackageManager.GET_SIGNATURES).signatures;
+            if (signatures.length > 0) {
+                HashSet<String> result = new HashSet<String>();
+                for (Signature sig : signatures) {
+                    String fingerprint = computeNormalizedSha256Fingerprint(sig.toByteArray());
+                    result.add(fingerprint);
+                }
+                return result;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Cannot get signatures for package name: " + packageName);
+        }
+        return Collections.EMPTY_SET;
+    }
+
+    private static String computeNormalizedSha256Fingerprint(byte[] signature) {
+        MessageDigest digester;
+        try {
+            digester = MessageDigest.getInstance("SHA-256");
+        } catch (NoSuchAlgorithmException e) {
+            throw new AssertionError("No SHA-256 implementation found.");
+        }
+        digester.update(signature);
+        return byteArrayToHexString(digester.digest());
+    }
+
+    private static String byteArrayToHexString(byte[] array) {
+        if (array.length == 0) {
+            return "";
+        }
+        char[] buf = new char[array.length * 3 - 1];
+
+        int bufIndex = 0;
+        for (int i = 0; i < array.length; i++) {
+            byte b = array[i];
+            if (i > 0) {
+                buf[bufIndex++] = ':';
+            }
+            buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F];
+            buf[bufIndex++] = HEX_DIGITS[b & 0x0F];
+        }
+        return new String(buf);
+    }
+
+    private static String getAssociationPath() {
+        return WELL_KNOWN_ASSOCIATIONS_JSON;
+    }
+
+    private void sendNetworkVerifications(String scheme, final VerificationState vs) {
+        final int verificationId = vs.verificationId;
+        final String hosts = vs.hosts;
+
+        String[] array = hosts.split(" ");
+        for (final String host : array) {
+            try {
+                final URL url = new URL(scheme, host, getAssociationPath());
+                final String urlStr = url.toString();
+                Log.d(TAG, "Using verification URL: " + urlStr);
+                IntentVerificationRequest req = new IntentVerificationRequest(urlStr,
+                    new Response.Listener<JSONArray>() {
+                        @Override
+                        public void onResponse(JSONArray response) {
+                            Log.d(TAG, "From: " + urlStr + " received response: "
+                                    + response.toString());
+                            handleResponse(vs, host, response);
+                        }
+                    }, new Response.ErrorListener() {
+                        @Override
+                        public void onErrorResponse(VolleyError error) {
+                            Slog.d(TAG, "From: " + urlStr + " got error: " + error.getMessage()
+                                    + (error.networkResponse != null ? " with status code: "
+                                    + error.networkResponse.statusCode : ""));
+                            handleError(vs, host);
+                        }
+                    }
+                );
+                mRequestQueue.add(req);
+            } catch (MalformedURLException e) {
+                Log.w(TAG, "Cannot send verificationId: " + verificationId + " to host: " + host);
+            }
+        }
+    }
+
+    private void handleError(VerificationState vs, String host) {
+        vs.addFailedHost(host);
+        sendFailureResponseIfNeeded(vs);
+    }
+
+    private void handleResponse(VerificationState vs, String host, JSONArray response) {
+        try {
+            if (response.length() == 0) {
+                Log.d(TAG, "Domain response is empty!");
+                handleError(vs, host);
+                return;
+            }
+
+            JSONObject firstRelation = (JSONObject) response.get(0);
+            if (firstRelation == null) {
+                Log.d(TAG, "Domain response is should have a relation!");
+                handleError(vs, host);
+                return;
+            }
+
+            JSONObject target = (JSONObject) firstRelation.get(JSON_KEY_TARGET);
+            if (target == null) {
+                Log.d(TAG, "Domain response target is empty!");
+                handleError(vs, host);
+                return;
+            }
+
+            String nameSpace = target.getString(JSON_KEY_NAMESPACE);
+            if (TextUtils.isEmpty(nameSpace) || !nameSpace.equals(JSON_VAL_ANDROID_APP)) {
+                Log.d(TAG, "Domain response target name space is not valid: " + nameSpace);
+                handleError(vs, host);
+                return;
+            }
+
+            String packageName = target.getString(JSON_KEY_PACKAGE_NAME);
+            JSONArray certFingerprints = target.getJSONArray(JSON_KEY_CERT_FINGERPRINTS);
+
+            // Early exits is the JSON response is not correct for the package name or signature
+            if (TextUtils.isEmpty(packageName)) {
+                Log.d(TAG, "Domain response has empty package name!");
+                handleError(vs, host);
+                return;
+            }
+            if (certFingerprints.length() == 0) {
+                Log.d(TAG, "Domain response has empty cert signature!");
+                handleError(vs, host);
+                return;
+            }
+            // Now do the real test on package name and signature
+            if (!packageName.equalsIgnoreCase(vs.packageName)) {
+                Log.d(TAG, "Domain response has package name mismatch!" + packageName +
+                        " vs " + vs.packageName);
+                handleError(vs, host);
+                return;
+            }
+            final int count = certFingerprints.length();
+            for (int i = 0; i < count; i++) {
+                String fingerprint = certFingerprints.getString(i);
+                if (!vs.fingerprints.contains(fingerprint)) {
+                    Log.d(TAG, "Domain response has cert fingerprint mismatch! " +
+                            "The domain fingerprint '" + fingerprint + "' is not from the App");
+                    handleError(vs, host);
+                    return;
+                }
+            }
+            sendSuccessResponseIfNeeded(vs);
+        } catch (JSONException e) {
+            Log.d(TAG, "Domain response is not well formed", e);
+            handleError(vs, host);
+        }
+    }
+
+    private void sendSuccessResponseIfNeeded(VerificationState vs) {
+        if (vs.setResponseCodeAndCheckMax(PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS)) {
+            sendMessage(vs);
+        }
+    }
+
+    private void sendFailureResponseIfNeeded(VerificationState vs) {
+        if (vs.setResponseCodeAndCheckMax(PackageManager.INTENT_FILTER_VERIFICATION_FAILURE)) {
+            sendMessage(vs);
+        }
+    }
+
+    private void sendMessage(VerificationState vs) {
+        Message msg = mHandler.obtainMessage(VerificationHandler.MSG_VERIFY_INTENT_DONE);
+        msg.obj = vs;
+        mHandler.sendMessage(msg);
+    }
+
+    private void processVerificationDone(Context context, VerificationState state) {
+        int verificationId = state.verificationId;
+        String hosts = state.hosts;
+        int responseCode = state.responseCode;
+
+        final PackageManager pm = context.getPackageManager();
+
+        // Callback the PackageManager
+        pm.verifyIntentFilter(verificationId, responseCode, state.getFailedHosts());
+        Log.d(TAG, "IntentFilter with verificationId: " + verificationId + " and hosts: " +
+                hosts + " got verification code: " + responseCode);
+    }
+
+    /**
+     * We only connect to this service from the same process.
+     */
+    public class LocalBinder extends Binder {
+        IntentVerificationService getService() { return IntentVerificationService.this; }
+    }
+
+    private final IBinder mBinder = new LocalBinder();
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Slog.i(TAG, "Received start id " + startId + ": " + intent);
+
+        final Message msg = mHandler.obtainMessage(VerificationHandler.MSG_VERIFY_INTENT_START);
+        msg.obj = intent;
+        mHandler.sendMessage(msg);
+
+        return START_STICKY;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        Slog.d(TAG, "Starting up...");
+
+        final HandlerThread handlerThread = new HandlerThread("IntentVerificationService");
+        handlerThread.start();
+        mHandlerLooper = handlerThread.getLooper();
+
+        mHandler = new VerificationHandler(getApplicationContext(), mHandlerLooper);
+
+        mRequestQueue = Volley.newRequestQueue(this);
+        mRequestQueue.start();
+
+        mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+
+        Slog.d(TAG, "Shutting down...");
+
+        mHandlerLooper.quit();
+        mRequestQueue.stop();
+    }
+
+    private boolean hasNetwork() {
+        NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
+        return (info != null) && info.isConnected();
+    }
+}
diff --git a/packages/Keyguard/Android.mk b/packages/Keyguard/Android.mk
index 96ed2e7..9083212 100644
--- a/packages/Keyguard/Android.mk
+++ b/packages/Keyguard/Android.mk
@@ -22,6 +22,8 @@
 
 LOCAL_CERTIFICATE := platform
 
+LOCAL_JAVA_LIBRARIES := SettingsLib
+
 LOCAL_PRIVILEGED_MODULE := true
 
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
@@ -30,4 +32,4 @@
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
-#include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
+#include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index 5047330..9469ee7 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -300,4 +300,7 @@
     <!-- Content description of the switch input method button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">Switch input method button.</string>
 
+    <!-- Description of airplane mode -->
+    <string name="airplane_mode">Airplane mode</string>
+
 </resources>
diff --git a/packages/Keyguard/src/com/android/keyguard/CarrierText.java b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
index 7d0b81d..4fbcc1e 100644
--- a/packages/Keyguard/src/com/android/keyguard/CarrierText.java
+++ b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
@@ -35,6 +35,7 @@
 import com.android.internal.telephony.IccCardConstants;
 import com.android.internal.telephony.IccCardConstants.State;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.settingslib.WirelessUtils;
 
 public class CarrierText extends TextView {
     private static final boolean DEBUG = KeyguardConstants.DEBUG;
@@ -146,6 +147,9 @@
                         getContext().getText(R.string.keyguard_missing_sim_message_short), text);
             }
         }
+        if (WirelessUtils.isAirplaneModeOn(mContext)) {
+            displayText = getContext().getString(R.string.airplane_mode);
+        }
         setText(displayText);
     }
 
diff --git a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
index 3627e3e..7d5bf6b 100644
--- a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
@@ -36,7 +36,10 @@
  * allows the user to return to the call.
  */
 public class EmergencyButton extends Button {
-    private static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL";
+    private static final Intent INTENT_EMERGENCY_DIAL = new Intent()
+            .setAction("com.android.phone.EmergencyDialer.DIAL")
+            .setPackage("com.android.phone")
+            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
 
     KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
 
@@ -112,12 +115,9 @@
                 mEmergencyButtonCallback.onEmergencyButtonClickedWhenInCall();
             }
         } else {
-            final boolean bypassHandler = true;
-            KeyguardUpdateMonitor.getInstance(mContext).reportEmergencyCallAction(bypassHandler);
-            Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
-            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-            getContext().startActivityAsUser(intent,
+            KeyguardUpdateMonitor.getInstance(mContext).reportEmergencyCallAction(
+                    true /* bypassHandler */);
+            getContext().startActivityAsUser(INTENT_EMERGENCY_DIAL,
                     new UserHandle(mLockPatternUtils.getCurrentUser()));
         }
     }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 396fe4f..82dec30 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -40,6 +40,7 @@
 
 import android.media.AudioManager;
 import android.os.BatteryManager;
+import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.IRemoteCallback;
 import android.os.Message;
@@ -51,9 +52,11 @@
 import com.android.internal.telephony.IccCardConstants.State;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyIntents;
-import android.service.fingerprint.FingerprintManager;
-import android.service.fingerprint.FingerprintManagerReceiver;
-import android.service.fingerprint.FingerprintUtils;
+
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
+import android.hardware.fingerprint.FingerprintUtils;
+import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
@@ -109,10 +112,11 @@
     private static final int MSG_SCREEN_TURNED_ON = 319;
     private static final int MSG_SCREEN_TURNED_OFF = 320;
     private static final int MSG_KEYGUARD_BOUNCER_CHANGED = 322;
-    private static final int MSG_FINGERPRINT_PROCESSED = 323;
-    private static final int MSG_FINGERPRINT_ACQUIRED = 324;
-    private static final int MSG_FACE_UNLOCK_STATE_CHANGED = 325;
-    private static final int MSG_SIM_SUBSCRIPTION_INFO_CHANGED = 326;
+    private static final int MSG_FINGERPRINT_AUTHENTICATED = 323;
+    private static final int MSG_FINGERPRINT_ERROR = 324;
+    private static final int MSG_FINGERPRINT_HELP = 325;
+    private static final int MSG_FACE_UNLOCK_STATE_CHANGED = 326;
+    private static final int MSG_SIM_SUBSCRIPTION_INFO_CHANGED = 327;
 
     private static KeyguardUpdateMonitor sInstance;
 
@@ -201,11 +205,14 @@
                 case MSG_SCREEN_TURNED_ON:
                     handleScreenTurnedOn();
                     break;
-                case MSG_FINGERPRINT_ACQUIRED:
-                    handleFingerprintAcquired(msg.arg1);
+                case MSG_FINGERPRINT_AUTHENTICATED:
+                    handleFingerprintAuthenticated(msg.arg1, msg.arg2);
                     break;
-                case MSG_FINGERPRINT_PROCESSED:
-                    handleFingerprintProcessed(msg.arg1);
+                case MSG_FINGERPRINT_HELP:
+                    handleFingerprintHelp(msg.arg1 /* msgId */, (String) msg.obj /* errString */);
+                    break;
+                case MSG_FINGERPRINT_ERROR:
+                    handleFingerprintError(msg.arg1 /* msgId */, (String) msg.obj /* errString */);
                     break;
                 case MSG_FACE_UNLOCK_STATE_CHANGED:
                     handleFaceUnlockStateChanged(msg.arg1 != 0, msg.arg2);
@@ -227,7 +234,7 @@
 
     private SparseBooleanArray mUserHasTrust = new SparseBooleanArray();
     private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray();
-    private SparseBooleanArray mUserFingerprintRecognized = new SparseBooleanArray();
+    private SparseBooleanArray mUserFingerprintAuthenticated = new SparseBooleanArray();
     private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray();
 
     @Override
@@ -314,18 +321,18 @@
         }
     }
 
-    private void onFingerprintRecognized(int userId) {
-        mUserFingerprintRecognized.put(userId, true);
+    private void onFingerprintAuthenticated(int userId) {
+        mUserFingerprintAuthenticated.put(userId, true);
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onFingerprintRecognized(userId);
+                cb.onFingerprintAuthenticated(userId);
             }
         }
     }
 
-    private void handleFingerprintProcessed(int fingerprintId) {
-        if (fingerprintId == 0) return; // not a valid fingerprint
+    private void handleFingerprintAuthenticated(int fingerId, int groupId) {
+        if (fingerId == 0) return; // not a valid fingerprint
 
         final int userId;
         try {
@@ -341,17 +348,28 @@
         final ContentResolver res = mContext.getContentResolver();
         final int ids[] = FingerprintUtils.getFingerprintIdsForUser(res, userId);
         for (int i = 0; i < ids.length; i++) {
-            if (ids[i] == fingerprintId) {
-                onFingerprintRecognized(userId);
+            // TODO: fix once HAL supports storing group id
+            final boolean isCorrectUser = true || (groupId == userId);
+            if (ids[i] == fingerId && isCorrectUser) {
+                onFingerprintAuthenticated(userId);
             }
         }
     }
 
-    private void handleFingerprintAcquired(int info) {
+    private void handleFingerprintHelp(int msgId, String helpString) {
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onFingerprintAcquired(info);
+                cb.onFingerprintHelp(msgId, helpString);
+            }
+        }
+    }
+
+    private void handleFingerprintError(int msgId, String errString) {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onFingerprintError(msgId, errString);
             }
         }
     }
@@ -387,7 +405,7 @@
 
     public boolean getUserHasTrust(int userId) {
         return !isTrustDisabled(userId) && mUserHasTrust.get(userId)
-                || mUserFingerprintRecognized.get(userId);
+                || mUserFingerprintAuthenticated.get(userId);
     }
 
     public boolean getUserTrustIsManaged(int userId) {
@@ -464,23 +482,29 @@
             }
         }
     };
-    private FingerprintManagerReceiver mFingerprintManagerReceiver =
-            new FingerprintManagerReceiver() {
-        @Override
-        public void onProcessed(int fingerprintId) {
-            mHandler.obtainMessage(MSG_FINGERPRINT_PROCESSED, fingerprintId, 0).sendToTarget();
-        };
+
+    private FingerprintManager.AuthenticationCallback mAuthenticationCallback
+            = new AuthenticationCallback() {
 
         @Override
-        public void onAcquired(int info) {
-            mHandler.obtainMessage(MSG_FINGERPRINT_ACQUIRED, info, 0).sendToTarget();
+        public void onAuthenticationSucceeded(AuthenticationResult result) {
+            mHandler.obtainMessage(MSG_FINGERPRINT_AUTHENTICATED,
+                    result.getFingerprint().getFingerId(),
+                    result.getFingerprint().getGroupId()).sendToTarget();
         }
 
         @Override
-        public void onError(int error) {
-            if (DEBUG) Log.w(TAG, "FingerprintManager reported error: " + error);
+        public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
+            mHandler.obtainMessage(MSG_FINGERPRINT_HELP, helpMsgId, 0, helpString).sendToTarget();
+        }
+
+        @Override
+        public void onAuthenticationError(int errMsgId, CharSequence errString) {
+            mHandler.obtainMessage(MSG_FINGERPRINT_ERROR, errMsgId, 0, errString);
         }
     };
+    private CancellationSignal mFingerprintCancelSignal;
+    private FingerprintManager mFpm;
 
     /**
      * When we receive a
@@ -606,6 +630,7 @@
                 cb.onScreenTurnedOn();
             }
         }
+        startListeningForFingerprint(mContext);
     }
 
     protected void handleScreenTurnedOff(int arg1) {
@@ -617,6 +642,7 @@
                 cb.onScreenTurnedOff(arg1);
             }
         }
+        stopListeningForFingerprint();
     }
 
     /**
@@ -705,9 +731,25 @@
         TrustManager trustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE);
         trustManager.registerTrustListener(this);
 
-        FingerprintManager fpm;
-        fpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
-        fpm.startListening(mFingerprintManagerReceiver);
+        mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
+        startListeningForFingerprint(context);
+    }
+
+    private void startListeningForFingerprint(Context context) {
+        if (mFpm != null && mFpm.isHardwareDetected()) {
+            if (mFingerprintCancelSignal == null) {
+                mFingerprintCancelSignal = new CancellationSignal();
+            } else {
+                mFingerprintCancelSignal.cancel();
+            }
+            mFpm.authenticate(null, mAuthenticationCallback, mFingerprintCancelSignal, 0);
+        }
+    }
+
+    private void stopListeningForFingerprint() {
+        if (mFingerprintCancelSignal != null) {
+            mFingerprintCancelSignal.cancel();
+        }
     }
 
     private boolean isDeviceProvisionedInSettingsDb() {
@@ -1152,7 +1194,7 @@
     }
 
     public void clearFingerprintRecognized() {
-        mUserFingerprintRecognized.clear();
+        mUserFingerprintAuthenticated.clear();
     }
 
     public void reportFailedUnlockAttempt() {
@@ -1239,7 +1281,7 @@
         mHandler.sendEmptyMessage(MSG_SCREEN_TURNED_ON);
     }
 
-    public void dispatchScreenTurndOff(int why) {
+    public void dispatchScreenTurnedOff(int why) {
         synchronized(this) {
             mScreenOn = false;
         }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index f0e2389..8d2562d 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -19,6 +19,7 @@
 import android.graphics.Bitmap;
 import android.media.AudioManager;
 import android.os.SystemClock;
+import android.hardware.fingerprint.FingerprintManager;
 import android.telephony.TelephonyManager;
 import android.view.WindowManagerPolicy;
 
@@ -176,14 +177,24 @@
 
     /**
      * Called when a fingerprint is recognized.
-     * @param userId
+     * @param userId the user id for which the fingerprint was authenticated
      */
-    public void onFingerprintRecognized(int userId) { }
+    public void onFingerprintAuthenticated(int userId) { }
 
     /**
-     * Called when fingerprint is acquired but not yet recognized
+     * Called when fingerprint provides help string (e.g. "Try again")
+     * @param msgId
+     * @param helpString
      */
-    public void onFingerprintAcquired(int info) { }
+    public void onFingerprintHelp(int msgId, String helpString) { }
+
+    /**
+     * Called when fingerprint provides an semi-permanent error message
+     * (e.g. "Hardware not available").
+     * @param msgId one of the error messages listed in {@link FingerprintManager}
+     * @param errString
+     */
+    public void onFingerprintError(int msgId, String errString) { }
 
     /**
      * Called when the state of face unlock changed.
diff --git a/packages/PrintSpooler/res/values-hi/strings.xml b/packages/PrintSpooler/res/values-hi/strings.xml
index 5d8c3c6..13d5543 100644
--- a/packages/PrintSpooler/res/values-hi/strings.xml
+++ b/packages/PrintSpooler/res/values-hi/strings.xml
@@ -69,7 +69,7 @@
       <item quantity="one"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> प्रिंट कार्य</item>
       <item quantity="other"><xliff:g id="PRINT_JOB_NAME_1">%1$d</xliff:g> प्रिंट कार्य</item>
     </plurals>
-    <string name="cancel" msgid="4373674107267141885">"रहने दें"</string>
+    <string name="cancel" msgid="4373674107267141885">"अभी नहीं"</string>
     <string name="restart" msgid="2472034227037808749">"पुन: आरंभ करें"</string>
     <string name="no_connection_to_printer" msgid="2159246915977282728">"प्रिंटर के लिए कोई कनेक्शन नहीं"</string>
     <string name="reason_unknown" msgid="5507940196503246139">"अज्ञात"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index e1cb878..64b4452 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -104,6 +104,7 @@
 
     // See mConnectAttempted
     private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
+    private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000;
 
     /** Auto-connect after pairing only if locally initiated. */
     private boolean mConnectAfterPairing;
@@ -525,9 +526,11 @@
      */
     void onUuidChanged() {
         updateProfiles();
+        ParcelUuid[] uuids = mDevice.getUuids();
+        long timeout = MAX_UUID_DELAY_FOR_AUTO_CONNECT;
 
         if (DEBUG) {
-            Log.e(TAG, "onUuidChanged: Time since last connect"
+            Log.d(TAG, "onUuidChanged: Time since last connect"
                     + (SystemClock.elapsedRealtime() - mConnectAttempted));
         }
 
@@ -535,9 +538,11 @@
          * If a connect was attempted earlier without any UUID, we will do the
          * connect now.
          */
+        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) {
+            timeout = MAX_HOGP_DELAY_FOR_AUTO_CONNECT;
+        }
         if (!mProfiles.isEmpty()
-                && (mConnectAttempted + MAX_UUID_DELAY_FOR_AUTO_CONNECT) > SystemClock
-                        .elapsedRealtime()) {
+                && (mConnectAttempted + timeout) > SystemClock.elapsedRealtime()) {
             connectWithoutResettingTimer(false);
         }
         dispatchAttributesChanged();
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 729efcb..0b6ab99 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -61,10 +61,18 @@
 import java.util.Set;
 
 /**
- * Database helper class for {@link SettingsProvider}.
- * Mostly just has a bit {@link #onCreate} to initialize the database.
+ * Legacy settings database helper class for {@link SettingsProvider}.
+ *
+ * IMPORTANT: Do not add any more upgrade steps here as the global,
+ * secure, and system settings are no longer stored in a database
+ * but are kept in memory and persisted to XML.
+ *
+ * See: SettingsProvider.UpgradeController#onUpgradeLocked
+ *
+ * @deprecated The implementation is frozen.  Do not add any new code to this class!
  */
-public class DatabaseHelper extends SQLiteOpenHelper {
+@Deprecated
+class DatabaseHelper extends SQLiteOpenHelper {
     private static final String TAG = "SettingsProvider";
     private static final String DATABASE_NAME = "settings.db";
 
@@ -1932,19 +1940,14 @@
             upgradeVersion = 118;
         }
 
-        /**
+        /*
          * IMPORTANT: Do not add any more upgrade steps here as the global,
          * secure, and system settings are no longer stored in a database
-         * but are kept in memory and persisted to XML. The correct places
-         * for adding upgrade steps are:
+         * but are kept in memory and persisted to XML.
          *
-         * Global: SettingsProvider.UpgradeController#onUpgradeGlobalSettings
-         * Secure: SettingsProvider.UpgradeController#onUpgradeSecureSettings
-         * System: SettingsProvider.UpgradeController#onUpgradeSystemSettings
+         * See: SettingsProvider.UpgradeController#onUpgradeLocked
          */
 
-        // *** Remember to update DATABASE_VERSION above!
-
         if (upgradeVersion != currentVersion) {
             recreateDatabase(db, oldVersion, upgradeVersion, currentVersion);
         }
@@ -2386,6 +2389,14 @@
 
             loadIntegerSetting(stmt, Settings.System.POINTER_SPEED,
                     R.integer.def_pointer_speed);
+
+            /*
+             * IMPORTANT: Do not add any more upgrade steps here as the global,
+             * secure, and system settings are no longer stored in a database
+             * but are kept in memory and persisted to XML.
+             *
+             * See: SettingsProvider.UpgradeController#onUpgradeLocked
+             */
         } finally {
             if (stmt != null) stmt.close();
         }
@@ -2517,6 +2528,14 @@
 
             loadIntegerSetting(stmt, Settings.Secure.SLEEP_TIMEOUT,
                     R.integer.def_sleep_timeout);
+
+            /*
+             * IMPORTANT: Do not add any more upgrade steps here as the global,
+             * secure, and system settings are no longer stored in a database
+             * but are kept in memory and persisted to XML.
+             *
+             * See: SettingsProvider.UpgradeController#onUpgradeLocked
+             */
         } finally {
             if (stmt != null) stmt.close();
         }
@@ -2693,7 +2712,14 @@
                     R.bool.def_guest_user_enabled);
             loadSetting(stmt, Settings.Global.ENHANCED_4G_MODE_ENABLED,
                     ImsConfig.FeatureValueConstants.ON);
-            // --- New global settings start here
+
+            /*
+             * IMPORTANT: Do not add any more upgrade steps here as the global,
+             * secure, and system settings are no longer stored in a database
+             * but are kept in memory and persisted to XML.
+             *
+             * See: SettingsProvider.UpgradeController#onUpgradeLocked
+             */
         } finally {
             if (stmt != null) stmt.close();
         }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 30786f0..952b220 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -62,9 +62,10 @@
      */
     private static final ArraySet<String> sBroadcastOnRestore;
     static {
-        sBroadcastOnRestore = new ArraySet<String>(2);
+        sBroadcastOnRestore = new ArraySet<String>(3);
         sBroadcastOnRestore.add(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
         sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+        sBroadcastOnRestore.add(Settings.Secure.ENABLED_INPUT_METHODS);
     }
 
     private interface SettingsLookup {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 2c63647..126b4aa 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1806,7 +1806,7 @@
                 final int oldVersion = secureSettings.getVersionLocked();
                 final int newVersion = SETTINGS_VERSION;
 
-                // If up do data - done.
+                // If up do date - done.
                 if (oldVersion == newVersion) {
                     return;
                 }
@@ -1859,40 +1859,43 @@
                 return getSettingsLocked(SETTINGS_TYPE_SYSTEM, userId);
             }
 
+            /**
+             * You must perform all necessary mutations to bring the settings
+             * for this user from the old to the new version. When you add a new
+             * upgrade step you *must* update SETTINGS_VERSION.
+             *
+             * This is an example of moving a setting from secure to global.
+             *
+             * // v119: Example settings changes.
+             * if (currentVersion == 118) {
+             *     if (userId == UserHandle.USER_OWNER) {
+             *         // Remove from the secure settings.
+             *         SettingsState secureSettings = getSecureSettingsLocked(userId);
+             *         String name = "example_setting_to_move";
+             *         String value = secureSettings.getSetting(name);
+             *         secureSettings.deleteSetting(name);
+             *
+             *         // Add to the global settings.
+             *         SettingsState globalSettings = getGlobalSettingsLocked();
+             *         globalSettings.insertSetting(name, value, SettingsState.SYSTEM_PACKAGE_NAME);
+             *     }
+             *
+             *     // Update the current version.
+             *     currentVersion = 119;
+             * }
+             */
             private int onUpgradeLocked(int userId, int oldVersion, int newVersion) {
                 if (DEBUG) {
                     Slog.w(LOG_TAG, "Upgrading settings for user: " + userId + " from version: "
                             + oldVersion + " to version: " + newVersion);
                 }
 
-                // You must perform all necessary mutations to bring the settings
-                // for this user from the old to the new version. When you add a new
-                // upgrade step you *must* update SETTINGS_VERSION.
+                int currentVersion = oldVersion;
 
-                /**
-                 * This is an example of moving a setting from secure to global.
-                 *
-                 * int currentVersion = oldVersion;
-                 * if (currentVersion == 118) {
-                 *     // Remove from the secure settings.
-                 *     SettingsState secureSettings = getSecureSettingsLocked(userId);
-                 *     String name = "example_setting_to_move";
-                 *     String value = secureSettings.getSetting(name);
-                 *     secureSettings.deleteSetting(name);
-                 *
-                 *     // Add to the global settings.
-                 *     SettingsState globalSettings = getGlobalSettingsLocked();
-                 *     globalSettings.insertSetting(name, value, SettingsState.SYSTEM_PACKAGE_NAME);
-                 *
-                 *     // Update the current version.
-                 *     currentVersion = 119;
-                 * }
-                 *
-                 * // Return the current version.
-                 * return currentVersion;
-                 */
+                // vXXX: Add new settings above this point.
 
-                return SettingsState.VERSION_UNDEFINED;
+                // Return the current version.
+                return currentVersion;
             }
         }
     }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 3bf6828..c7092b3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -416,78 +416,48 @@
 
     private void parseStateLocked(XmlPullParser parser)
             throws IOException, XmlPullParserException {
-        parser.next();
-        skipEmptyTextTags(parser);
-        expect(parser, XmlPullParser.START_TAG, TAG_SETTINGS);
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals(TAG_SETTINGS)) {
+                parseSettingsLocked(parser);
+            }
+        }
+    }
+
+    private void parseSettingsLocked(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
 
         mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERSION));
 
-        parser.next();
-
-        while (parseSettingLocked(parser)) {
-            parser.next();
-        }
-
-        skipEmptyTextTags(parser);
-        expect(parser, XmlPullParser.END_TAG, TAG_SETTINGS);
-    }
-
-    private boolean parseSettingLocked(XmlPullParser parser)
-            throws IOException, XmlPullParserException {
-        skipEmptyTextTags(parser);
-        if (!accept(parser, XmlPullParser.START_TAG, TAG_SETTING)) {
-            return false;
-        }
-
-        String id = parser.getAttributeValue(null, ATTR_ID);
-        String name = parser.getAttributeValue(null, ATTR_NAME);
-        String value = parser.getAttributeValue(null, ATTR_VALUE);
-        String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
-        mSettings.put(name, new Setting(name, unpackValue(value),
-                unpackValue(packageName), id));
-
-        if (DEBUG_PERSISTENCE) {
-            Slog.i(LOG_TAG, "[RESTORED] " + name + "=" + value);
-        }
-
-        parser.next();
-
-        skipEmptyTextTags(parser);
-        expect(parser, XmlPullParser.END_TAG, TAG_SETTING);
-
-        return true;
-    }
-
-    private void expect(XmlPullParser parser, int type, String tag)
-            throws IOException, XmlPullParserException {
-        if (!accept(parser, type, tag)) {
-            throw new XmlPullParserException("Expected event: " + type
-                    + " and tag: " + tag + " but got event: " + parser.getEventType()
-                    + " and tag:" + parser.getName());
-        }
-    }
-
-    private void skipEmptyTextTags(XmlPullParser parser)
-            throws IOException, XmlPullParserException {
-        while (accept(parser, XmlPullParser.TEXT, null)
-                && parser.isWhitespace()) {
-            parser.next();
-        }
-    }
-
-    private boolean accept(XmlPullParser parser, int type, String tag)
-            throws IOException, XmlPullParserException {
-        if (parser.getEventType() != type) {
-            return false;
-        }
-        if (tag != null) {
-            if (!tag.equals(parser.getName())) {
-                return false;
+        final int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
             }
-        } else if (parser.getName() != null) {
-            return false;
+
+            String tagName = parser.getName();
+            if (tagName.equals(TAG_SETTING)) {
+                String id = parser.getAttributeValue(null, ATTR_ID);
+                String name = parser.getAttributeValue(null, ATTR_NAME);
+                String value = parser.getAttributeValue(null, ATTR_VALUE);
+                String packageName = parser.getAttributeValue(null, ATTR_PACKAGE);
+                mSettings.put(name, new Setting(name, unpackValue(value),
+                        unpackValue(packageName), id));
+
+                if (DEBUG_PERSISTENCE) {
+                    Slog.i(LOG_TAG, "[RESTORED] " + name + "=" + value);
+                }
+            }
         }
-        return true;
     }
 
     private final class MyHandler extends Handler {
diff --git a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java
index 0485334..0f8ccd7 100644
--- a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java
+++ b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/ObbBackupService.java
@@ -17,8 +17,8 @@
 package com.android.sharedstoragebackup;
 
 import android.app.Service;
-import android.app.backup.BackupDataOutput;
 import android.app.backup.FullBackup;
+import android.app.backup.FullBackupDataOutput;
 import android.app.backup.IBackupManager;
 import android.content.Intent;
 import android.os.Environment;
@@ -67,7 +67,7 @@
                                 Log.i(TAG, obbList.size() + " files to back up");
                             }
                             final String rootPath = obbDir.getCanonicalPath();
-                            final BackupDataOutput out = new BackupDataOutput(outFd);
+                            final FullBackupDataOutput out = new FullBackupDataOutput(data);
                             for (File f : obbList) {
                                 final String filePath = f.getCanonicalPath();
                                 if (DEBUG) {
@@ -92,7 +92,7 @@
                 }
 
                 try {
-                    callbackBinder.opComplete(token);
+                    callbackBinder.opComplete(token, 0);
                 } catch (RemoteException e) {
                 }
             }
@@ -119,7 +119,7 @@
                 Log.i(TAG, "Exception restoring OBB " + path, e);
             } finally {
                 try {
-                    callbackBinder.opComplete(token);
+                    callbackBinder.opComplete(token, 0);
                 } catch (RemoteException e) {
                 }
             }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 3c44245..9d16501 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -105,6 +105,18 @@
                 android:resource="@xml/file_provider_paths" />
         </provider>
 
+        <provider
+            android:name=".BugreportStorageProvider"
+            android:authorities="com.android.shell.documents"
+            android:grantUriPermissions="true"
+            android:exported="true"
+            android:permission="android.permission.MANAGE_DOCUMENTS"
+            android:enabled="false">
+            <intent-filter>
+                <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
+            </intent-filter>
+        </provider>
+
         <activity
             android:name=".BugreportWarningActivity"
             android:theme="@*android:style/Theme.DeviceDefault.Light.Dialog.Alert"
diff --git a/packages/Shell/res/values-af/strings.xml b/packages/Shell/res/values-af/strings.xml
index 8c4f75f..43beb576d 100644
--- a/packages/Shell/res/values-af/strings.xml
+++ b/packages/Shell/res/values-af/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Raak om jou foutverslag te deel"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Foutverslae bevat data van die stelsel se verskillende loglêers af, insluitend persoonlike en private inligting. Deel foutverslae net met programme en mense wat jy vertrou."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Wys hierdie boodskap volgende keer"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Foutverslae"</string>
 </resources>
diff --git a/packages/Shell/res/values-am/strings.xml b/packages/Shell/res/values-am/strings.xml
index af84a09..e86ecf8 100644
--- a/packages/Shell/res/values-am/strings.xml
+++ b/packages/Shell/res/values-am/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"የሳንካ ሪፖርትዎን ለማጋራት ይንክኩ"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"የሳንካ ሪፖርቶች የግል መረጃን ጨምሮ ከበርካታ የስርዓቱ ምዝግብ ማስታወሻዎች የመጣ ውሂብን ይዟል። የሳንካ ሪፖርቶች ለሚያምኗቸው መተግበሪያዎችን እና ሰዎችን ብቻ ያጋሩ።"</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ይህን መልዕክት በሚቀጥለው ጊዜ አሳይ"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"የሳንካ ሪፖርቶች"</string>
 </resources>
diff --git a/packages/Shell/res/values-ar/strings.xml b/packages/Shell/res/values-ar/strings.xml
index 69d7d3f..0fcf019 100644
--- a/packages/Shell/res/values-ar/strings.xml
+++ b/packages/Shell/res/values-ar/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"المس لمشاركة تقرير الأخطاء"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"تحتوي تقارير الأخطاء على بيانات من ملفات سجلات النظام المتنوعة، بما في ذلك معلومات شخصية وخاصة. لا تشارك تقارير الأخطاء إلا مع التطبيقات والأشخاص الموثوق بهم."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"إظهار هذه الرسالة في المرة القادمة"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"تقارير الأخطاء"</string>
 </resources>
diff --git a/packages/Shell/res/values-bg/strings.xml b/packages/Shell/res/values-bg/strings.xml
index f7ae49c..2d779b6 100644
--- a/packages/Shell/res/values-bg/strings.xml
+++ b/packages/Shell/res/values-bg/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Докоснете, за да споделите отчета си за програмни грешки"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Отчетите за програмни грешки съдържат данни от различни регистрационни файлове на системата, включително лична и поверителна информация. Споделяйте ги само с приложения и хора, на които имате доверие."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Това съобщение да се показва следващия път"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Отчети за програмни грешки"</string>
 </resources>
diff --git a/packages/Shell/res/values-bn-rBD/strings.xml b/packages/Shell/res/values-bn-rBD/strings.xml
index 5e8cec4..76da84b 100644
--- a/packages/Shell/res/values-bn-rBD/strings.xml
+++ b/packages/Shell/res/values-bn-rBD/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"আপনার ত্রুটির প্রতিবেদন ভাগ করতে স্পর্শ করুন"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"ত্রুটির প্রতিবেদনগুলিতে থাকা ডেটা, সিস্টেমের বিভিন্ন লগ ফাইলগুলি থেকে আসে, যাতে ব্যক্তিগত এবং গোপনীয় তথ্য অন্তর্ভুক্ত থাকে৷ আপনি বিশ্বাস করেন শুধুমাত্র এমন অ্যাপ্লিকেশান এবং ব্যক্তিদের সাথে ত্রুটির প্রতিবেদনগুলি ভাগ করুন৷"</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"এই বার্তাটি পরের বার দেখান"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"ত্রুটির প্রতিবেদনগুলি"</string>
 </resources>
diff --git a/packages/Shell/res/values-ca/strings.xml b/packages/Shell/res/values-ca/strings.xml
index fa4d1f3..327fdc2 100644
--- a/packages/Shell/res/values-ca/strings.xml
+++ b/packages/Shell/res/values-ca/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toca aquí per compartir el teu informe d\'error."</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Els informes d\'error contenen dades dels diferents fitxers de registre del sistema, inclosa informació privada i personal. Comparteix els informes d\'error només amb les aplicacions i amb les persones en qui confies."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostra aquest missatge la propera vegada"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Informes d\'error"</string>
 </resources>
diff --git a/packages/Shell/res/values-cs/strings.xml b/packages/Shell/res/values-cs/strings.xml
index d321159..3e36c6f 100644
--- a/packages/Shell/res/values-cs/strings.xml
+++ b/packages/Shell/res/values-cs/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Chybové hlášení můžete sdílet klepnutím."</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Chybová hlášení obsahují data z různých souborů protokolů systému včetně osobních a soukromých informací. Chybová hlášení sdílejte pouze s aplikacemi a uživateli, kterým důvěřujete."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Zobrazit tuto zprávu příště"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Zprávy o chybách"</string>
 </resources>
diff --git a/packages/Shell/res/values-da/strings.xml b/packages/Shell/res/values-da/strings.xml
index 3e58bc2..8925b85 100644
--- a/packages/Shell/res/values-da/strings.xml
+++ b/packages/Shell/res/values-da/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Tryk for at dele din fejlrapport"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Fejlrapporter indeholder data fra systemets forskellige logfiler, f.eks. personlige og private oplysninger. Del kun fejlrapporter med apps og personer, du har tillid til."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Vis denne underretning næste gang"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Fejlrapporter"</string>
 </resources>
diff --git a/packages/Shell/res/values-de/strings.xml b/packages/Shell/res/values-de/strings.xml
index f7387ff..19d58fe 100644
--- a/packages/Shell/res/values-de/strings.xml
+++ b/packages/Shell/res/values-de/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Tippen, um Fehlerbericht zu teilen"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Fehlerberichte enthalten Daten aus verschiedenen Protokolldateien des Systems, darunter auch personenbezogene und private Daten. Teilen Sie Fehlerberichte nur mit Apps und Personen, denen Sie vertrauen."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Diese Nachricht nächstes Mal zeigen"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Fehlerberichte"</string>
 </resources>
diff --git a/packages/Shell/res/values-el/strings.xml b/packages/Shell/res/values-el/strings.xml
index 529b420..5fadaa4 100644
--- a/packages/Shell/res/values-el/strings.xml
+++ b/packages/Shell/res/values-el/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Αγγίξτε για να μοιραστείτε τη αναφορά σφαλμάτων"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Οι αναφορές σφαλμάτων περιέχουν δεδομένα από τα διάφορα αρχεία καταγραφής του συστήματος, συμπεριλαμβανομένων προσωπικών και ιδιωτικών πληροφοριών. Να μοιράζεστε αναφορές σφαλμάτων μόνο με εφαρμογές και άτομα που εμπιστεύεστε."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Εμφάνιση αυτού του μηνύματος την επόμενη φορά"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Αναφορές σφαλμάτων"</string>
 </resources>
diff --git a/packages/Shell/res/values-en-rAU/strings.xml b/packages/Shell/res/values-en-rAU/strings.xml
index fab4223..b50fc2a 100644
--- a/packages/Shell/res/values-en-rAU/strings.xml
+++ b/packages/Shell/res/values-en-rAU/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Touch to share your bug report"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Bug reports contain data from the system\'s various log files, including personal and private information. Only share bug reports with apps and people that you trust."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Show this message next time"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Bug reports"</string>
 </resources>
diff --git a/packages/Shell/res/values-en-rGB/strings.xml b/packages/Shell/res/values-en-rGB/strings.xml
index fab4223..b50fc2a 100644
--- a/packages/Shell/res/values-en-rGB/strings.xml
+++ b/packages/Shell/res/values-en-rGB/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Touch to share your bug report"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Bug reports contain data from the system\'s various log files, including personal and private information. Only share bug reports with apps and people that you trust."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Show this message next time"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Bug reports"</string>
 </resources>
diff --git a/packages/Shell/res/values-en-rIN/strings.xml b/packages/Shell/res/values-en-rIN/strings.xml
index fab4223..b50fc2a 100644
--- a/packages/Shell/res/values-en-rIN/strings.xml
+++ b/packages/Shell/res/values-en-rIN/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Touch to share your bug report"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Bug reports contain data from the system\'s various log files, including personal and private information. Only share bug reports with apps and people that you trust."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Show this message next time"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Bug reports"</string>
 </resources>
diff --git a/packages/Shell/res/values-es-rUS/strings.xml b/packages/Shell/res/values-es-rUS/strings.xml
index d01561c..06edfdc 100644
--- a/packages/Shell/res/values-es-rUS/strings.xml
+++ b/packages/Shell/res/values-es-rUS/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toca para compartir tu informe de errores."</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Los informes de errores contienen datos de los distintos archivos de registro del sistema, incluida la información personal y privada. Comparte los informes de errores únicamente con aplicaciones y personas en las que confíes."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar este mensaje la próxima vez"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Informes de errores"</string>
 </resources>
diff --git a/packages/Shell/res/values-es/strings.xml b/packages/Shell/res/values-es/strings.xml
index 19bfc25..3398ca3 100644
--- a/packages/Shell/res/values-es/strings.xml
+++ b/packages/Shell/res/values-es/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toca para compartir tu informe de error"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Los informes de errores contienen datos de los distintos archivos de registro del sistema, incluida información personal y privada. Comparte los informes de errores únicamente con aplicaciones y usuarios en los que confíes."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar este mensaje la próxima vez"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Informes de error"</string>
 </resources>
diff --git a/packages/Shell/res/values-et-rEE/strings.xml b/packages/Shell/res/values-et-rEE/strings.xml
index 7de367d..549ee26 100644
--- a/packages/Shell/res/values-et-rEE/strings.xml
+++ b/packages/Shell/res/values-et-rEE/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Veaaruande jagamiseks puudutage"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Veaaruanded sisaldavad andmeid erinevatest süsteemi logifailidest, sh isiklikku ja privaatset teavet. Jagage veaaruandeid ainult usaldusväärsete rakenduste ja inimestega."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Kuva see sõnum järgmisel korral"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Veaaruanded"</string>
 </resources>
diff --git a/packages/Shell/res/values-eu-rES/strings.xml b/packages/Shell/res/values-eu-rES/strings.xml
index 0c42cab..048ab8e 100644
--- a/packages/Shell/res/values-eu-rES/strings.xml
+++ b/packages/Shell/res/values-eu-rES/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Akatsen txostena partekatzeko, ukitu"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Akatsen txostenek sistemaren erregistro-fitxategietako datuak dauzkate, informazio pertsonala eta pribatua barne. Akatsen txostenak partekatzen badituzu, partekatu soilik aplikazio eta pertsona fidagarriekin."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Erakutsi mezu hau hurrengoan"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Akatsen txostenak"</string>
 </resources>
diff --git a/packages/Shell/res/values-fa/strings.xml b/packages/Shell/res/values-fa/strings.xml
index a737a77..f42ba81 100644
--- a/packages/Shell/res/values-fa/strings.xml
+++ b/packages/Shell/res/values-fa/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"جهت اشتراک‌گذاری گزارش اشکال خود لمس کنید"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"گزارش‌های اشکال حاوی داده‌هایی از فایل‌های گزارش مختلف در سیستم هستند، شامل اطلاعات شخصی و خصوصی. گزارش‌های اشکال را فقط با افراد و برنامه‌های مورد اعتماد خود به اشتراک بگذارید."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"دفعه بعد این پیام نشان داده شود"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"گزارش اشکال"</string>
 </resources>
diff --git a/packages/Shell/res/values-fi/strings.xml b/packages/Shell/res/values-fi/strings.xml
index 247cb6f..26003b3 100644
--- a/packages/Shell/res/values-fi/strings.xml
+++ b/packages/Shell/res/values-fi/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Jaa virheraportti koskettamalla tätä"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Virheraportit sisältävät järjestelmän lokitietoja, ja niihin voi sisältyä henkilökohtaisia ja yksityisiä tietoja. Jaa virheraportteja vain luotettaville sovelluksille ja käyttäjille."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Näytä tämä viesti seuraavalla kerralla"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Virheraportit"</string>
 </resources>
diff --git a/packages/Shell/res/values-fr-rCA/strings.xml b/packages/Shell/res/values-fr-rCA/strings.xml
index d82ec95..b4b81ec 100644
--- a/packages/Shell/res/values-fr-rCA/strings.xml
+++ b/packages/Shell/res/values-fr-rCA/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Appuyer ici pour partager votre rapport de bogue"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Les rapports de bogue contiennent des données des fichiers journaux du système, y compris des informations personnelles et privées. Ne partagez les rapports de bogue qu\'avec les applications et les personnes que vous estimez fiables."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Afficher ce message la prochaine fois"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Rapports de bogues"</string>
 </resources>
diff --git a/packages/Shell/res/values-fr/strings.xml b/packages/Shell/res/values-fr/strings.xml
index c52dd8d..e801054 100644
--- a/packages/Shell/res/values-fr/strings.xml
+++ b/packages/Shell/res/values-fr/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Appuyez ici pour partager le rapport de bug"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Les rapports de bug contiennent des données des fichiers journaux du système, y compris des informations personnelles et privées. Ne partagez les rapports de bug qu\'avec les applications et les personnes que vous estimez fiables."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Afficher ce message la prochaine fois"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Rapports d\'erreur"</string>
 </resources>
diff --git a/packages/Shell/res/values-gl-rES/strings.xml b/packages/Shell/res/values-gl-rES/strings.xml
index 8d01a8b..5690c9e 100644
--- a/packages/Shell/res/values-gl-rES/strings.xml
+++ b/packages/Shell/res/values-gl-rES/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toca aquí para compartir o teu informe de erros"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Os informes de erros conteñen datos dos distintos ficheiros de rexistro do sistema, incluída información persoal e privada. Comparte os informes de erros unicamente con aplicacións e persoas de confianza."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar esta mensaxe a próxima vez"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Informes de erros"</string>
 </resources>
diff --git a/packages/Shell/res/values-hi/strings.xml b/packages/Shell/res/values-hi/strings.xml
index daf6553..aee1121 100644
--- a/packages/Shell/res/values-hi/strings.xml
+++ b/packages/Shell/res/values-hi/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"अपनी बग रिपोर्ट साझा करने के लिए स्पर्श करें"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"बग रिपोर्ट में व्यक्तिगत और निजी जानकारी सहित, सिस्टम की विभिन्न लॉग फ़ाइलों का डेटा होता है. बग रिपोर्ट केवल विश्वसनीय ऐप्स  और व्यक्तियों से ही साझा करें."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"यह संदेश अगली बार दिखाएं"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"बग रिपोर्ट"</string>
 </resources>
diff --git a/packages/Shell/res/values-hr/strings.xml b/packages/Shell/res/values-hr/strings.xml
index da01dd8..cf122ab 100644
--- a/packages/Shell/res/values-hr/strings.xml
+++ b/packages/Shell/res/values-hr/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Dodirnite za dijeljenje prijave programske pogreške"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Prijave programskih pogrešaka sadržavaju podatke iz različitih datoteka zapisnika sustava, uključujući osobne i privatne informacije. Prijave programskih pogrešaka dijelite samo s aplikacijama i osobama koje smatrate pouzdanima."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Prikaži tu poruku sljedeći put"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Izvješća o programskim pogreškama"</string>
 </resources>
diff --git a/packages/Shell/res/values-hu/strings.xml b/packages/Shell/res/values-hu/strings.xml
index 0ebdc3d..f0bc227 100644
--- a/packages/Shell/res/values-hu/strings.xml
+++ b/packages/Shell/res/values-hu/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Érintse meg a programhiba-jelentés megosztásához"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"A programhiba-jelentések a rendszer különféle naplófájljaiból származó adatokat tartalmaznak, köztük személyes és magánjellegű információkat is. Csak olyan alkalmazásokkal és személyekkel osszon meg programhiba-jelentéseket, amelyekben vagy akikben megbízik."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Üzenet mutatása legközelebb"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Hibajelentések"</string>
 </resources>
diff --git a/packages/Shell/res/values-hy-rAM/strings.xml b/packages/Shell/res/values-hy-rAM/strings.xml
index 80b1ccb..a254192 100644
--- a/packages/Shell/res/values-hy-rAM/strings.xml
+++ b/packages/Shell/res/values-hy-rAM/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Հպեք` ձեր վրիպակի մասին զեկույցը տարածելու համար"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Վրիպակի զեկույցները պարունակում են տվյալներ համակարգի տարբեր մուտքի ֆայլերից, այդ թվում նաև անհատական ​​և գաղտնի տեղեկություններ: Վրիպակի զեկույցները կիսեք միայն այն հավելվածների և մարդկանց հետ, որոնց վստահում եք:"</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Այս հաղորդագրությունը ցույց տալ հաջորդ անգամ"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Վրիպակների հաշվետվություններ"</string>
 </resources>
diff --git a/packages/Shell/res/values-in/strings.xml b/packages/Shell/res/values-in/strings.xml
index b1c7293..627fc5e 100644
--- a/packages/Shell/res/values-in/strings.xml
+++ b/packages/Shell/res/values-in/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Sentuh untuk membagikan laporan bug Anda"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Laporan bug berisi data dari berbagai file log sistem, termasuk informasi pribadi dan rahasia. Hanya bagikan laporan bug dengan aplikasi dan orang yang Anda percaya."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Tampilkan pesan ini lain kali"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Laporan bug"</string>
 </resources>
diff --git a/packages/Shell/res/values-is-rIS/strings.xml b/packages/Shell/res/values-is-rIS/strings.xml
index 3f4fd3b..dbd39c4 100644
--- a/packages/Shell/res/values-is-rIS/strings.xml
+++ b/packages/Shell/res/values-is-rIS/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Snertu til að deila villutilkynningunni"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Villutilkynningar innihalda gögn úr hinum ýmsu annálsskrám kerfisins, þ. á m. persónuleg gögn og trúnaðarupplýsingar. Deildu villutilkynningum eingöngu með forritum og fólki sem þú treystir."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Sýna þessi skilaboð næst"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Villutilkynningar"</string>
 </resources>
diff --git a/packages/Shell/res/values-it/strings.xml b/packages/Shell/res/values-it/strings.xml
index 9e86aff..cd63891 100644
--- a/packages/Shell/res/values-it/strings.xml
+++ b/packages/Shell/res/values-it/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Tocca per condividere la segnalazione di bug"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Le segnalazioni di bug contengono dati da vari file di log del sistema, incluse informazioni personali e private. Condividi le segnalazioni di bug solo con app e persone attendibili."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostra questo messaggio la prossima volta"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Rapporti sui bug"</string>
 </resources>
diff --git a/packages/Shell/res/values-iw/strings.xml b/packages/Shell/res/values-iw/strings.xml
index 4e06d03..39c784f 100644
--- a/packages/Shell/res/values-iw/strings.xml
+++ b/packages/Shell/res/values-iw/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"גע כדי לשתף את דוח הבאגים שלך"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"דוחות על באגים כוללים נתונים מקובצי היומן השונים במערכת, כולל מידע אישי ופרטי. שתף דוחות באגים רק עם אפליקציות ואנשים שאתה סומך עליהם."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"הצג את ההודעה הזו בפעם הבאה"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"דוחות באגים"</string>
 </resources>
diff --git a/packages/Shell/res/values-ja/strings.xml b/packages/Shell/res/values-ja/strings.xml
index 1b24f31..48be802 100644
--- a/packages/Shell/res/values-ja/strings.xml
+++ b/packages/Shell/res/values-ja/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"バグレポートを共有するにはタップします"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"バグレポートには、個人の非公開情報など、システムのさまざまなログファイルのデータが含まれます。共有する場合は信頼するアプリとユーザーのみを選択してください。"</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"このメッセージを次回も表示する"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"バグレポート"</string>
 </resources>
diff --git a/packages/Shell/res/values-ka-rGE/strings.xml b/packages/Shell/res/values-ka-rGE/strings.xml
index 977e9aa..bb539d0 100644
--- a/packages/Shell/res/values-ka-rGE/strings.xml
+++ b/packages/Shell/res/values-ka-rGE/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"შეეხეთ თქვენი ხარვეზების ანგარიშის გასაზიარებლად"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"ხარვეზის ანგარიშები მოიცავს მონაცემებს სხვადასხვა სისტემური ჟურნალის ფაილებიდან, მათ შორის პირად და კონფიდენციალურ ინფორმაციას."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"შემდგომში აჩვენე ეს შეტყობინება"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"შეცდომების ანგარიშები"</string>
 </resources>
diff --git a/packages/Shell/res/values-kk-rKZ/strings.xml b/packages/Shell/res/values-kk-rKZ/strings.xml
index 9969eff..b22ca45 100644
--- a/packages/Shell/res/values-kk-rKZ/strings.xml
+++ b/packages/Shell/res/values-kk-rKZ/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Бөліс үшін, вирус туралы баянатты түртіңіз."</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Вирус туралы баянатта жүйеде тіркелген әртүрлі файлдар туралы деректер болады, оған жеке және құпия ақпарат та кіреді. Вирус баянаттарын сенімді қолданбалар және сенімді адамдармен ғана бөлісіңіз."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Бұл хабарды келесі жолы көрсетіңіз"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Қате туралы баяндамалар"</string>
 </resources>
diff --git a/packages/Shell/res/values-km-rKH/strings.xml b/packages/Shell/res/values-km-rKH/strings.xml
index 04e3170..50c893b 100644
--- a/packages/Shell/res/values-km-rKH/strings.xml
+++ b/packages/Shell/res/values-km-rKH/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"ប៉ះ​ ដើម្បី​ចែក​រំលែក​របាយការណ៍​កំហុស​របស់​អ្នក"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"របាយការណ៍​កំហុស​រួមមាន​ឯកសារ​កំណត់​ហេតុ​ផ្សេងៗ​របស់​ប្រព័ន្ធ រួមមាន​ព័ត៌មាន​ផ្ទាល់ខ្លួន និង​ឯកជន។ ចែករំលែក​របាយការណ៍​កំហុស​ជា​មួយ​កម្មវិធី និង​មនុស្ស​ដែល​អ្នក​ទុក​ចិត្ត។"</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"បង្ហាញ​សារ​នេះ​ពេល​ក្រោយ"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"រាយការណ៍ពីកំហុស"</string>
 </resources>
diff --git a/packages/Shell/res/values-kn-rIN/strings.xml b/packages/Shell/res/values-kn-rIN/strings.xml
index 42e9eaa..7ab6abf 100644
--- a/packages/Shell/res/values-kn-rIN/strings.xml
+++ b/packages/Shell/res/values-kn-rIN/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"ನಿಮ್ಮ ದೋಷದ ವರದಿಯನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ಸ್ಪರ್ಶಿಸಿ"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"ವೈಯಕ್ತಿಕ ಮತ್ತು ಖಾಸಗಿ ಮಾಹಿತಿಯು ಸೇರಿದಂತೆ, ಸಿಸ್ಟಂನ ಹಲವಾರು ಲಾಗ್ ಫೈಲ್‌ಗಳಿಂದ ಡೇಟಾವನ್ನು ದೋಷದ ವರದಿಗಳು ಒಳಗೊಂಡಿವೆ. ನೀವು ನಂಬುವಂತಹ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ಜನರೊಂದಿಗೆ ಮಾತ್ರ ದೋಷದ ವರದಿಗಳನ್ನು ಹಂಚಿಕೊಳ್ಳಿ."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ಈ ಸಂದೇಶವನ್ನು ಮುಂದಿನ ಬಾರಿ ತೋರಿಸಿ"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"ದೋಷ ವರದಿಗಳು"</string>
 </resources>
diff --git a/packages/Shell/res/values-ko/strings.xml b/packages/Shell/res/values-ko/strings.xml
index 3e0dd0b..da0b31f 100644
--- a/packages/Shell/res/values-ko/strings.xml
+++ b/packages/Shell/res/values-ko/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"버그 신고서를 공유하려면 터치하세요."</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"버그 신고서는 시스템의 다양한 로그 파일 데이터(예: 개인 및 비공개 정보)를 포함합니다. 신뢰할 수 있는 앱과 사용자에게만 버그 신고서를 공유하세요."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"다음에 이 메시지 표시"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"버그 신고"</string>
 </resources>
diff --git a/packages/Shell/res/values-ky-rKG/strings.xml b/packages/Shell/res/values-ky-rKG/strings.xml
index 6d9ace7..7183f77 100644
--- a/packages/Shell/res/values-ky-rKG/strings.xml
+++ b/packages/Shell/res/values-ky-rKG/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Ката тууралуу билдирүүңүздү жөнөтүш үчүн, тийиңиз"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Ката тууралуу билдирүүлөр системанын ар кандай лог файлдарынын берилиштерин камтыйт, аларга өздүк жана купуя маалыматтар дагы кирет. Ката тууралуу билдирүүлөрдү сиз ишенген колдонмолор жана адамдар менен гана бөлүшүңүз."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Бул билдирүү кийин көрсөтүлсүн"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Мүчүлүштүктөрдү кабарлоолор"</string>
 </resources>
diff --git a/packages/Shell/res/values-lo-rLA/strings.xml b/packages/Shell/res/values-lo-rLA/strings.xml
index b2724ad..fcc58e9 100644
--- a/packages/Shell/res/values-lo-rLA/strings.xml
+++ b/packages/Shell/res/values-lo-rLA/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"ແຕະເພື່ອສົ່ງການລາຍງານປັນຫາຂອງທ່ານ"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"ການລາຍງານຂໍ້ຜິດພາດປະກອບມີ ຂໍ້ມູນຈາກໄຟລ໌ບັນທຶກຂອງລະບົບຫຼາຍໄຟລ໌, ຮວມທັງຂໍ້ມູນສ່ວນໂຕນຳ. ທ່ານຕ້ອງແບ່ງປັນລາຍງານຂໍ້ຜິດພາດໃຫ້ແອັບຯ ແລະຄົນທີ່ທ່ານເຊື່ອຖືໄດ້ເທົ່ານັ້ນ."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ສະແດງຂໍ້ຄວາມນີ້ອີກໃນເທື່ອຕໍ່ໄປ"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"ລາຍ​ງານ​ບັນ​ຫາ"</string>
 </resources>
diff --git a/packages/Shell/res/values-lt/strings.xml b/packages/Shell/res/values-lt/strings.xml
index a78b1db..51655a4 100644
--- a/packages/Shell/res/values-lt/strings.xml
+++ b/packages/Shell/res/values-lt/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Palieskite, kad bendrintumėte riktų ataskaitą"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Riktų ataskaitose pateikiami duomenys iš įvairių sistemos žurnalo failų, įskaitant asmeninę ir privačią informaciją. Riktų ataskaitas bendrinkite tik su patikimomis programomis ir žmonėmis."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Rodyti šį pranešimą kitą kartą"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Riktų ataskaitos"</string>
 </resources>
diff --git a/packages/Shell/res/values-lv/strings.xml b/packages/Shell/res/values-lv/strings.xml
index 430cb8d..cf1a75a 100644
--- a/packages/Shell/res/values-lv/strings.xml
+++ b/packages/Shell/res/values-lv/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Pieskarieties, lai kopīgotu kļūdu pārskatu."</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Kļūdu pārskatā ir iekļauti dati no dažādiem sistēmas žurnālfailiem, tostarp personas dati un privāta informācija. Kļūdu pārskatus ieteicams kopīgot tikai ar uzticamām lietotnēm un lietotājiem."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Rādīt šo ziņojumu nākamajā reizē"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Kļūdu ziņojumi"</string>
 </resources>
diff --git a/packages/Shell/res/values-mk-rMK/strings.xml b/packages/Shell/res/values-mk-rMK/strings.xml
index e6c2c92..785a841 100644
--- a/packages/Shell/res/values-mk-rMK/strings.xml
+++ b/packages/Shell/res/values-mk-rMK/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Допри да се сподели твојот извештај за грешка"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Извештаите за грешка содржат податоци од разни датотеки за евиденција на системот, вклучувајќи лични и приватни информации. Извештаите за грешка споделувајте ги само со апликации и луѓе на коишто им верувате."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Прикажи ја поракава следниот пат"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Извештаи за грешки"</string>
 </resources>
diff --git a/packages/Shell/res/values-ml-rIN/strings.xml b/packages/Shell/res/values-ml-rIN/strings.xml
index 5f3e0b6..8fa6d67c 100644
--- a/packages/Shell/res/values-ml-rIN/strings.xml
+++ b/packages/Shell/res/values-ml-rIN/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"നിങ്ങളുടെ ബഗ് റിപ്പോർട്ട് പങ്കിടാൻ സ്‌പർശിക്കുക"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"വ്യക്തിഗതവും സ്വകാര്യവുമായ വിവരങ്ങൾ ഉൾപ്പെടെ, സിസ്റ്റത്തിന്റെ നിരവധി ലോഗ് ഫയലുകളിൽ നിന്നുള്ള ഡാറ്റ, ബഗ് റിപ്പോർട്ടുകളിൽ അടങ്ങിയിരിക്കുന്നു. നിങ്ങൾ വിശ്വസിക്കുന്ന അപ്ലിക്കേഷനുകൾക്കും ആളുകൾക്കും മാത്രം ബഗ് റിപ്പോർട്ടുകൾ പങ്കിടുക."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ഈ സന്ദേശം അടുത്ത തവണ ദൃശ്യമാക്കുക"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"ബഗ് റിപ്പോർട്ടുകൾ"</string>
 </resources>
diff --git a/packages/Shell/res/values-mn-rMN/strings.xml b/packages/Shell/res/values-mn-rMN/strings.xml
index 4637f2d..4010072 100644
--- a/packages/Shell/res/values-mn-rMN/strings.xml
+++ b/packages/Shell/res/values-mn-rMN/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Та алдааны мэдэгдлийг хуваалцах бол хүрнэ үү"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Алдааны репорт нь хувийн болон нууц мэдээлэл зэргийг агуулсан системийн төрөл бүрийн лог файлын датаг агуулна. Алдааны репортыг зөвхөн итгэлтэй апп болон хүмүүст хуваалцана уу."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Энэ мессежийг дараагийн удаа харуулах"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Гэмтлийн тухай тайлан"</string>
 </resources>
diff --git a/packages/Shell/res/values-mr-rIN/strings.xml b/packages/Shell/res/values-mr-rIN/strings.xml
index ca3c4b3..c4993fc 100644
--- a/packages/Shell/res/values-mr-rIN/strings.xml
+++ b/packages/Shell/res/values-mr-rIN/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"आपला दोष अहवाल सामायिक करण्‍यासाठी स्‍पर्श करा"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"दोष अहवालांमध्‍ये वैयक्तिक आणि खाजगी माहितीसह, सिस्‍टमच्‍या अनेक लॉग फायलींमधील डेटा असतो. केवळ आपला विश्वास असलेल्‍या अ‍ॅप्‍स आणि लोकांसह दोष अहवाल सामायिक करा."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"पुढील वेळी हा संदेश दर्शवा"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"दोष अहवाल"</string>
 </resources>
diff --git a/packages/Shell/res/values-ms-rMY/strings.xml b/packages/Shell/res/values-ms-rMY/strings.xml
index d7bdc78..f3e4f7e 100644
--- a/packages/Shell/res/values-ms-rMY/strings.xml
+++ b/packages/Shell/res/values-ms-rMY/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Sentuh untuk berkongsi laporan pepijat anda"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Laporan pepijat mengandungi data dari pelbagai fail log sistem, termasuk maklumat peribadi dan sulit. Kongsikan laporan pepijat hanya dengan apl dan orang yang anda percayai."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Tunjukkan mesej ini pada masa akan datang"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Laporan pepijat"</string>
 </resources>
diff --git a/packages/Shell/res/values-my-rMM/strings.xml b/packages/Shell/res/values-my-rMM/strings.xml
index 6cac668..d223bd9 100644
--- a/packages/Shell/res/values-my-rMM/strings.xml
+++ b/packages/Shell/res/values-my-rMM/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"အမှားအယွင်း မှတ်တမ်းကို မျှဝေရန် ထိလိုက်ပါ"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"အမှားအယွင်း မှတ်တမ်းမှာ ပါရှိသော အချက်အလက်များမှာ ကိုယ်ရေးကိုယ်တာ နဲ့ လုံခြုံရေး အချက်အလက်များပါဝင်သော စနစ်မှ ပြုလုပ်မှု မှတ်တမ်းများ ဖြစ်ပါသည်၊ အမှားအယွင်း မှတ်တမ်းများကို ယုံကြည်ရသော အပလီကေးရှင်းများနဲ့ လူများကိုသာ ပေးဝေပြသမှု လုပ်ပါရန်။"</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ဤစာတန်းကို နောက်တစ်ခါတွင် ပြရန်"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"ချို့ယွင်းမှု အစီရင်ခံစာများ"</string>
 </resources>
diff --git a/packages/Shell/res/values-nb/strings.xml b/packages/Shell/res/values-nb/strings.xml
index 657a209..c00e2d0 100644
--- a/packages/Shell/res/values-nb/strings.xml
+++ b/packages/Shell/res/values-nb/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Trykk for å dele feilrapporten din"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Feilrapporter inkluderer data fra systemets forskjellige loggfiler. Dette omfatter personlig og privat informasjon. Du bør bare dele feilrapporter med apper og folk du stoler på."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Vis denne meldingen neste gang"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Feilrapporter"</string>
 </resources>
diff --git a/packages/Shell/res/values-ne-rNP/strings.xml b/packages/Shell/res/values-ne-rNP/strings.xml
index a5514822..7344889 100644
--- a/packages/Shell/res/values-ne-rNP/strings.xml
+++ b/packages/Shell/res/values-ne-rNP/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"तपाईंको बग रिपोर्ट साझेदारी गर्न छुनुहोस्"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"बग रिपोर्टहरूमा प्रणालीका विभिन्न लग फाइलहरूबाट व्यक्तिगत तथा नीजि सूचनासहितको डेटा रहन्छ।  बग रिपोर्टहरू अनुप्रयोगहरू र तपाईँले विश्वास गरेका व्यक्तिहरूसँग मात्र साझेदारी गर्नुहोस्।"</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"यो सन्देश अर्को पटक देखाउनुहोस्"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"बग रिपोर्टहरू"</string>
 </resources>
diff --git a/packages/Shell/res/values-nl/strings.xml b/packages/Shell/res/values-nl/strings.xml
index 51ae329..2936387 100644
--- a/packages/Shell/res/values-nl/strings.xml
+++ b/packages/Shell/res/values-nl/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Raak aan om uw foutenrapport te delen"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Foutenrapporten bevatten gegevens uit de verschillende logbestanden van het systeem, waaronder persoonlijke en privégegevens. Deel foutenrapporten alleen met apps en mensen die u vertrouwt."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Dit bericht de volgende keer weergeven"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Foutenrapporten"</string>
 </resources>
diff --git a/packages/Shell/res/values-pl/strings.xml b/packages/Shell/res/values-pl/strings.xml
index 2c810d6..266f070 100644
--- a/packages/Shell/res/values-pl/strings.xml
+++ b/packages/Shell/res/values-pl/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Kliknij, by udostępnić raport o błędach"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Raporty o błędach zawierają dane z różnych plików dzienników systemu, w tym dane osobowe i prywatne. Udostępniaj je tylko aplikacjom i osobom, którym ufasz."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Pokaż ten komunikat następnym razem"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Zgłoszenia błędów"</string>
 </resources>
diff --git a/packages/Shell/res/values-pt-rPT/strings.xml b/packages/Shell/res/values-pt-rPT/strings.xml
index 7a833d6..648ef94 100644
--- a/packages/Shell/res/values-pt-rPT/strings.xml
+++ b/packages/Shell/res/values-pt-rPT/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toque para partilhar o relatório de erros"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Os relatórios de erros incluem dados de vários ficheiros de registo do sistema, nomeadamente informações pessoais e privadas. Partilhe relatórios de erros apenas com aplicações e pessoas fidedignas."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar esta mensagem da próxima vez"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Relatórios de erros"</string>
 </resources>
diff --git a/packages/Shell/res/values-pt/strings.xml b/packages/Shell/res/values-pt/strings.xml
index c166bf3..e04d600 100644
--- a/packages/Shell/res/values-pt/strings.xml
+++ b/packages/Shell/res/values-pt/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Toque para compartilhar seu relatório de bugs"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Os relatórios de bugs contêm dados de diversos arquivos de registro do sistema, inclusive informações pessoais e particulares. Compartilhe relatórios de bugs somente com apps e pessoas nos quais você confia."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Mostrar esta mensagem da próxima vez"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Relatórios de bugs"</string>
 </resources>
diff --git a/packages/Shell/res/values-ro/strings.xml b/packages/Shell/res/values-ro/strings.xml
index 537ba3d..aab29b7 100644
--- a/packages/Shell/res/values-ro/strings.xml
+++ b/packages/Shell/res/values-ro/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Atingeți pentru a permite accesul la raportul despre erori"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Rapoartele despre erori conțin date din diferite fișiere de jurnal ale sistemului, inclusiv informații private și personale. Permiteți accesul la rapoartele despre erori numai aplicațiilor și persoanelor în care aveți încredere."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Afișați acest mesaj data viitoare"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Rapoarte de erori"</string>
 </resources>
diff --git a/packages/Shell/res/values-ru/strings.xml b/packages/Shell/res/values-ru/strings.xml
index 77a8cd0..1f1444d 100644
--- a/packages/Shell/res/values-ru/strings.xml
+++ b/packages/Shell/res/values-ru/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Нажмите, чтобы отправить отчет об ошибках"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Отчеты об ошибках содержат данные различных системных журналов и могут включать личную информацию. Рекомендуем открывать к ним доступ только лицам и приложениям, заслуживающим доверие."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Показать это сообщение в следующий раз"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Отчеты об ошибках"</string>
 </resources>
diff --git a/packages/Shell/res/values-si-rLK/strings.xml b/packages/Shell/res/values-si-rLK/strings.xml
index 34d7f82..4244f2b 100644
--- a/packages/Shell/res/values-si-rLK/strings.xml
+++ b/packages/Shell/res/values-si-rLK/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"ඔබගේ දෝෂ වාර්තාව බෙදා ගැනීමට ස්පර්ශ කරන්න"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"පුද්ගලික සහ පෞද්ගලික තොරතුරු ඇතුළත්ව පද්ධතියේ විවිධ ලොග් ගොනු වල දත්ත දෝෂ වාර්තාවේ අඩංගු වේ. ඔබට විශ්වාසවන්ත යෙදුම් සහ පුද්ගලයින් සමඟ පමණක් දෝෂ වාර්තා බෙදා ගන්න."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"ඊළඟ වෙලාවේ මෙම පණිවිඩය පෙන්වන්න"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"දෝෂ වාර්තා"</string>
 </resources>
diff --git a/packages/Shell/res/values-sk/strings.xml b/packages/Shell/res/values-sk/strings.xml
index 0d6940d..a79059d 100644
--- a/packages/Shell/res/values-sk/strings.xml
+++ b/packages/Shell/res/values-sk/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Hlásenie o chybách môžete zdielať klepnutím"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Správy o chybách obsahujú údaje z rôznych súborov denníkov systému vrátane osobných a súkromných informácií. Zdieľajte ich iba s dôveryhodnými aplikáciami a ľuďmi."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Zobraziť túto správu nabudúce"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Hlásenia o chybe"</string>
 </resources>
diff --git a/packages/Shell/res/values-sl/strings.xml b/packages/Shell/res/values-sl/strings.xml
index b2caa07..8c3bedc 100644
--- a/packages/Shell/res/values-sl/strings.xml
+++ b/packages/Shell/res/values-sl/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Dotaknite se, če želite deliti sporočilo o napaki z drugimi"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Poročila o napakah vsebujejo podatke iz različnih dnevniških datotek sistema, vključno z osebnimi in zasebnimi podatki. Poročila o napakah delite samo z aplikacijami in ljudmi, ki jim zaupate."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Pokaži to sporočilo naslednjič"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Poročila o napakah"</string>
 </resources>
diff --git a/packages/Shell/res/values-sr/strings.xml b/packages/Shell/res/values-sr/strings.xml
index 9e4e595..763bd2b 100644
--- a/packages/Shell/res/values-sr/strings.xml
+++ b/packages/Shell/res/values-sr/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Додирните да бисте делили извештај о грешци"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Извештаји о грешкама садрже податке из различитих системских датотека евиденције, укључујући личне и приватне податке. Делите извештаје о грешкама само са апликацијама и људима у које имате поверења."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Прикажи ову поруку следећи пут"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Извештаји о грешкама"</string>
 </resources>
diff --git a/packages/Shell/res/values-sv/strings.xml b/packages/Shell/res/values-sv/strings.xml
index c26a7ce..47ee2f7 100644
--- a/packages/Shell/res/values-sv/strings.xml
+++ b/packages/Shell/res/values-sv/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Tryck om du vill dela felrapporten"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Felrapporter innehåller data från systemets olika loggfiler, inklusive personliga och privata uppgifter. Dela bara felrapporter med personer du litar på."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Visa det här meddelandet nästa gång"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Felrapporter"</string>
 </resources>
diff --git a/packages/Shell/res/values-sw/strings.xml b/packages/Shell/res/values-sw/strings.xml
index 4553328..0d12cc9 100644
--- a/packages/Shell/res/values-sw/strings.xml
+++ b/packages/Shell/res/values-sw/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Gusa ili ushiriki ripoti yako ya hitilafu"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Ripoti ya hitilafu ina data kutoka kwenye faili za kumbukumbu mbalimbali za mfumo, pamoja na maelezo ya kibinafsi na faragha. Shiriki ripoti ya hitilafu na programu na watu unaowaamini pekee."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Onyesha ujumbe huu wakati mwingine"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Ripoti za hitilafu"</string>
 </resources>
diff --git a/packages/Shell/res/values-ta-rIN/strings.xml b/packages/Shell/res/values-ta-rIN/strings.xml
index d94555d..cd0462b 100644
--- a/packages/Shell/res/values-ta-rIN/strings.xml
+++ b/packages/Shell/res/values-ta-rIN/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"உங்கள் பிழை அறிக்கையைப் பகிர, தொடவும்"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"பிழை அறிக்கைகளில், சொந்த வாழ்க்கை மற்றும் தனிப்பட்ட தகவல் உள்பட கணினியின் பல்வேறு பதிவுகளில் உள்ள தரவு இருக்கும். நீங்கள் நம்பும் பயன்பாடுகள் மற்றும் நபர்களுடன் மட்டும் பிழை அறிக்கைகளைப் பகிரவும்."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"இந்தச் செய்தியை அடுத்த முறைக் காட்டு"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"பிழை அறிக்கைகள்"</string>
 </resources>
diff --git a/packages/Shell/res/values-te-rIN/strings.xml b/packages/Shell/res/values-te-rIN/strings.xml
index 6b5710b..127f602 100644
--- a/packages/Shell/res/values-te-rIN/strings.xml
+++ b/packages/Shell/res/values-te-rIN/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"మీ బగ్ నివేదికను భాగస్వామ్యం చేయడానికి తాకండి"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"బగ్ నివేదికలు వ్యక్తిగతమైన మరియు రహస్యమైన సమాచారంతో సహా సిస్టమ్ యొక్క విభిన్న లాగ్ ఫైల్‌ల్లోని డేటాను కలిగి ఉంటాయి. కనుక బగ్ నివేదికలను మీరు విశ్వసించే అనువర్తనాలు మరియు వ్యక్తులతో మాత్రమే భాగస్వామ్యం చేయండి."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"తదుపరిసారి ఈ సందేశాన్ని చూపు"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"బగ్ నివేదికలు"</string>
 </resources>
diff --git a/packages/Shell/res/values-th/strings.xml b/packages/Shell/res/values-th/strings.xml
index 0702952..2bc8f0d 100644
--- a/packages/Shell/res/values-th/strings.xml
+++ b/packages/Shell/res/values-th/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"แตะเพื่อแชร์รายงานข้อบกพร่องของคุณ"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"รายงานข้อบกพร่องมีข้อมูลจากไฟล์บันทึกต่างๆ ของระบบ รวมถึงข้อมูลส่วนตัว แชร์รายงานข้อบกพร่องกับแอปและบุคคลที่คุณไว้ใจเท่านั้น"</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"แสดงข้อความนี้ในครั้งต่อไป"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"รายงานข้อบกพร่อง"</string>
 </resources>
diff --git a/packages/Shell/res/values-tl/strings.xml b/packages/Shell/res/values-tl/strings.xml
index 8d42ecd..a5c0e8af 100644
--- a/packages/Shell/res/values-tl/strings.xml
+++ b/packages/Shell/res/values-tl/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Pindutin upang ibahagi ang iyong ulat ng bug"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Naglalaman ang mga ulat ng bug ng data mula sa iba\'t ibang file ng log ng system, kabilang ang personal at pribadong impormasyon. Magbahagi lang ng mga ulat ng bug sa apps at mga tao na pinagkakatiwalaan mo."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Ipakita ang mensaheng ito sa susunod"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Mga ulat sa bug"</string>
 </resources>
diff --git a/packages/Shell/res/values-tr/strings.xml b/packages/Shell/res/values-tr/strings.xml
index ed0697e..67390b7 100644
--- a/packages/Shell/res/values-tr/strings.xml
+++ b/packages/Shell/res/values-tr/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Hata raporunuzu paylaşmak için dokunun"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Hata raporları, kişisel ve özel bilgiler dahil olmak üzere sistemin çeşitli günlük dosyalarından veriler içerir. Hata raporlarını sadece güvendiğiniz uygulamalar ve kişilerle paylaşın."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Bir dahaki sefere bu iletiyi göster"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Hata raporları"</string>
 </resources>
diff --git a/packages/Shell/res/values-uk/strings.xml b/packages/Shell/res/values-uk/strings.xml
index f9f5bb3..f8f1798 100644
--- a/packages/Shell/res/values-uk/strings.xml
+++ b/packages/Shell/res/values-uk/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Торкніться, щоб надіслати звіт про помилки"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Звіти про помилки містять дані з різних файлів журналу системи, зокрема особисті та конфіденційні. Надсилайте звіт про помилки лише тим, кому довіряєте."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Показати це повідомлення наступного разу"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Звіти про помилки"</string>
 </resources>
diff --git a/packages/Shell/res/values-ur-rPK/strings.xml b/packages/Shell/res/values-ur-rPK/strings.xml
index afdb241..73d6877 100644
--- a/packages/Shell/res/values-ur-rPK/strings.xml
+++ b/packages/Shell/res/values-ur-rPK/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"اپنی بَگ رپورٹ کا اشتراک کرنے کیلئے ٹچ کریں"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"بَگ رپورٹس میں سسٹم کی مختلف لاگ فائلوں سے ڈیٹا شامل ہوتا ہے، بشمول ذاتی اور نجی معلومات۔ بَگ رپورٹس کا اشتراک صرف اپنے بھروسے مند ایپس اور لوگوں کے ساتھ کریں۔"</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"یہ پیغام اگلی بار دکھائیں"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"بگ رپورٹس"</string>
 </resources>
diff --git a/packages/Shell/res/values-uz-rUZ/strings.xml b/packages/Shell/res/values-uz-rUZ/strings.xml
index e9c3900..b221171 100644
--- a/packages/Shell/res/values-uz-rUZ/strings.xml
+++ b/packages/Shell/res/values-uz-rUZ/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Xatolik hisobotini bo‘lishish uchun barmog‘ingizni tegizing."</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Xatolik hisobotlari tizimdagi har xil jurnal fayllardagi ma’lumotlarni, shuningdek, shaxsiy hamda maxfiy ma’lumotlarni o‘z ichiga oladi. Xatolik hisobotlarini faqat ishonchli dasturlar va odamlar bilan bo‘lishing."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Ushbu xabar keyingi safar ko‘rsatilsin"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Xatoliklar hisoboti"</string>
 </resources>
diff --git a/packages/Shell/res/values-vi/strings.xml b/packages/Shell/res/values-vi/strings.xml
index 4919411..16a7df9 100644
--- a/packages/Shell/res/values-vi/strings.xml
+++ b/packages/Shell/res/values-vi/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Chạm để chia sẻ báo cáo lỗi của bạn"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Các báo cáo lỗi chứa dữ liệu từ nhiều tệp nhật ký khác nhau của hệ thống, bao gồm cả thông tin cá nhân và riêng tư. Chỉ chia sẻ báo cáo lỗi với các ứng dụng và những người mà bạn tin tưởng."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Hiển thị thông báo này vào lần tới"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Báo cáo lỗi"</string>
 </resources>
diff --git a/packages/Shell/res/values-zh-rCN/strings.xml b/packages/Shell/res/values-zh-rCN/strings.xml
index 409b5ca..17fdedd 100644
--- a/packages/Shell/res/values-zh-rCN/strings.xml
+++ b/packages/Shell/res/values-zh-rCN/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"触摸即可分享您的错误报告"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"错误报告包含的数据来自于系统的各个日志文件,其中包含个人信息和隐私信息。请务必只与您信任的应用和用户分享错误报告。"</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"下次再显示这条讯息"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"错误报告"</string>
 </resources>
diff --git a/packages/Shell/res/values-zh-rHK/strings.xml b/packages/Shell/res/values-zh-rHK/strings.xml
index f2e034b..0f70bab 100644
--- a/packages/Shell/res/values-zh-rHK/strings.xml
+++ b/packages/Shell/res/values-zh-rHK/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"輕觸即可分享您的錯誤報告"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"錯誤報告中有來自系統各個記錄檔案的資料,包括個人和私人資料。請只與您信任的應用程式和使用者分享錯誤報告。"</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"下次再顯示這則訊息"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"錯誤報告"</string>
 </resources>
diff --git a/packages/Shell/res/values-zh-rTW/strings.xml b/packages/Shell/res/values-zh-rTW/strings.xml
index fe3bcff..d7f7507 100644
--- a/packages/Shell/res/values-zh-rTW/strings.xml
+++ b/packages/Shell/res/values-zh-rTW/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"輕觸即可分享您的錯誤報告"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"錯誤報告的資料來自系統各個紀錄檔,包括個人和私密資訊。請務必只與您信任的應用程式和使用者分享錯誤報告。"</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"下次仍顯示這則訊息"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"錯誤報告"</string>
 </resources>
diff --git a/packages/Shell/res/values-zu/strings.xml b/packages/Shell/res/values-zu/strings.xml
index 38e9595..c82fb93 100644
--- a/packages/Shell/res/values-zu/strings.xml
+++ b/packages/Shell/res/values-zu/strings.xml
@@ -22,4 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"Thinta ukuze wabelane ngombiko wakho wesiphazamisi"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Imibiko yeziphazamisi iqukethe idatha yamafayela wokungena ahlukile wesistimu, afaka ulwazi lomuntu siqu noma lobumfihlo. Yabelana kuphela ngemibiko yeziphazamisi nezinhlelo zokusebenza nabantu obathembayo."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Bonisa lo mlayezo ngesikhathi esilandelayo"</string>
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"Imibiko yeziphazamiso"</string>
 </resources>
diff --git a/packages/Shell/res/values/strings.xml b/packages/Shell/res/values/strings.xml
index 51e2c95..3db0848 100644
--- a/packages/Shell/res/values/strings.xml
+++ b/packages/Shell/res/values/strings.xml
@@ -30,4 +30,7 @@
     <string name="bugreport_confirm">Bug reports contain data from the system\'s various log files, including personal and private information.  Only share bug reports with apps and people you trust.</string>
     <!-- Checkbox that indicates this dialog should be shown again when the next bugreport is taken. [CHAR LIMIT=50] -->
     <string name="bugreport_confirm_repeat">Show this message next time</string>
+
+    <!-- Title for documents backend that offers bugreports. -->
+    <string name="bugreport_storage_title">Bug reports</string>
 </resources>
diff --git a/packages/Shell/src/com/android/shell/BugreportStorageProvider.java b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
new file mode 100644
index 0000000..814aa8c
--- /dev/null
+++ b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java
@@ -0,0 +1,164 @@
+/*
+ * 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.shell;
+
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.MatrixCursor.RowBuilder;
+import android.os.CancellationSignal;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsContract.Root;
+import android.provider.DocumentsProvider;
+import android.webkit.MimeTypeMap;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+public class BugreportStorageProvider extends DocumentsProvider {
+    private static final String DOC_ID_ROOT = "bugreport";
+
+    private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
+            Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
+            Root.COLUMN_DOCUMENT_ID,
+    };
+
+    private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
+            Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME,
+            Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
+    };
+
+    private File mRoot;
+
+    @Override
+    public boolean onCreate() {
+        mRoot = new File(getContext().getFilesDir(), "bugreports");
+        return true;
+    }
+
+    @Override
+    public Cursor queryRoots(String[] projection) throws FileNotFoundException {
+        final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
+        final RowBuilder row = result.newRow();
+        row.add(Root.COLUMN_ROOT_ID, DOC_ID_ROOT);
+        row.add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED);
+        row.add(Root.COLUMN_ICON, android.R.mipmap.sym_def_app_icon);
+        row.add(Root.COLUMN_TITLE, getContext().getString(R.string.bugreport_storage_title));
+        row.add(Root.COLUMN_DOCUMENT_ID, DOC_ID_ROOT);
+        return result;
+    }
+
+    @Override
+    public Cursor queryDocument(String documentId, String[] projection)
+            throws FileNotFoundException {
+        final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
+        if (DOC_ID_ROOT.equals(documentId)) {
+            final RowBuilder row = result.newRow();
+            row.add(Document.COLUMN_DOCUMENT_ID, documentId);
+            row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
+            row.add(Document.COLUMN_DISPLAY_NAME, mRoot.getName());
+            row.add(Document.COLUMN_LAST_MODIFIED, mRoot.lastModified());
+            row.add(Document.COLUMN_FLAGS, Document.FLAG_DIR_PREFERS_LAST_MODIFIED);
+        } else {
+            addFileRow(result, getFileForDocId(documentId));
+        }
+        return result;
+    }
+
+    @Override
+    public Cursor queryChildDocuments(
+            String parentDocumentId, String[] projection, String sortOrder)
+            throws FileNotFoundException {
+        final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
+        if (DOC_ID_ROOT.equals(parentDocumentId)) {
+            final File[] files = mRoot.listFiles();
+            if (files != null) {
+                for (File file : files) {
+                    addFileRow(result, file);
+                }
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public ParcelFileDescriptor openDocument(
+            String documentId, String mode, CancellationSignal signal)
+            throws FileNotFoundException {
+        if (ParcelFileDescriptor.parseMode(mode) != ParcelFileDescriptor.MODE_READ_ONLY) {
+            throw new FileNotFoundException("Failed to open: " + documentId + ", mode = " + mode);
+        }
+        return ParcelFileDescriptor.open(getFileForDocId(documentId),
+                ParcelFileDescriptor.MODE_READ_ONLY);
+    }
+
+    @Override
+    public void deleteDocument(String documentId) throws FileNotFoundException {
+        if (!getFileForDocId(documentId).delete()) {
+            throw new FileNotFoundException("Failed to delete: " + documentId);
+        }
+    }
+
+    private static String[] resolveRootProjection(String[] projection) {
+        return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
+    }
+
+    private static String[] resolveDocumentProjection(String[] projection) {
+        return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION;
+    }
+
+    private static String getTypeForName(String name) {
+        final int lastDot = name.lastIndexOf('.');
+        if (lastDot >= 0) {
+            final String extension = name.substring(lastDot + 1).toLowerCase();
+            final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
+            if (mime != null) {
+                return mime;
+            }
+        }
+        return "application/octet-stream";
+    }
+
+    private String getDocIdForFile(File file) {
+        return DOC_ID_ROOT + ":" + file.getName();
+    }
+
+    private File getFileForDocId(String documentId) throws FileNotFoundException {
+        final int splitIndex = documentId.indexOf(':', 1);
+        final String name = documentId.substring(splitIndex + 1);
+        if (splitIndex == -1 || !DOC_ID_ROOT.equals(documentId.substring(0, splitIndex)) ||
+                !FileUtils.isValidExtFilename(name)) {
+            throw new FileNotFoundException("Invalid document ID: " + documentId);
+        }
+        final File file = new File(mRoot, name);
+        if (!file.exists()) {
+            throw new FileNotFoundException("File not found: " + documentId);
+        }
+        return file;
+    }
+
+    private void addFileRow(MatrixCursor result, File file) {
+        final RowBuilder row = result.newRow();
+        row.add(Document.COLUMN_DOCUMENT_ID, getDocIdForFile(file));
+        row.add(Document.COLUMN_MIME_TYPE, getTypeForName(file.getName()));
+        row.add(Document.COLUMN_DISPLAY_NAME, file.getName());
+        row.add(Document.COLUMN_LAST_MODIFIED, file.lastModified());
+        row.add(Document.COLUMN_FLAGS, Document.FLAG_SUPPORTS_DELETE);
+        row.add(Document.COLUMN_SIZE, file.length());
+    }
+}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 158e133..7a58c87 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -106,6 +106,9 @@
     <uses-permission android:name="android.permission.TRUST_LISTENER" />
     <uses-permission android:name="android.permission.USE_FINGERPRINT" />
 
+    <!-- Needed for WallpaperManager.clear in ImageWallpaper.updateWallpaperLocked -->
+    <uses-permission android:name="android.permission.SET_WALLPAPER"/>
+
     <!-- Recents -->
     <uses-permission android:name="android.permission.BIND_APPWIDGET" />
 
diff --git a/packages/SystemUI/res/anim/ic_volume_collapse_chevron_02_animation.xml b/packages/SystemUI/res/anim/ic_volume_collapse_chevron_02_animation.xml
new file mode 100644
index 0000000..443f2a6
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_volume_collapse_chevron_02_animation.xml
@@ -0,0 +1,25 @@
+<!--
+     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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <objectAnimator
+        android:duration="250"
+        android:interpolator="@android:interpolator/fast_out_slow_in"
+        android:pathData="M 12.0,9.0 c 0.0,0.66667 0.0,5.0 0.0,6.0"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY" />
+
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/ic_volume_collapse_rectangle_1_animation.xml b/packages/SystemUI/res/anim/ic_volume_collapse_rectangle_1_animation.xml
new file mode 100644
index 0000000..d82f670
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_volume_collapse_rectangle_1_animation.xml
@@ -0,0 +1,26 @@
+<!--
+     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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <objectAnimator
+        android:duration="200"
+        android:interpolator="@interpolator/ic_volume_collapse_animation_interpolator_0"
+        android:propertyName="rotation"
+        android:valueFrom="45.0"
+        android:valueTo="-45.0"
+        android:valueType="floatType" />
+
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/ic_volume_collapse_rectangle_2_animation.xml b/packages/SystemUI/res/anim/ic_volume_collapse_rectangle_2_animation.xml
new file mode 100644
index 0000000..0bc66bd
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_volume_collapse_rectangle_2_animation.xml
@@ -0,0 +1,26 @@
+<!--
+     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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <objectAnimator
+        android:duration="200"
+        android:interpolator="@interpolator/ic_volume_collapse_animation_interpolator_0"
+        android:propertyName="rotation"
+        android:valueFrom="-45.0"
+        android:valueTo="45.0"
+        android:valueType="floatType" />
+
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/ic_volume_expand_chevron_01_animation.xml b/packages/SystemUI/res/anim/ic_volume_expand_chevron_01_animation.xml
new file mode 100644
index 0000000..e43e645
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_volume_expand_chevron_01_animation.xml
@@ -0,0 +1,25 @@
+<!--
+     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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <objectAnimator
+        android:duration="250"
+        android:interpolator="@android:interpolator/fast_out_slow_in"
+        android:pathData="M 12.0,15.0 c 0.0,-1.0 0.0,-5.33333 0.0,-6.0"
+        android:propertyXName="translateX"
+        android:propertyYName="translateY" />
+
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/ic_volume_expand_rectangle_3_animation.xml b/packages/SystemUI/res/anim/ic_volume_expand_rectangle_3_animation.xml
new file mode 100644
index 0000000..9b575d8
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_volume_expand_rectangle_3_animation.xml
@@ -0,0 +1,26 @@
+<!--
+     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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <objectAnimator
+        android:duration="200"
+        android:interpolator="@interpolator/ic_volume_expand_animation_interpolator_0"
+        android:propertyName="rotation"
+        android:valueFrom="45.0"
+        android:valueTo="-45.0"
+        android:valueType="floatType" />
+
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/ic_volume_expand_rectangle_4_animation.xml b/packages/SystemUI/res/anim/ic_volume_expand_rectangle_4_animation.xml
new file mode 100644
index 0000000..6ae0fef
--- /dev/null
+++ b/packages/SystemUI/res/anim/ic_volume_expand_rectangle_4_animation.xml
@@ -0,0 +1,26 @@
+<!--
+     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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <objectAnimator
+        android:duration="200"
+        android:interpolator="@interpolator/ic_volume_expand_animation_interpolator_0"
+        android:propertyName="rotation"
+        android:valueFrom="-45.0"
+        android:valueTo="45.0"
+        android:valueType="floatType" />
+
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/btn_borderless_rect.xml b/packages/SystemUI/res/drawable/btn_borderless_rect.xml
index d640141..c0a89f9 100644
--- a/packages/SystemUI/res/drawable/btn_borderless_rect.xml
+++ b/packages/SystemUI/res/drawable/btn_borderless_rect.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+     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.
@@ -13,13 +13,15 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="?android:attr/colorControlHighlight">
+    android:color="?android:attr/colorControlHighlight" >
+
     <item android:id="@android:id/mask">
         <shape>
             <corners android:radius="@dimen/borderless_button_radius" />
+
             <solid android:color="@android:color/white" />
         </shape>
     </item>
+
 </ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_dnd.xml b/packages/SystemUI/res/drawable/ic_dnd.xml
new file mode 100644
index 0000000..17ecf21
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_dnd.xml
@@ -0,0 +1,26 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="48.0"
+    android:viewportWidth="48.0"
+    android:width="24dp" >
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M24.0,4.0C12.95,4.0 4.0,12.95 4.0,24.0s8.95,20.0 20.0,20.0 20.0,-8.95 20.0,-20.0S35.05,4.0 24.0,4.0zm10.0,22.0L14.0,26.0l0.0,-4.0l20.0,0.0l0.0,4.0z" />
+
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_qs_minus.xml b/packages/SystemUI/res/drawable/ic_qs_minus.xml
index 4722c9e..6a3410a 100644
--- a/packages/SystemUI/res/drawable/ic_qs_minus.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_minus.xml
@@ -1,25 +1,26 @@
 <!--
-Copyright (C) 2014 The Android Open Source Project
+     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
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
 
-         http://www.apache.org/licenses/LICENSE-2.0
+          http://www.apache.org/licenses/LICENSE-2.0
 
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
+     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.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
+    android:height="24.0dp"
+    android:viewportHeight="48.0"
+    android:viewportWidth="48.0"
+    android:width="24.0dp" >
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M24.0,4.0C13.0,4.0 4.0,13.0 4.0,24.0s9.0,20.0 20.0,20.0c11.0,0.0 20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0zM34.0,26.0L14.0,26.0l0.0,-4.0l20.0,0.0L34.0,26.0z"/>
-</vector>
+        android:pathData="M38.0,26.0L10.0,26.0l0.0,-4.0l28.0,0.0l0.0,4.0z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_qs_plus.xml b/packages/SystemUI/res/drawable/ic_qs_plus.xml
index 17d74cf..393f51c 100644
--- a/packages/SystemUI/res/drawable/ic_qs_plus.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_plus.xml
@@ -1,25 +1,26 @@
 <!--
-Copyright (C) 2014 The Android Open Source Project
+     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
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
 
-         http://www.apache.org/licenses/LICENSE-2.0
+          http://www.apache.org/licenses/LICENSE-2.0
 
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
+     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.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
+    android:height="24.0dp"
+    android:viewportHeight="48.0"
+    android:viewportWidth="48.0"
+    android:width="24.0dp" >
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M24.0,4.0C13.0,4.0 4.0,13.0 4.0,24.0s9.0,20.0 20.0,20.0c11.0,0.0 20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0zM34.0,26.0l-8.0,0.0l0.0,8.0l-4.0,0.0l0.0,-8.0l-8.0,0.0l0.0,-4.0l8.0,0.0l0.0,-8.0l4.0,0.0l0.0,8.0l8.0,0.0L34.0,26.0z"/>
-</vector>
+        android:pathData="M38.0,26.0L26.0,26.0l0.0,12.0l-4.0,0.0L22.0,26.0L10.0,26.0l0.0,-4.0l12.0,0.0L22.0,10.0l4.0,0.0l0.0,12.0l12.0,0.0l0.0,4.0z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_alarm.xml b/packages/SystemUI/res/drawable/ic_volume_alarm.xml
new file mode 100644
index 0000000..a8ca0d6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_alarm.xml
@@ -0,0 +1,26 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24.0dp"
+    android:viewportHeight="48.0"
+    android:viewportWidth="48.0"
+    android:width="24.0dp" >
+
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M44.0,11.44l-9.19,-7.71 -2.57,3.06 9.19,7.71 2.57,-3.06zm-28.24,-4.66l-2.57,-3.06 -9.19,7.71 2.57,3.06 9.19,-7.71zm9.24,9.22l-3.0,0.0l0.0,12.0l9.49,5.71 1.51,-2.47 -8.0,-4.74l0.0,-10.5zm-1.01,-8.0c-9.95,0.0 -17.99,8.06 -17.99,18.0s8.04,18.0 17.99,18.0 18.01,-8.06 18.01,-18.0 -8.06,-18.0 -18.01,-18.0zm0.01,32.0c-7.73,0.0 -14.0,-6.27 -14.0,-14.0s6.27,-14.0 14.0,-14.0 14.0,6.27 14.0,14.0 -6.26,14.0 -14.0,14.0z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_alarm_mute.xml b/packages/SystemUI/res/drawable/ic_volume_alarm_mute.xml
new file mode 100644
index 0000000..8e2f083
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_alarm_mute.xml
@@ -0,0 +1,26 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24.0dp"
+    android:viewportHeight="48.0"
+    android:viewportWidth="48.0"
+    android:width="24.0dp" >
+
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M24.0,12.0c7.73,0.0 14.0,6.27 14.0,14.0 0.0,1.69 -0.31,3.3 -0.86,4.8l3.04,3.04c1.16,-2.37 1.82,-5.03 1.82,-7.84 0.0,-9.94 -8.06,-18.0 -18.01,-18.0 -2.81,0.0 -5.46,0.66 -7.84,1.81l3.05,3.05c1.5,-0.55 3.11,-0.86 4.8,-0.86zm20.0,-0.56l-9.19,-7.71 -2.57,3.06 9.19,7.71 2.57,-3.06zm-38.16,-6.85l-2.55,2.54 2.66,2.66 -2.22,1.86 2.84,2.84 2.22,-1.86 1.6,1.6c-2.73,3.16 -4.39,7.27 -4.39,11.77 0.0,9.94 8.04,18.0 17.99,18.0 4.51,0.0 8.62,-1.67 11.77,-4.4l4.4,4.4 2.54,-2.55 -34.91,-34.91 -1.95,-1.95zm27.1,32.19c-2.43,2.01 -5.54,3.22 -8.94,3.22 -7.73,0.0 -14.0,-6.27 -14.0,-14.0 0.0,-3.4 1.21,-6.51 3.22,-8.94l19.72,19.72zm-16.91,-30.23l-2.84,-2.84 -1.7,1.43 2.84,2.84 1.7,-1.43z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_bt.xml b/packages/SystemUI/res/drawable/ic_volume_bt.xml
new file mode 100644
index 0000000..bce407a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_bt.xml
@@ -0,0 +1,26 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="48.0"
+    android:viewportWidth="48.0"
+    android:width="24dp" >
+
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M35.4,15.4L24.0,4.0l-2.0,0.0l0.0,15.2L12.8,10.0L10.0,12.8L21.2,24.0L10.0,35.2l2.8,2.8l9.2,-9.2L22.0,44.0l2.0,0.0l11.4,-11.4L26.8,24.0L35.4,15.4zM26.0,11.7l3.8,3.8L26.0,19.2L26.0,11.7zM29.8,32.6L26.0,36.3l0.0,-7.5L29.8,32.6z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_bt_mute.xml b/packages/SystemUI/res/drawable/ic_volume_bt_mute.xml
new file mode 100644
index 0000000..98a8137
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_bt_mute.xml
@@ -0,0 +1,26 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="48.0"
+    android:viewportWidth="48.0"
+    android:width="24dp" >
+
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M26.0,11.8l3.8,3.8l-3.2,3.2l2.8,2.8l6.0,-6.0L24.0,4.2l-2.0,0.0l0.0,10.1l4.0,4.0L26.0,11.8zM10.8,8.2L8.0,11.0l13.2,13.2L10.0,35.3l2.8,2.8L22.0,29.0l0.0,15.2l2.0,0.0l8.6,-8.6l4.6,4.6l2.8,-2.8L10.8,8.2zM26.0,36.5L26.0,29.0l3.8,3.8L26.0,36.5z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_bt_sco.xml b/packages/SystemUI/res/drawable/ic_volume_bt_sco.xml
new file mode 100644
index 0000000..71df4d3
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_bt_sco.xml
@@ -0,0 +1,26 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24.0dp"
+    android:viewportHeight="48.0"
+    android:viewportWidth="48.0"
+    android:width="24.0dp" >
+
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M29.41,19.0L34.0,14.41L34.0,22.0l1.0,0.0l5.71,-5.71 -4.3,-4.29 4.29,-4.29L35.0,2.0l-1.0,0.0l0.0,7.59L29.41,5.0 28.0,6.41 33.59,12.0 28.0,17.59 29.41,19.0zM36.0,5.83l1.88,1.88L36.0,9.59L36.0,5.83zm0.0,8.58l1.88,1.88L36.0,18.17l0.0,-3.76zM40.0,31.0c-2.49,0.0 -4.89,-0.4 -7.14,-1.14 -0.69,-0.22 -1.48,-0.06 -2.0,0.49l-4.4,4.41c-5.67,-2.88 -10.29,-7.51 -13.18,-13.17l4.4,-4.41c0.55,-0.5 0.71,-1.3 0.49,-2.03C17.4,12.9 17.0,10.49 17.0,8.0c0.0,-1.11 -0.89,-2.0 -2.0,-2.0L8.0,6.0c-1.11,0.0 -2.0,0.89 -2.0,2.0 0.0,18.78 15.22,34.0 34.0,34.0 1.11,0.0 2.0,-0.89 2.0,-2.0l0.0,-7.0c0.0,-1.11 -0.89,-2.0 -2.0,-2.0z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_collapse.xml b/packages/SystemUI/res/drawable/ic_volume_collapse.xml
new file mode 100644
index 0000000..dc6d301
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_collapse.xml
@@ -0,0 +1,62 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:name="ic_volume_collapse"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+
+    <group
+        android:name="chevron_02"
+        android:rotation="90"
+        android:translateX="12"
+        android:translateY="9" >
+        <group
+            android:name="rectangle_2"
+            android:rotation="-45" >
+            <group
+                android:name="rectangle_2_pivot"
+                android:translateY="4" >
+                <group
+                    android:name="rectangle_path_2_position"
+                    android:translateY="-1" >
+                    <path
+                        android:name="rectangle_path_2"
+                        android:fillColor="#FFFFFFFF"
+                        android:pathData="M -1.0,-4.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="rectangle_1"
+            android:rotation="45" >
+            <group
+                android:name="rectangle_1_pivot"
+                android:translateY="-4" >
+                <group
+                    android:name="rectangle_path_1_position"
+                    android:translateY="1" >
+                    <path
+                        android:name="rectangle_path_1"
+                        android:fillColor="#FFFFFFFF"
+                        android:pathData="M -1.0,-4.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml b/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml
new file mode 100644
index 0000000..5c482bc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_collapse_animation.xml
@@ -0,0 +1,29 @@
+<!--
+     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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/ic_volume_collapse" >
+
+    <target
+        android:name="chevron_02"
+        android:animation="@anim/ic_volume_collapse_chevron_02_animation" />
+    <target
+        android:name="rectangle_2"
+        android:animation="@anim/ic_volume_collapse_rectangle_2_animation" />
+    <target
+        android:name="rectangle_1"
+        android:animation="@anim/ic_volume_collapse_rectangle_1_animation" />
+
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_expand.xml b/packages/SystemUI/res/drawable/ic_volume_expand.xml
new file mode 100644
index 0000000..a60623f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_expand.xml
@@ -0,0 +1,62 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:name="ic_volume_expand"
+    android:height="24dp"
+    android:viewportHeight="24"
+    android:viewportWidth="24"
+    android:width="24dp" >
+
+    <group
+        android:name="chevron_01"
+        android:rotation="90"
+        android:translateX="12"
+        android:translateY="15" >
+        <group
+            android:name="rectangle_3"
+            android:rotation="45" >
+            <group
+                android:name="rectangle_2_pivot_0"
+                android:translateY="4" >
+                <group
+                    android:name="rectangle_path_3_position"
+                    android:translateY="-1" >
+                    <path
+                        android:name="rectangle_path_3"
+                        android:fillColor="#FFFFFFFF"
+                        android:pathData="M -1.0,-4.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+        <group
+            android:name="rectangle_4"
+            android:rotation="-45" >
+            <group
+                android:name="rectangle_1_pivot_0"
+                android:translateY="-4" >
+                <group
+                    android:name="rectangle_path_4_position"
+                    android:translateY="1" >
+                    <path
+                        android:name="rectangle_path_4"
+                        android:fillColor="#FFFFFFFF"
+                        android:pathData="M -1.0,-4.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
+                </group>
+            </group>
+        </group>
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml b/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml
new file mode 100644
index 0000000..ae2d7e4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_expand_animation.xml
@@ -0,0 +1,29 @@
+<!--
+     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.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/ic_volume_expand" >
+
+    <target
+        android:name="chevron_01"
+        android:animation="@anim/ic_volume_expand_chevron_01_animation" />
+    <target
+        android:name="rectangle_3"
+        android:animation="@anim/ic_volume_expand_rectangle_3_animation" />
+    <target
+        android:name="rectangle_4"
+        android:animation="@anim/ic_volume_expand_rectangle_4_animation" />
+
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_media.xml b/packages/SystemUI/res/drawable/ic_volume_media.xml
new file mode 100644
index 0000000..97089f1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_media.xml
@@ -0,0 +1,26 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24.0dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:width="24.0dp" >
+
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M12.0,3.0l0.0,9.28c-0.47,-0.17 -0.97,-0.28 -1.5,-0.28C8.01,12.0 6.0,14.01 6.0,16.5S8.01,21.0 10.5,21.0c2.31,0.0 4.2,-1.75 4.45,-4.0L15.0,17.0L15.0,6.0l4.0,0.0L19.0,3.0l-7.0,0.0z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_media_mute.xml b/packages/SystemUI/res/drawable/ic_volume_media_mute.xml
new file mode 100644
index 0000000..beb806c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_media_mute.xml
@@ -0,0 +1,29 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24.0dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:width="24.0dp" >
+
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M15.0,6.0l4.0,0.0L19.0,3.0l-7.0,0.0l0.0,5.6l3.0,3.0C15.0,8.8 15.0,6.0 15.0,6.0z" />
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M4.8,3.9L3.5,5.1l6.9,6.9C8.0,12.1 6.0,14.0 6.0,16.5C6.0,19.0 8.0,21.0 10.5,21.0c2.7,0.0 4.5,-2.3 4.5,-4.3c0.0,0.0 0.0,-0.1 0.0,-0.1l4.0,4.0l1.3,-1.3L4.8,3.9z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_remote.xml b/packages/SystemUI/res/drawable/ic_volume_remote.xml
new file mode 100644
index 0000000..b363178
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_remote.xml
@@ -0,0 +1,26 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="48.0"
+    android:viewportWidth="48.0"
+    android:width="24dp" >
+
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M2.0,36.0l0.0,6.0l6.0,0.0C8.0,38.7 5.3,36.0 2.0,36.0zM2.0,28.0l0.0,4.0c5.5,0.0 10.0,4.5 10.0,10.0l4.0,0.0C16.0,34.3 9.7,28.0 2.0,28.0zM38.0,14.0L10.0,14.0l0.0,3.3c7.9,2.6 14.2,8.8 16.7,16.7L38.0,34.0L38.0,14.0zM2.0,20.0l0.0,4.0c9.9,0.0 18.0,8.1 18.0,18.0l4.0,0.0C24.0,29.8 14.1,20.0 2.0,20.0zM42.0,6.0L6.0,6.0c-2.2,0.0 -4.0,1.8 -4.0,4.0l0.0,6.0l4.0,0.0l0.0,-6.0l36.0,0.0l0.0,28.0L28.0,38.0l0.0,4.0l14.0,0.0c2.2,0.0 4.0,-1.8 4.0,-4.0L46.0,10.0C46.0,7.8 44.2,6.0 42.0,6.0z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_remote_mute.xml b/packages/SystemUI/res/drawable/ic_volume_remote_mute.xml
new file mode 100644
index 0000000..5f39ad7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_remote_mute.xml
@@ -0,0 +1,38 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24.0dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:width="24.0dp" >
+
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M23.7,21.3l-1.1,-1.0c0.0,0.0 0.0,0.0 0.0,0.0L21.0,18.8l0.0,0.0L5.8,5.0l0.0,0.0L3.6,3.0l0.0,0.0L1.7,1.3L0.3,2.7l1.1,1.0C1.2,4.1 1.0,4.5 1.0,5.0l0.0,3.0l2.0,0.0L3.0,5.2L18.2,19.0L14.0,19.0l0.0,2.0l6.4,0.0l1.9,1.7L23.7,21.3z" />
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M21.0,5.0l0.0,11.1l2.0,1.8L23.0,5.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0L6.6,3.0l2.2,2.0L21.0,5.0z" />
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M1.0,18.0l0.0,3.0l3.0,0.0C4.0,19.3 2.7,18.0 1.0,18.0z" />
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M1.0,14.0l0.0,2.0c2.8,0.0 5.0,2.2 5.0,5.0l2.0,0.0C8.0,17.1 4.9,14.0 1.0,14.0z" />
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M1.0,10.0l0.0,2.0c5.0,0.0 9.0,4.0 9.0,9.0l2.0,0.0C12.0,14.9 7.1,10.0 1.0,10.0z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_ringer.xml b/packages/SystemUI/res/drawable/ic_volume_ringer.xml
new file mode 100644
index 0000000..c566d5a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_ringer.xml
@@ -0,0 +1,26 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:width="24dp" >
+
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M11.5,22.0c1.1,0.0 2.0,-0.9 2.0,-2.0l-4.0,0.0C9.5,21.1 10.4,22.0 11.5,22.0zM18.0,16.0l0.0,-5.5c0.0,-3.1 -2.1,-5.6 -5.0,-6.3L13.0,3.5C13.0,2.7 12.3,2.0 11.5,2.0C10.7,2.0 10.0,2.7 10.0,3.5l0.0,0.7c-2.9,0.7 -5.0,3.2 -5.0,6.3L5.0,16.0l-2.0,2.0l0.0,1.0l17.0,0.0l0.0,-1.0L18.0,16.0z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_ringer_mute.xml b/packages/SystemUI/res/drawable/ic_volume_ringer_mute.xml
new file mode 100644
index 0000000..0c20361
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_ringer_mute.xml
@@ -0,0 +1,26 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="48.0"
+    android:viewportWidth="48.0"
+    android:width="24dp" >
+
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M23.000000,44.000000c2.200000,0.000000 4.000000,-1.800000 4.000000,-4.000000l-8.000000,0.000000C19.000000,42.200001 20.799999,44.000000 23.000000,44.000000zM36.000000,21.000000c0.000000,-6.100000 -4.300000,-11.300000 -10.000000,-12.600000L26.000000,7.000000c0.000000,-1.700000 -1.300000,-3.000000 -3.000000,-3.000000c-1.700000,0.000000 -3.000000,1.300000 -3.000000,3.000000l0.000000,1.400000c-1.000000,0.200000 -2.000000,0.600000 -2.900000,1.100000L36.000000,28.400000L36.000000,21.000000zM35.500000,38.000000l4.000000,4.000000l2.500000,-2.500000L8.500000,6.000000L6.000000,8.500000l5.800000,5.800000C10.700000,16.299999 10.000000,18.600000 10.000000,21.000000l0.000000,11.000000l-4.000000,4.000000l0.000000,2.000000L35.500000,38.000000z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_ringer_vibrate.xml b/packages/SystemUI/res/drawable/ic_volume_ringer_vibrate.xml
new file mode 100644
index 0000000..38b3234
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_ringer_vibrate.xml
@@ -0,0 +1,26 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:width="24dp" >
+
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M0.0,15.0l2.0,0.0L2.0,9.0L0.0,9.0L0.0,15.0zM3.0,17.0l2.0,0.0L5.0,7.0L3.0,7.0L3.0,17.0zM22.0,9.0l0.0,6.0l2.0,0.0L24.0,9.0L22.0,9.0zM19.0,17.0l2.0,0.0L21.0,7.0l-2.0,0.0L19.0,17.0zM16.5,3.0l-9.0,0.0C6.7,3.0 6.0,3.7 6.0,4.5l0.0,15.0C6.0,20.3 6.7,21.0 7.5,21.0l9.0,0.0c0.8,0.0 1.5,-0.7 1.5,-1.5l0.0,-15.0C18.0,3.7 17.3,3.0 16.5,3.0zM16.0,19.0L8.0,19.0L8.0,5.0l8.0,0.0L16.0,19.0z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_settings.xml b/packages/SystemUI/res/drawable/ic_volume_settings.xml
new file mode 100644
index 0000000..9f79b5a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_settings.xml
@@ -0,0 +1,26 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="20dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:width="20dp" >
+
+    <path
+        android:fillColor="@color/volume_settings_icon_color"
+        android:pathData="M19.4,13.0c0.0,-0.3 0.1,-0.6 0.1,-1.0s0.0,-0.7 -0.1,-1.0l2.1,-1.7c0.2,-0.2 0.2,-0.4 0.1,-0.6l-2.0,-3.5C19.5,5.1 19.3,5.0 19.0,5.1l-2.5,1.0c-0.5,-0.4 -1.1,-0.7 -1.7,-1.0l-0.4,-2.6C14.5,2.2 14.2,2.0 14.0,2.0l-4.0,0.0C9.8,2.0 9.5,2.2 9.5,2.4L9.1,5.1C8.5,5.3 8.0,5.7 7.4,6.1L5.0,5.1C4.7,5.0 4.5,5.1 4.3,5.3l-2.0,3.5C2.2,8.9 2.3,9.2 2.5,9.4L4.6,11.0c0.0,0.3 -0.1,0.6 -0.1,1.0s0.0,0.7 0.1,1.0l-2.1,1.7c-0.2,0.2 -0.2,0.4 -0.1,0.6l2.0,3.5C4.5,18.9 4.7,19.0 5.0,18.9l2.5,-1.0c0.5,0.4 1.1,0.7 1.7,1.0l0.4,2.6c0.0,0.2 0.2,0.4 0.5,0.4l4.0,0.0c0.2,0.0 0.5,-0.2 0.5,-0.4l0.4,-2.6c0.6,-0.3 1.2,-0.6 1.7,-1.0l2.5,1.0c0.2,0.1 0.5,0.0 0.6,-0.2l2.0,-3.5c0.1,-0.2 0.1,-0.5 -0.1,-0.6L19.4,13.0zM12.0,15.5c-1.9,0.0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5s3.5,1.6 3.5,3.5S13.9,15.5 12.0,15.5z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_system.xml b/packages/SystemUI/res/drawable/ic_volume_system.xml
new file mode 100644
index 0000000..ccd8e18
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_system.xml
@@ -0,0 +1,32 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24.0dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:width="24.0dp" >
+
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M17.7,14.8l-4.5,-2.3c-0.2,-0.1 -0.4,-0.1 -0.5,-0.1l-0.8,0.0l0.0,-6.0c0.0,-0.8 -0.7,-1.5 -1.5,-1.5S8.9,5.6 8.9,6.4l0.0,10.7l-3.4,-0.7c-0.1,0.0 -0.2,0.0 -0.2,0.0c-0.3,0.0 -0.6,0.1 -0.8,0.3l-0.8,0.8l4.9,4.9c0.3,0.3 0.6,0.4 1.1,0.4l6.8,0.0c0.8,0.0 1.3,-0.6 1.4,-1.3l0.8,-5.3c0.0,-0.1 0.0,-0.1 0.0,-0.2C18.6,15.5 18.2,15.0 17.7,14.8z" />
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M14.3,8.8l1.8,0.9c1.5,-2.0 1.4,-4.8 -0.4,-6.6l-1.4,1.4C15.5,5.7 15.5,7.6 14.3,8.8z" />
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M17.9,10.6l1.8,0.9C22.0,8.0 21.6,3.3 18.5,0.3l-1.4,1.4C19.5,4.1 19.8,7.9 17.9,10.6z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_system_mute.xml b/packages/SystemUI/res/drawable/ic_volume_system_mute.xml
new file mode 100644
index 0000000..dfcb655
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_system_mute.xml
@@ -0,0 +1,35 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24.0dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:width="24.0dp" >
+
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M14.3,8.8l1.8,0.9c1.5,-2.0 1.4,-4.8 -0.4,-6.6l-1.4,1.4C15.5,5.7 15.5,7.6 14.3,8.8z" />
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M17.9,10.6l1.8,0.9C22.0,8.0 21.6,3.3 18.5,0.3l-1.4,1.4C19.5,4.1 19.8,7.9 17.9,10.6z" />
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M22.0,20.6l-3.5,-2.8l0.0,0.0l-9.6,-7.6l0.0,0.0L3.2,5.7L2.0,7.3l6.9,5.4L8.9,17.0l-3.4,-0.7c-0.1,0.0 -0.2,0.0 -0.2,0.0c-0.3,0.0 -0.6,0.1 -0.8,0.3l-0.8,0.8l4.9,4.9c0.3,0.3 0.6,0.4 1.1,0.4l6.8,0.0c0.8,0.0 1.3,-0.6 1.4,-1.3l0.2,-1.5l2.6,2.1L22.0,20.6z" />
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M11.9,6.4c0.0,-0.8 -0.7,-1.5 -1.5,-1.5S8.9,5.6 8.9,6.4l0.0,1.5l3.0,2.4L11.9,6.4z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_volume_voice.xml b/packages/SystemUI/res/drawable/ic_volume_voice.xml
new file mode 100644
index 0000000..133253e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_volume_voice.xml
@@ -0,0 +1,26 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24.0dp"
+    android:viewportHeight="48.0"
+    android:viewportWidth="48.0"
+    android:width="24.0dp" >
+
+    <path
+        android:fillColor="@color/volume_icon_color"
+        android:pathData="M13.25,21.59c2.88,5.66 7.51,10.29 13.18,13.17l4.4,-4.41c0.55,-0.55 1.34,-0.71 2.03,-0.49C35.1,30.6 37.51,31.0 40.0,31.0c1.11,0.0 2.0,0.89 2.0,2.0l0.0,7.0c0.0,1.11 -0.89,2.0 -2.0,2.0C21.22,42.0 6.0,26.78 6.0,8.0c0.0,-1.1 0.9,-2.0 2.0,-2.0l7.0,0.0c1.11,0.0 2.0,0.89 2.0,2.0 0.0,2.4 0.4,4.9 1.14,7.1 0.2,0.6 0.06,1.48 -0.49,2.03l-4.4,4.42z" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_sys_no_sims.xml b/packages/SystemUI/res/drawable/stat_sys_no_sims.xml
index 8bad226..2229c99 100644
--- a/packages/SystemUI/res/drawable/stat_sys_no_sims.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_no_sims.xml
@@ -20,6 +20,6 @@
         android:viewportHeight="24.0">
 
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="?attr/backgroundColor"
         android:pathData="M19.0,5.0c0.0,-1.1 -0.9,-2.0 -2.0,-2.0l-7.0,0.0L7.7,5.3L19.0,16.7L19.0,5.0zM3.7,3.9L2.4,5.2L5.0,7.8L5.0,19.0c0.0,1.1 0.9,2.0 2.0,2.0l10.0,0.0c0.4,0.0 0.7,-0.1 1.0,-0.3l1.9,1.9l1.3,-1.3L3.7,3.9z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_0.xml b/packages/SystemUI/res/drawable/stat_sys_signal_0.xml
index e1e81fd..643c4f9 100644
--- a/packages/SystemUI/res/drawable/stat_sys_signal_0.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_0.xml
@@ -20,12 +20,12 @@
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/singleToneColor"
         android:pathData="M19.700001,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/singleToneColor"
         android:pathData="M19.700001,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
     <path
         android:pathData="M17.700001,8.000000l4.299999,0.000000 0.000000,-6.000000 -20.000000,20.000000 15.700001,0.000000z"
-        android:fillColor="#4DFFFFFF"/>
+        android:fillColor="?attr/backgroundColor"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_0_fully.xml b/packages/SystemUI/res/drawable/stat_sys_signal_0_fully.xml
index c0dfcf4..e267d25 100644
--- a/packages/SystemUI/res/drawable/stat_sys_signal_0_fully.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_0_fully.xml
@@ -20,6 +20,6 @@
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="?attr/backgroundColor"
         android:pathData="M2.000000,22.000000l20.000000,0.000000 0.000000,-20.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_1.xml b/packages/SystemUI/res/drawable/stat_sys_signal_1.xml
index d1124ee..64781c3 100644
--- a/packages/SystemUI/res/drawable/stat_sys_signal_1.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_1.xml
@@ -20,15 +20,15 @@
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/singleToneColor"
         android:pathData="M19.7,20.0l2.0,0.0l0.0,2.0l-2.0,0.0z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/singleToneColor"
         android:pathData="M19.7,10.0l2.0,0.0l0.0,8.1l-2.0,0.0z"/>
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="?attr/backgroundColor"
         android:pathData="M17.7,8.0l4.299999,0.0 0.0,-6.0 -20.0,20.0 15.700001,0.0z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M10.1,13.9l-8.1,8.1 8.1,0.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_1_fully.xml b/packages/SystemUI/res/drawable/stat_sys_signal_1_fully.xml
index 29eda94..60822f4 100644
--- a/packages/SystemUI/res/drawable/stat_sys_signal_1_fully.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_1_fully.xml
@@ -20,9 +20,9 @@
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="?attr/backgroundColor"
         android:pathData="M2.0,22.0l20.0,0.0 0.0,-20.0z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M10.1,13.9l-8.1,8.1 8.1,0.0z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_2.xml b/packages/SystemUI/res/drawable/stat_sys_signal_2.xml
index 537c788..eb2be08 100644
--- a/packages/SystemUI/res/drawable/stat_sys_signal_2.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_2.xml
@@ -20,15 +20,15 @@
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/singleToneColor"
         android:pathData="M19.700001,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/singleToneColor"
         android:pathData="M19.700001,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M13.900000,10.000000l-11.900000,12.000000 11.900000,0.000000z"/>
     <path
         android:pathData="M17.700001,8.000000l4.299999,0.000000 0.000000,-6.000000 -20.000000,20.000000 15.700001,0.000000z"
-        android:fillColor="#4DFFFFFF"/>
+        android:fillColor="?attr/backgroundColor"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_2_fully.xml b/packages/SystemUI/res/drawable/stat_sys_signal_2_fully.xml
index 7d9376e..5e68eed 100644
--- a/packages/SystemUI/res/drawable/stat_sys_signal_2_fully.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_2_fully.xml
@@ -20,9 +20,9 @@
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="?attr/backgroundColor"
         android:pathData="M2.000000,22.000000l20.000000,0.000000 0.000000,-20.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M14.000000,10.000000l-12.000000,12.000000 12.000000,0.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_3.xml b/packages/SystemUI/res/drawable/stat_sys_signal_3.xml
index 09fe33a..22afad0 100644
--- a/packages/SystemUI/res/drawable/stat_sys_signal_3.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_3.xml
@@ -20,15 +20,15 @@
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/singleToneColor"
         android:pathData="M19.700001,19.900000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/singleToneColor"
         android:pathData="M19.700001,9.900000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M16.700001,7.200000l-14.700001,14.700000 14.700001,0.000000z"/>
     <path
         android:pathData="M17.700001,7.900000l4.299999,0.000000 0.000000,-6.000000 -20.000000,20.000000 15.700001,0.000000z"
-        android:fillColor="#4DFFFFFF"/>
+        android:fillColor="?attr/backgroundColor"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_3_fully.xml b/packages/SystemUI/res/drawable/stat_sys_signal_3_fully.xml
index 8ec5500..599b34a 100644
--- a/packages/SystemUI/res/drawable/stat_sys_signal_3_fully.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_3_fully.xml
@@ -20,9 +20,9 @@
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="?attr/backgroundColor"
         android:pathData="M2.000000,22.000000l20.000000,0.000000 0.000000,-20.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M16.700001,7.300000l-14.700001,14.700000 14.700001,0.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_4.xml b/packages/SystemUI/res/drawable/stat_sys_signal_4.xml
index bb98541..d1e866d 100644
--- a/packages/SystemUI/res/drawable/stat_sys_signal_4.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_4.xml
@@ -19,13 +19,14 @@
         android:height="17dp"
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
+
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/singleToneColor"
         android:pathData="M19.700001,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/singleToneColor"
         android:pathData="M19.700001,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/singleToneColor"
         android:pathData="M2.000000,22.000000l15.700001,0.000000 0.000000,-14.000000 4.299999,0.000000 0.000000,-6.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_4_fully.xml b/packages/SystemUI/res/drawable/stat_sys_signal_4_fully.xml
index a468410..b66d89a 100644
--- a/packages/SystemUI/res/drawable/stat_sys_signal_4_fully.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_4_fully.xml
@@ -20,6 +20,6 @@
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/singleToneColor"
         android:pathData="M2.000000,22.000000l20.000000,0.000000 0.000000,-20.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_null.xml b/packages/SystemUI/res/drawable/stat_sys_signal_null.xml
index d25fc1c..2b487f9e 100644
--- a/packages/SystemUI/res/drawable/stat_sys_signal_null.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_null.xml
@@ -20,6 +20,6 @@
         android:viewportWidth="24.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="?attr/backgroundColor"
         android:pathData="M2.000000,22.000000l20.000000,0.000000L22.000000,2.000000L2.000000,22.000000zM20.000000,20.000000L6.800000,20.000000L20.000000,6.800000L20.000000,20.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_0.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_0.xml
index be930e8..7f1b715e 100644
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_0.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_0.xml
@@ -19,12 +19,12 @@
         android:viewportWidth="26.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="?attr/backgroundColor"
         android:pathData="M19.000000,8.000000l5.300000,0.000000l1.200000,-1.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.400000,6.500000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,8.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M21.000000,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M21.000000,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_0_fully.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_0_fully.xml
index b2691f6..60f7eb6 100644
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_0_fully.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_0_fully.xml
@@ -19,6 +19,6 @@
         android:viewportWidth="26.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="?attr/backgroundColor"
         android:pathData="M13.000000,22.000000L25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.400000,6.500000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_1.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_1.xml
index dde781d..acd89be 100644
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_1.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_1.xml
@@ -19,15 +19,15 @@
         android:viewportWidth="26.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="?attr/backgroundColor"
         android:pathData="M19.000000,8.000000l5.300000,0.000000l1.300000,-1.600000C25.100000,6.000000 20.299999,2.000000 13.000000,2.000000S0.900000,6.000000 0.400000,6.400000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,8.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M13.000000,22.000000l5.500000,-6.800000c-0.200000,-0.200000 -2.300000,-1.900000 -5.500000,-1.900000s-5.300000,1.800000 -5.500000,1.900000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M21.000000,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M21.000000,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_1_fully.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_1_fully.xml
index 65931f4..554350d 100644
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_1_fully.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_1_fully.xml
@@ -19,9 +19,9 @@
         android:viewportWidth="26.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="?attr/backgroundColor"
         android:pathData="M13.100000,22.000000L25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.500000,6.500000L13.100000,22.000000L13.100000,22.000000L13.100000,22.000000L13.100000,22.000000L13.100000,22.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M13.100000,22.000000l5.500000,-6.800000c-0.200000,-0.200000 -2.300000,-1.900000 -5.500000,-1.900000s-5.300000,1.800000 -5.500000,1.900000L13.100000,22.000000L13.100000,22.000000L13.100000,22.000000L13.100000,22.000000L13.100000,22.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_2.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_2.xml
index 63d9dca..f33b25c 100644
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_2.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_2.xml
@@ -19,15 +19,15 @@
         android:viewportWidth="26.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="?attr/backgroundColor"
         android:pathData="M19.000000,8.000000l5.300000,0.000000l1.300000,-1.600000C25.100000,6.000000 20.299999,2.000000 13.000000,2.000000S0.900000,6.000000 0.400000,6.400000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,8.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M19.000000,11.600000c-1.300000,-0.700000 -3.400000,-1.600000 -6.000000,-1.600000c-4.400000,0.000000 -7.300000,2.400000 -7.600000,2.700000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,11.600000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M21.000000,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M21.000000,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_2_fully.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_2_fully.xml
index 7ba4895..2c2465a 100644
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_2_fully.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_2_fully.xml
@@ -19,9 +19,9 @@
         android:viewportWidth="26.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="?attr/backgroundColor"
         android:pathData="M13.000000,22.000000L25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.400000,6.500000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M13.000000,22.000000l7.600000,-9.400000C20.299999,12.400000 17.400000,10.000000 13.000000,10.000000s-7.300000,2.400000 -7.600000,2.700000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_3.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_3.xml
index 7d393b4..09d2e50 100644
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_3.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_3.xml
@@ -19,15 +19,15 @@
         android:viewportWidth="26.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="?attr/backgroundColor"
         android:pathData="M19.000000,8.000000l5.300000,0.000000l1.300000,-1.600000C25.100000,6.000000 20.299999,2.000000 13.000000,2.000000S0.900000,6.000000 0.400000,6.400000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,8.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M19.000000,8.600000c-1.600000,-0.700000 -3.600000,-1.300000 -6.000000,-1.300000c-5.300000,0.000000 -8.900000,3.000000 -9.200000,3.200000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,8.600000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M21.000000,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M21.000000,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_3_fully.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_3_fully.xml
index e2cde53..7d0f756 100644
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_3_fully.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_3_fully.xml
@@ -19,9 +19,9 @@
         android:viewportWidth="26.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="?attr/backgroundColor"
         android:pathData="M13.000000,22.000000L25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.400000,6.500000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/fillColor"
         android:pathData="M13.000000,22.000000l9.200000,-11.400000c-0.400000,-0.300000 -3.900000,-3.200000 -9.200000,-3.200000s-8.900000,3.000000 -9.200000,3.200000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_4.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_4.xml
index ec8137f..fb1f584 100644
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_4.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_4.xml
@@ -19,12 +19,12 @@
         android:viewportWidth="26.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/singleToneColor"
         android:pathData="M19.000000,8.000000l5.300000,0.000000l1.300000,-1.600000C25.100000,6.000000 20.299999,2.000000 13.000000,2.000000S0.900000,6.000000 0.400000,6.400000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l6.000000,-7.400000L19.000000,8.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/singleToneColor"
         android:pathData="M21.000000,20.000000l2.000000,0.000000l0.000000,2.000000l-2.000000,0.000000z"/>
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/singleToneColor"
         android:pathData="M21.000000,10.000000l2.000000,0.000000l0.000000,8.100000l-2.000000,0.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_4_fully.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_4_fully.xml
index 9d02980..a7e213f 100644
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_4_fully.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_4_fully.xml
@@ -19,6 +19,6 @@
         android:viewportWidth="26.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#FFFFFFFF"
+        android:fillColor="?attr/singleToneColor"
         android:pathData="M13.000000,22.000000L25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.400000,6.500000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_null.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_null.xml
index 95c6531..5169de4 100644
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_null.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_null.xml
@@ -19,6 +19,6 @@
         android:viewportWidth="26.0"
         android:viewportHeight="24.0">
     <path
-        android:fillColor="#4DFFFFFF"
+        android:fillColor="?attr/backgroundColor"
         android:pathData="M13.000000,2.000000C7.700000,2.000000 3.700000,3.900000 0.400000,6.400000L13.000000,22.000000L25.600000,6.500000C22.299999,4.000000 18.299999,2.000000 13.000000,2.000000zM13.000000,18.600000L3.300000,7.000000l0.000000,0.000000l0.000000,0.000000C6.000000,5.300000 8.700000,4.000000 13.000000,4.000000s7.000000,1.400000 9.700000,3.000000l0.000000,0.000000l0.000000,0.000000L13.000000,18.600000z"/>
 </vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_bottom.xml b/packages/SystemUI/res/drawable/vector_drawable_place_bottom.xml
new file mode 100644
index 0000000..14f1981
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_bottom.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0zM4.0,10.0l16.0,0.0l0.0,10.0L4.0,20.0L4.0,10.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_bottom_left.xml b/packages/SystemUI/res/drawable/vector_drawable_place_bottom_left.xml
new file mode 100644
index 0000000..cea6324
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_bottom_left.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M24.0,24.0L24.0,0.0L0.0,0.0l0.0,24.0L24.0,24.0zM4.0,10.0l10.0,0.0l0.0,10.0L4.0,20.0L4.0,10.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_bottom_right.xml b/packages/SystemUI/res/drawable/vector_drawable_place_bottom_right.xml
new file mode 100644
index 0000000..c2ae9c8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_bottom_right.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M24.0,24.0L24.0,0.0L0.0,0.0l0.0,24.0L24.0,24.0zM20.0,20.0L10.0,20.0L10.0,10.0l10.0,0.0L20.0,20.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_fullscreen.xml b/packages/SystemUI/res/drawable/vector_drawable_place_fullscreen.xml
new file mode 100644
index 0000000..aee0b7f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_fullscreen.xml
@@ -0,0 +1,33 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M0.0,8.0l4.0,0.0 0.0,-4.0 4.0,0.0 0.0,-4.0 -8.0,0.0z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M4.0,16.0l-4.0,0.0 0.0,8.0 8.0,0.0 0.0,-4.0 -4.0,0.0z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.0,0.0l0.0,4.0 4.0,0.0 0.0,4.0 4.0,0.0 0.0,-8.0z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20.0,20.0l-4.0,0.0 0.0,4.0 8.0,0.0 0.0,-8.0 -4.0,0.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_left.xml b/packages/SystemUI/res/drawable/vector_drawable_place_left.xml
new file mode 100644
index 0000000..078f83c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_left.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M24.0,0.0L0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0zM14.0,4.0l0.0,16.0L4.0,20.0L4.0,4.0L14.0,4.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_right.xml b/packages/SystemUI/res/drawable/vector_drawable_place_right.xml
new file mode 100644
index 0000000..86730db
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_right.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0L0.0,24.0zM10.0,20.0L10.0,4.0l10.0,0.0l0.0,16.0L10.0,20.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_top.xml b/packages/SystemUI/res/drawable/vector_drawable_place_top.xml
new file mode 100644
index 0000000..92e01af
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_top.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M24.0,24.0L24.0,0.0L0.0,0.0l0.0,24.0L24.0,24.0zM20.0,14.0L4.0,14.0L4.0,4.0l16.0,0.0L20.0,14.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_top_left.xml b/packages/SystemUI/res/drawable/vector_drawable_place_top_left.xml
new file mode 100644
index 0000000..feb612c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_top_left.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0zM4.0,4.0l10.0,0.0l0.0,10.0L4.0,14.0L4.0,4.0z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_place_top_right.xml b/packages/SystemUI/res/drawable/vector_drawable_place_top_right.xml
new file mode 100644
index 0000000..9f4ee49
--- /dev/null
+++ b/packages/SystemUI/res/drawable/vector_drawable_place_top_right.xml
@@ -0,0 +1,24 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24.0dp"
+        android:height="24.0dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M0.0,0.0l0.0,24.0l24.0,0.0L24.0,0.0L0.0,0.0zM20.0,14.0L10.0,14.0L10.0,4.0l10.0,0.0L20.0,14.0z"/>
+</vector>
diff --git a/core/res/res/drawable/time_picker_header_material.xml b/packages/SystemUI/res/drawable/volume_dialog_background.xml
similarity index 66%
copy from core/res/res/drawable/time_picker_header_material.xml
copy to packages/SystemUI/res/drawable/volume_dialog_background.xml
index ef2068a..f09c01b 100644
--- a/core/res/res/drawable/time_picker_header_material.xml
+++ b/packages/SystemUI/res/drawable/volume_dialog_background.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+     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.
@@ -13,8 +13,10 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" >
 
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-        android:color="?attr/colorControlHighlight">
-    <item android:drawable="?attr/colorAccent" />
-</ripple>
+    <solid android:color="@color/system_primary_color" />
+
+    <corners android:radius="@dimen/notification_material_rounded_rect_radius" />
+
+</shape>
\ No newline at end of file
diff --git a/core/res/res/drawable/time_picker_header_material.xml b/packages/SystemUI/res/interpolator/ic_volume_collapse_animation_interpolator_0.xml
similarity index 67%
copy from core/res/res/drawable/time_picker_header_material.xml
copy to packages/SystemUI/res/interpolator/ic_volume_collapse_animation_interpolator_0.xml
index ef2068a..c3930e4 100644
--- a/core/res/res/drawable/time_picker_header_material.xml
+++ b/packages/SystemUI/res/interpolator/ic_volume_collapse_animation_interpolator_0.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+     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.
@@ -13,8 +13,5 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-        android:color="?attr/colorControlHighlight">
-    <item android:drawable="?attr/colorAccent" />
-</ripple>
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 c 0.0001,0.0 0.0,1.0 1.0,1.0" />
diff --git a/core/res/res/drawable/time_picker_header_material.xml b/packages/SystemUI/res/interpolator/ic_volume_expand_animation_interpolator_0.xml
similarity index 67%
copy from core/res/res/drawable/time_picker_header_material.xml
copy to packages/SystemUI/res/interpolator/ic_volume_expand_animation_interpolator_0.xml
index ef2068a..c3930e4 100644
--- a/core/res/res/drawable/time_picker_header_material.xml
+++ b/packages/SystemUI/res/interpolator/ic_volume_expand_animation_interpolator_0.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+     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.
@@ -13,8 +13,5 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-        android:color="?attr/colorControlHighlight">
-    <item android:drawable="?attr/colorAccent" />
-</ripple>
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 c 0.0001,0.0 0.0,1.0 1.0,1.0" />
diff --git a/packages/SystemUI/res/layout-land/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-land/recents_task_resize_dialog.xml
new file mode 100644
index 0000000..a718d4d
--- /dev/null
+++ b/packages/SystemUI/res/layout-land/recents_task_resize_dialog.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:padding="16dp"
+    android:orientation="vertical"
+    android:descendantFocusability="beforeDescendants"
+    android:focusableInTouchMode="true">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <Button
+            android:id="@+id/place_left"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_left" />
+        <Button
+            android:id="@+id/place_right"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_right" />
+        <Button
+            android:id="@+id/place_full"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_fullscreen" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-port/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-port/recents_task_resize_dialog.xml
new file mode 100644
index 0000000..250f53d
--- /dev/null
+++ b/packages/SystemUI/res/layout-port/recents_task_resize_dialog.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:padding="16dp"
+    android:orientation="vertical"
+    android:descendantFocusability="beforeDescendants"
+    android:focusableInTouchMode="true">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <Button
+            android:id="@+id/place_top"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_top" />
+        <Button
+            android:id="@+id/place_bottom"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_bottom" />
+        <Button
+            android:id="@+id/place_full"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_fullscreen" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
new file mode 100644
index 0000000..26c9b1a
--- /dev/null
+++ b/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:padding="16dp"
+    android:orientation="vertical"
+    android:descendantFocusability="beforeDescendants"
+    android:focusableInTouchMode="true">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <Button
+            android:id="@+id/place_left"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_left" />
+        <Button
+            android:id="@+id/place_right"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_right" />
+        <Button
+            android:id="@+id/place_top_left"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_top_left" />
+        <Button
+            android:id="@+id/place_top_right"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_top_right" />
+        <Button
+            android:id="@+id/place_bottom_left"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_bottom_left" />
+        <Button
+            android:id="@+id/place_bottom_right"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_bottom_right" />
+        <Button
+            android:id="@+id/place_full"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_fullscreen" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
new file mode 100644
index 0000000..e180daa
--- /dev/null
+++ b/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:padding="16dp"
+    android:orientation="vertical"
+    android:descendantFocusability="beforeDescendants"
+    android:focusableInTouchMode="true">
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <Button
+            android:id="@+id/place_top"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_top" />
+        <Button
+            android:id="@+id/place_bottom"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_bottom" />
+        <Button
+            android:id="@+id/place_top_left"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_top_left" />
+        <Button
+            android:id="@+id/place_top_right"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_top_right" />
+        <Button
+            android:id="@+id/place_bottom_left"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_bottom_left" />
+        <Button
+            android:id="@+id/place_bottom_right"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_bottom_right" />
+        <Button
+            android:id="@+id/place_full"
+            android:layout_width="36dp"
+            android:layout_height="36dp"
+            android:layout_weight="1"
+            android:layout_margin="10dp"
+            android:background="@drawable/vector_drawable_place_fullscreen" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml
index 97697189..6a4ac2c 100644
--- a/packages/SystemUI/res/layout/mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/mobile_signal_group.xml
@@ -23,11 +23,19 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     >
-    <ImageView
+    <com.android.systemui.statusbar.AlphaOptimizedImageView
+        android:theme="@style/DualToneLightTheme"
         android:id="@+id/mobile_signal"
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
         />
+    <com.android.systemui.statusbar.AlphaOptimizedImageView
+        android:theme="@style/DualToneDarkTheme"
+        android:id="@+id/mobile_signal_dark"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:alpha="0.0"
+        />
     <ImageView
         android:id="@+id/mobile_type"
         android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout/recents_multistack_stack_size_dialog.xml b/packages/SystemUI/res/layout/recents_multistack_stack_size_dialog.xml
deleted file mode 100644
index 36e54a0..0000000
--- a/packages/SystemUI/res/layout/recents_multistack_stack_size_dialog.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:padding="16dp"
-    android:orientation="vertical"
-    android:descendantFocusability="beforeDescendants"
-    android:focusableInTouchMode="true">
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal">
-        <EditText
-            android:id="@+id/inset_left"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:hint="Left"
-            android:singleLine="true"
-            android:imeOptions="actionNext"
-            android:inputType="number"
-            android:selectAllOnFocus="true" />
-        <EditText
-            android:id="@+id/inset_top"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:hint="Top"
-            android:singleLine="true"
-            android:imeOptions="actionNext"
-            android:inputType="number"
-            android:selectAllOnFocus="true" />
-        <EditText
-            android:id="@+id/inset_right"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:hint="Right"
-            android:singleLine="true"
-            android:imeOptions="actionNext"
-            android:inputType="number"
-            android:selectAllOnFocus="true" />
-        <EditText
-            android:id="@+id/inset_bottom"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_weight="1"
-            android:hint="Bottom"
-            android:singleLine="true"
-            android:imeOptions="actionDone"
-            android:inputType="number"
-            android:selectAllOnFocus="true" />
-    </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/segmented_button.xml b/packages/SystemUI/res/layout/segmented_button.xml
index e92f310..2f84bab7b 100644
--- a/packages/SystemUI/res/layout/segmented_button.xml
+++ b/packages/SystemUI/res/layout/segmented_button.xml
@@ -19,10 +19,8 @@
     android:layout_height="wrap_content"
     android:layout_marginStart="@dimen/segmented_button_spacing"
     android:layout_weight="1"
-    android:gravity="center_horizontal|top"
+    android:gravity="center"
     android:textColor="@color/segmented_button_text_selector"
     android:background="@drawable/btn_borderless_rect"
-    android:textAppearance="@style/TextAppearance.QS.SegmentedButton"
-    android:minHeight="64dp"
-    android:paddingTop="11dp"
-    android:drawablePadding="6dp" />
+    android:textAppearance="@style/TextAppearance.Volume.ZenSwitchSummary"
+    android:minHeight="48dp" />
diff --git a/packages/SystemUI/res/layout/signal_cluster_view.xml b/packages/SystemUI/res/layout/signal_cluster_view.xml
index 8fbd8f7..c9edef8 100644
--- a/packages/SystemUI/res/layout/signal_cluster_view.xml
+++ b/packages/SystemUI/res/layout/signal_cluster_view.xml
@@ -38,11 +38,19 @@
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
         >
-        <ImageView
+        <com.android.systemui.statusbar.AlphaOptimizedImageView
+            android:theme="@style/DualToneLightTheme"
             android:id="@+id/wifi_signal"
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
             />
+        <com.android.systemui.statusbar.AlphaOptimizedImageView
+            android:theme="@style/DualToneDarkTheme"
+            android:id="@+id/wifi_signal_dark"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:alpha="0.0"
+            />
     </FrameLayout>
     <View
         android:id="@+id/wifi_signal_spacer"
@@ -56,12 +64,25 @@
         android:layout_width="wrap_content"
         >
     </LinearLayout>
-    <ImageView
-        android:id="@+id/no_sims"
+    <FrameLayout
         android:layout_height="wrap_content"
-        android:layout_width="wrap_content"
-        android:src="@drawable/stat_sys_no_sims"
-        />
+        android:layout_width="wrap_content">
+        <com.android.systemui.statusbar.AlphaOptimizedImageView
+            android:theme="@style/DualToneLightTheme"
+            android:id="@+id/no_sims"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:src="@drawable/stat_sys_no_sims"
+            />
+        <com.android.systemui.statusbar.AlphaOptimizedImageView
+            android:theme="@style/DualToneDarkTheme"
+            android:id="@+id/no_sims_dark"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:src="@drawable/stat_sys_no_sims"
+            android:alpha="0.0"
+            />
+    </FrameLayout>
     <View
         android:id="@+id/wifi_airplane_spacer"
         android:layout_width="4dp"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 3765fe8..7586227 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2014 The Android Open Source Project
+     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.
@@ -14,15 +13,50 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/volume_dialog"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:layout_marginBottom="4dp"
     android:layout_marginLeft="@dimen/notification_side_padding"
     android:layout_marginRight="@dimen/notification_side_padding"
-    android:background="@drawable/qs_background_primary"
-    android:translationZ="@dimen/volume_panel_z"
-    android:layout_marginBottom="@dimen/volume_panel_z">
+    android:layout_marginTop="4dp"
+    android:background="@drawable/volume_dialog_background"
+    android:translationZ="4dp" >
 
-        <include layout="@layout/volume_panel" />
+    <com.android.keyguard.AlphaOptimizedImageButton
+        android:id="@+id/volume_expand_button"
+        style="@style/VolumeButtons"
+        android:layout_alignParentLeft="true"
+        android:layout_width="@dimen/volume_button_size"
+        android:layout_height="@dimen/volume_button_size"
+        android:clickable="true"
+        android:soundEffectsEnabled="false"
+        android:src="@drawable/ic_volume_collapse_animation" />
 
-</FrameLayout>
\ No newline at end of file
+    <LinearLayout
+        android:id="@+id/volume_dialog_content"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:paddingBottom="4dp"
+        android:paddingEnd="4dp"
+        android:paddingStart="4dp"
+        android:paddingTop="6dp" >
+
+        <!-- volume rows added and removed here! :-) -->
+
+        <FrameLayout
+            android:id="@+id/volume_footer"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            tools:ignore="UselessParent" >
+
+            <include layout="@layout/volume_text_footer" />
+
+            <include layout="@layout/volume_zen_footer" />
+        </FrameLayout>
+    </LinearLayout>
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
new file mode 100644
index 0000000..af27cc4
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -0,0 +1,64 @@
+<!--
+     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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:clipChildren="false" >
+
+    <TextView
+        android:id="@+id/volume_row_header"
+        style="?android:attr/textAppearanceButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:alpha="@dimen/volume_secondary_alpha"
+        android:ellipsize="end"
+        android:maxLines="1"
+        android:paddingBottom="0dp"
+        android:paddingEnd="12dp"
+        android:paddingStart="13dp"
+        android:paddingTop="8dp" />
+
+    <com.android.keyguard.AlphaOptimizedImageButton
+        android:id="@+id/volume_row_icon"
+        style="@style/VolumeButtons"
+        android:layout_width="@dimen/volume_button_size"
+        android:layout_height="@dimen/volume_button_size"
+        android:layout_below="@id/volume_row_header"
+        android:soundEffectsEnabled="false" />
+
+    <SeekBar
+        android:id="@+id/volume_row_slider"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignBottom="@+id/volume_row_icon"
+        android:layout_alignWithParentIfMissing="true"
+        android:layout_below="@id/volume_row_header"
+        android:layout_toEndOf="@id/volume_row_icon"
+        android:layout_toStartOf="@+id/volume_settings_button"
+        android:paddingEnd="4dp"
+        android:paddingStart="4dp"
+        android:progressTint="@android:color/white"
+        android:thumbTint="@android:color/white" />
+
+    <com.android.keyguard.AlphaOptimizedImageButton
+        android:id="@+id/volume_settings_button"
+        style="@style/VolumeButtons"
+        android:layout_width="@dimen/volume_button_size"
+        android:layout_height="@dimen/volume_button_size"
+        android:layout_alignParentEnd="true"
+        android:layout_below="@id/volume_row_header" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_panel_dialog.xml b/packages/SystemUI/res/layout/volume_panel_dialog.xml
new file mode 100644
index 0000000..700102f
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_panel_dialog.xml
@@ -0,0 +1,27 @@
+<!--
+     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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_marginBottom="@dimen/volume_panel_z"
+    android:layout_marginLeft="@dimen/notification_side_padding"
+    android:layout_marginRight="@dimen/notification_side_padding"
+    android:background="@drawable/qs_background_primary"
+    android:translationZ="@dimen/volume_panel_z" >
+
+    <include layout="@layout/volume_panel" />
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_text_footer.xml b/packages/SystemUI/res/layout/volume_text_footer.xml
new file mode 100644
index 0000000..7436488
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_text_footer.xml
@@ -0,0 +1,54 @@
+<!--
+     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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/volume_text_footer"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:visibility="gone"
+    tools:ignore="UselessParent" >
+
+    <TextView
+        android:id="@+id/volume_footline_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignBaseline="@+id/volume_footline_action_button"
+        android:alpha="@dimen/volume_secondary_alpha"
+        android:fontFamily="sans-serif"
+        android:paddingEnd="8dp"
+        android:paddingStart="13dp"
+        android:textColor="?android:attr/textColorPrimary" />
+
+    <Button
+        android:id="@+id/volume_footline_action_button"
+        style="@android:style/Widget.Material.Button.Borderless"
+        android:layout_width="wrap_content"
+        android:layout_height="@dimen/volume_button_size"
+        android:layout_toEndOf="@id/volume_footline_text"
+        android:layout_toStartOf="@+id/volume_settings_button"
+        android:alpha="@dimen/volume_secondary_alpha"
+        android:paddingEnd="0dp"
+        android:paddingStart="0dp" />
+
+    <com.android.keyguard.AlphaOptimizedImageButton
+        android:id="@+id/volume_settings_button"
+        style="@style/VolumeButtons"
+        android:layout_width="@dimen/volume_button_size"
+        android:layout_height="@dimen/volume_button_size"
+        android:layout_alignParentEnd="true"
+        android:src="@drawable/ic_volume_settings" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_zen_footer.xml b/packages/SystemUI/res/layout/volume_zen_footer.xml
new file mode 100644
index 0000000..f4a8b89
--- /dev/null
+++ b/packages/SystemUI/res/layout/volume_zen_footer.xml
@@ -0,0 +1,108 @@
+<!--
+     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.
+-->
+<com.android.systemui.volume.ZenFooter xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/volume_zen_footer"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical" > <!-- extends LinearLayout -->
+
+    <LinearLayout
+        android:id="@+id/volume_zen_switch_bar"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/volume_button_size"
+        android:clickable="true"
+        android:orientation="horizontal" >
+
+        <ImageView
+            android:id="@+id/volume_zen_switch_bar_icon"
+            android:layout_width="@dimen/volume_button_size"
+            android:layout_height="@dimen/volume_button_size"
+            android:scaleType="center"
+            android:src="@drawable/ic_dnd" />
+
+        <TextView
+            android:layout_width="0dp"
+            android:layout_height="fill_parent"
+            android:layout_weight="1"
+            android:gravity="center_vertical"
+            android:textDirection="locale"
+            android:padding="3dp"
+            android:text="@string/volume_zen_switch_text"
+            android:textAppearance="@style/TextAppearance.Volume.ZenSwitch" />
+
+        <Switch
+            android:id="@+id/volume_zen_switch"
+            android:layout_width="wrap_content"
+            android:layout_height="fill_parent"
+            android:layout_marginEnd="11dp" />
+    </LinearLayout>
+
+    <RelativeLayout
+        android:id="@+id/volume_zen_panel_summary"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/volume_button_size"
+        android:layout_marginStart="@dimen/volume_button_size"
+        android:paddingEnd="3dp"
+        android:paddingStart="3dp" >
+
+        <TextView
+            android:id="@+id/volume_zen_panel_summary_line_1"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="@style/TextAppearance.Volume.ZenSwitchSummary" />
+
+        <TextView
+            android:id="@+id/volume_zen_panel_summary_line_2"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/volume_zen_panel_summary_line_1"
+            android:textAppearance="@style/TextAppearance.Volume.ZenSwitchDetail" />
+    </RelativeLayout>
+
+    <include layout="@layout/zen_mode_panel" />
+
+    <LinearLayout
+        android:id="@+id/volume_zen_mode_panel_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="end" >
+
+        <TextView
+            android:id="@+id/volume_zen_mode_panel_more"
+            style="@style/QSBorderlessButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginEnd="8dp"
+            android:clickable="true"
+            android:focusable="true"
+            android:minWidth="132dp"
+            android:text="@string/quick_settings_more_settings"
+            android:textAppearance="@style/TextAppearance.QS.DetailButton" />
+
+        <TextView
+            android:id="@+id/volume_zen_mode_panel_done"
+            style="@style/QSBorderlessButton"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:clickable="true"
+            android:focusable="true"
+            android:minWidth="84dp"
+            android:text="@string/quick_settings_done"
+            android:textAppearance="@style/TextAppearance.QS.DetailButton" />
+    </LinearLayout>
+
+</com.android.systemui.volume.ZenFooter>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index 33c1899..ef403e4 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -34,8 +34,8 @@
             android:id="@+id/zen_buttons"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginLeft="8dp"
-            android:layout_marginRight="8dp"
+            android:layout_marginLeft="40dp"
+            android:layout_marginRight="40dp"
             android:layout_marginBottom="8dp"
             android:clipChildren="false" />
     </FrameLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index d234701..a74e120 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"ontsluit"</string>
     <string name="phone_label" msgid="2320074140205331708">"maak foon oop"</string>
     <string name="camera_label" msgid="7261107956054836961">"maak kamera oop"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Kies nuwe taakuitleg"</string>
+    <string name="cancel" msgid="6442560571259935130">"Kanselleer"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Versoenbaarheid-zoem se knoppie."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoem kleiner na groter skerm."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth gekoppel."</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 827a8a3..0c9aa53 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"ክፈት"</string>
     <string name="phone_label" msgid="2320074140205331708">"ስልክ ክፈት"</string>
     <string name="camera_label" msgid="7261107956054836961">"ካሜራ ክፈት"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"የአዲስ ተግባር አቀማመጥን ይምረጡ"</string>
+    <string name="cancel" msgid="6442560571259935130">"ይቅር"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"የተኳኋኝአጉላ አዝራር።"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"አነስተኛውን ማያ ወደ ትልቅ አጉላ።"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ብሉቱዝ ተያይዟል።"</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"ምንም አውታረ መረብ የለም"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi ጠፍቷል"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"ምንም የተቀመጡ አውታረ መረቦች አይገኙም"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"ውሰድ"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"በመውሰድ ላይ"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"ያልተሰየመ መሳሪያ"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"ለመውሰድ ዝግጁ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 2bcb2c1..de7250d 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -92,6 +92,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"إلغاء القفل"</string>
     <string name="phone_label" msgid="2320074140205331708">"فتح الهاتف"</string>
     <string name="camera_label" msgid="7261107956054836961">"فتح الكاميرا"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"تحديد تنسيق جديد للمهمة"</string>
+    <string name="cancel" msgid="6442560571259935130">"إلغاء"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"زر تكبير/تصغير للتوافق."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"استخدام التكبير/التصغير لتحويل شاشة صغيرة إلى شاشة أكبر"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"تم توصيل البلوتوث."</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index a4251be..1f69b11 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"отключване"</string>
     <string name="phone_label" msgid="2320074140205331708">"отваряне на телефона"</string>
     <string name="camera_label" msgid="7261107956054836961">"отваряне на камерата"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Избиране на ново оформление за задачите"</string>
+    <string name="cancel" msgid="6442560571259935130">"Отказ"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Бутон за промяна на мащаба с цел съвместимост."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Промяна на мащаба на екрана от по-малък до по-голям."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth е включен."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Няма мрежа"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi е изключен"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Няма налични запазени мрежи"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Предаване"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Предава се"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Устройство без име"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Готово за предаване"</string>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index b085767..26fd0f4 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"আনলক করুন"</string>
     <string name="phone_label" msgid="2320074140205331708">"ফোন খুলুন"</string>
     <string name="camera_label" msgid="7261107956054836961">"ক্যামেরা খুলুন"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"নতুন কার্য লেআউট নির্বাচন করুন"</string>
+    <string name="cancel" msgid="6442560571259935130">"বাতিল করুন"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"সামঞ্জস্যের জুম বোতাম৷"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ছোট থেকে বৃহৎ স্ক্রীণে জুম করুন৷"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth সংযুক্ত হয়েছে৷"</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"কোনো নেটওয়ার্ক নেই"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi বন্ধ"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"কোন সংরক্ষিত নেটওয়ার্ক উপলব্ধ নেই"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"কাস্ট করুন"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"কাস্ট করা হচ্ছে"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"নামবিহীন ডিভাইস"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"কাস্ট করার জন্য প্রস্তুত"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index b260e44..2454adb 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"desbloqueja"</string>
     <string name="phone_label" msgid="2320074140205331708">"obre el telèfon"</string>
     <string name="camera_label" msgid="7261107956054836961">"obre la càmera"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Selecciona el disseny de la tasca nova"</string>
+    <string name="cancel" msgid="6442560571259935130">"Cancel·la"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botó de zoom de compatibilitat."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Amplia menys com més gran sigui la pantalla."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connectat."</string>
@@ -259,8 +261,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"No hi ha cap xarxa"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi desconnectada"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"No hi ha cap xarxa desada disponible."</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Emet"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"En emissió"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositiu sense nom"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"A punt per a l\'emissió"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index f1cbb7e..a3ba579 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -90,6 +90,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"odemknout"</string>
     <string name="phone_label" msgid="2320074140205331708">"otevřít telefon"</string>
     <string name="camera_label" msgid="7261107956054836961">"spustit fotoaparát"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Vybrat nové rozvržení úkolů"</string>
+    <string name="cancel" msgid="6442560571259935130">"Zrušit"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Tlačítko úpravy velikosti z důvodu kompatibility"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zvětšit menší obrázek na větší obrazovku."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Rozhraní Bluetooth je připojeno."</string>
@@ -261,8 +263,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Žádná síť"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi vypnuta"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Nejsou dostupné žádné uložené sítě"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Odeslat"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Odesílání"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Nepojmenované zařízení"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Připraveno k vysílání"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 92efc89..2d4fa0f 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"lås op"</string>
     <string name="phone_label" msgid="2320074140205331708">"åbn telefon"</string>
     <string name="camera_label" msgid="7261107956054836961">"åbn kamera"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Vælg nyt opgavelayout"</string>
+    <string name="cancel" msgid="6442560571259935130">"Annuller"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Knap for kompatibilitetszoom."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom mindre til større skærm."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth tilsluttet."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Intet netværk"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi slået fra"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Der er ingen tilgængelige gemte netværk"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Cast"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Caster"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Enhed uden navn"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Klar til at caste"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 1119f1f..d97a063 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"Entsperren"</string>
     <string name="phone_label" msgid="2320074140205331708">"Telefon öffnen"</string>
     <string name="camera_label" msgid="7261107956054836961">"Kamera öffnen"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Neues Aufgabenlayout auswählen"</string>
+    <string name="cancel" msgid="6442560571259935130">"Abbrechen"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Schaltfläche für Kompatibilitätszoom"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom auf einen größeren Bildschirm"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Mit Bluetooth verbunden"</string>
@@ -259,8 +261,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Kein Netz"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"WLAN aus"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Keine gespeicherten Netzwerke verfügbar"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Übertragen"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Wird übertragen"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Unbenanntes Gerät"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Startklar"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index bc1b7a2..5ccd669 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"ξεκλείδωμα"</string>
     <string name="phone_label" msgid="2320074140205331708">"άνοιγμα τηλεφώνου"</string>
     <string name="camera_label" msgid="7261107956054836961">"άνοιγμα φωτογραφικής μηχανής"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Επιλέξτε τη νέα διάταξη εργασίας"</string>
+    <string name="cancel" msgid="6442560571259935130">"Ακύρωση"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Κουμπί εστίασης συμβατότητας."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Ζουμ από μικρότερη σε μεγαλύτερη οθόνη."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Το Bluetooth είναι συνδεδεμένο."</string>
@@ -259,8 +261,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Κανένα δίκτυο"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi ανενεργό"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Δεν υπάρχουν διαθέσιμα αποθηκευμένα δίκτυα"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Μετάδοση"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Μετάδοση"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Ανώνυμη συσκευή"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Έτοιμο για μετάδοση"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index ee3f826..dffe8ce 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"unlock"</string>
     <string name="phone_label" msgid="2320074140205331708">"open phone"</string>
     <string name="camera_label" msgid="7261107956054836961">"open camera"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
+    <string name="cancel" msgid="6442560571259935130">"Cancel"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Compatibility zoom button."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom smaller to larger screen."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connected."</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index ee3f826..dffe8ce 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"unlock"</string>
     <string name="phone_label" msgid="2320074140205331708">"open phone"</string>
     <string name="camera_label" msgid="7261107956054836961">"open camera"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
+    <string name="cancel" msgid="6442560571259935130">"Cancel"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Compatibility zoom button."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom smaller to larger screen."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connected."</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index ee3f826..dffe8ce 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"unlock"</string>
     <string name="phone_label" msgid="2320074140205331708">"open phone"</string>
     <string name="camera_label" msgid="7261107956054836961">"open camera"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Select new task layout"</string>
+    <string name="cancel" msgid="6442560571259935130">"Cancel"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Compatibility zoom button."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom smaller to larger screen."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connected."</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index a605a03..af28b22 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"desbloquear"</string>
     <string name="phone_label" msgid="2320074140205331708">"abrir teléfono"</string>
     <string name="camera_label" msgid="7261107956054836961">"abrir cámara"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Selecciona el nuevo diseño de la tarea."</string>
+    <string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botón de zoom de compatibilidad"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de pantalla más pequeña a más grande"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth conectado"</string>
@@ -259,8 +261,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Sin red"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi desactivada"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"No hay redes guardadas disponibles"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Transmitir"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Transmitiendo"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositivo sin nombre"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Listo para transmitir"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index dcffc65..989691a 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"desbloquear"</string>
     <string name="phone_label" msgid="2320074140205331708">"abrir teléfono"</string>
     <string name="camera_label" msgid="7261107956054836961">"abrir cámara"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Seleccionar diseño de tarea nueva"</string>
+    <string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botón de zoom de compatibilidad"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de pantalla más pequeña a más grande"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth conectado"</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"No hay red."</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi desactivado"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"No hay redes guardadas disponibles"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Enviar"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Enviando"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositivo sin nombre"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Listo para enviar"</string>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 1a74a8f..110ee53 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"ava lukk"</string>
     <string name="phone_label" msgid="2320074140205331708">"ava telefon"</string>
     <string name="camera_label" msgid="7261107956054836961">"ava kaamera"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Uue toimingu paigutuse valimine"</string>
+    <string name="cancel" msgid="6442560571259935130">"Tühista"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Sobivussuumi nupp."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Suumi suuremale ekraanile vähem."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth on ühendatud."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Võrku pole"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"WiFi-ühendus on väljas"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Ühtegi salvestatud võrku pole saadaval"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Ülekandmine"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Osatäitjad"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Nimeta seade"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Valmis ülekandmiseks"</string>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index c7019d9..220a5f0 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"desblokeatu"</string>
     <string name="phone_label" msgid="2320074140205331708">"ireki telefonoan"</string>
     <string name="camera_label" msgid="7261107956054836961">"ireki kamera"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Hautatu zereginen diseinua"</string>
+    <string name="cancel" msgid="6442560571259935130">"Utzi"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Zoom-bateragarritasunaren botoia."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Handiagotu pantaila txikia."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetootha konektatuta."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ez dago sarerik"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi konexioa desaktibatuta"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Ez dago gordetako sarerik erabilgarri"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Igorpena"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Igortzen"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Izenik gabeko gailua"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Igortzeko prest"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 18d8f76..b6a6ecb 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"بازکردن قفل"</string>
     <string name="phone_label" msgid="2320074140205331708">"باز کردن تلفن"</string>
     <string name="camera_label" msgid="7261107956054836961">"باز کردن دوربین"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"انتخاب طرح‌بندی جدید کار"</string>
+    <string name="cancel" msgid="6442560571259935130">"لغو"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"دکمه بزرگنمایی سازگار."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"بزرگنمایی از صفحه‌های کوچک تا بزرگ."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"بلوتوث متصل است."</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 61e591a..4edd4c2 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"avaa lukitus"</string>
     <string name="phone_label" msgid="2320074140205331708">"avaa puhelin"</string>
     <string name="camera_label" msgid="7261107956054836961">"avaa kamera"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Valitse uusi tehtävien asettelu"</string>
+    <string name="cancel" msgid="6442560571259935130">"Peruuta"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Yhteensopivuuszoomaus-painike."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoomaa pienemmältä suuremmalle ruudulle."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth yhdistetty."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ei verkkoa"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi-yhteys pois käytöstä"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Tallennettuja verkkoja ei ole käytettävissä"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Suoratoisto"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Lähetetään"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Nimetön laite"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Valmis lähetystä varten"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 3427cfa..a369dfd 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"déverrouiller"</string>
     <string name="phone_label" msgid="2320074140205331708">"Ouvrir le téléphone"</string>
     <string name="camera_label" msgid="7261107956054836961">"Ouvrir l\'appareil photo"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Sélectionner un nouveau format de tâche"</string>
+    <string name="cancel" msgid="6442560571259935130">"Annuler"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Bouton \"Zoom de compatibilité\""</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de compatibilité avec la taille de l\'écran"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connecté"</string>
@@ -259,8 +261,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Aucun réseau"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi désactivé"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Aucun des réseaux enregistrés n\'est disponible"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Diffuser"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Diffusion"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Appareil sans nom"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Prêt à diffuser"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 393a0f8..18dac4c 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"déverrouiller"</string>
     <string name="phone_label" msgid="2320074140205331708">"ouvrir le téléphone"</string>
     <string name="camera_label" msgid="7261107956054836961">"ouvrir l\'appareil photo"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Sélectionner un nouveau plan de tâche"</string>
+    <string name="cancel" msgid="6442560571259935130">"Annuler"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Bouton \"Zoom de compatibilité\""</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de compatibilité avec la taille de l\'écran"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth connecté"</string>
@@ -259,8 +261,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Aucun réseau"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi désactivé"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Aucun réseau enregistré disponible."</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Diffuser"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Diffusion"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Appareil sans nom"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Prêt à caster"</string>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index c47fc4e..75c0cb9 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"desbloquear"</string>
     <string name="phone_label" msgid="2320074140205331708">"abrir teléfono"</string>
     <string name="camera_label" msgid="7261107956054836961">"abrir cámara"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Seleccionar novo deseño de tarefas"</string>
+    <string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botón de zoom de compatibilidade"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de compatibilidade co tamaño da pantalla."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth conectado"</string>
@@ -259,8 +261,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Non hai rede"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wifi desactivada"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Non hai redes gardadas dispoñibles"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Emisión"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Emitindo"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositivo sen nome"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Listo para emitir"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 516ae5e..5c45c8b 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"अनलॉक करें"</string>
     <string name="phone_label" msgid="2320074140205331708">"फ़ोन खोलें"</string>
     <string name="camera_label" msgid="7261107956054836961">"कैमरा खोलें"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"नया कार्य लेआउट चुनें"</string>
+    <string name="cancel" msgid="6442560571259935130">"अभी नहीं"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"संगतता ज़ूम बटन."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"छोटी से बड़ी स्‍क्रीन पर ज़ूम करें."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ब्लूटूथ कनेक्ट किया गया."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"कोई नेटवर्क नहीं"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"वाई-फ़ाई  बंद"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"कोई भी सहेजा गया नेटवर्क उपलब्ध नहीं"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"कास्ट करें"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"कास्टिंग"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"अनाम डिवाइस"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"कास्ट करने के लिए तैयार"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index f63b1cd..4210738 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -89,6 +89,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"otključavanje"</string>
     <string name="phone_label" msgid="2320074140205331708">"otvaranje telefona"</string>
     <string name="camera_label" msgid="7261107956054836961">"otvaranje fotoaparata"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Odaberite novi izgled zadataka"</string>
+    <string name="cancel" msgid="6442560571259935130">"Odustani"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Gumb za kompatibilnost zumiranja."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zumiranje manjeg zaslona na veći."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth povezan."</string>
@@ -258,8 +260,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Nema mreže"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi isključen"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Spremljene mreže nisu dostupne"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Emitiranje"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Emitiranje"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Uređaj bez naziva"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Spreman za emitiranje"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 621bdb8..2e8c4f3c 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"feloldás"</string>
     <string name="phone_label" msgid="2320074140205331708">"telefon megnyitása"</string>
     <string name="camera_label" msgid="7261107956054836961">"kamera megnyitása"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Új feladatelrendezés kiválasztása"</string>
+    <string name="cancel" msgid="6442560571259935130">"Mégse"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Kompatibilitási zoom gomb."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Kicsinyítsen a nagyobb képernyőhöz."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth csatlakoztatva."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Nincs hálózat"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi kikapcsolva"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Nem áll rendelkezésre mentett hálózat"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Tartalomátküldés"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Átküldés"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Név nélküli eszköz"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Küldésre kész"</string>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index 4529415..91d36da 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"ապակողպել"</string>
     <string name="phone_label" msgid="2320074140205331708">"բացել հեռախոսը"</string>
     <string name="camera_label" msgid="7261107956054836961">"բացել ֆոտոխցիկը"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Ընտրել առաջադրանքի նոր դասավորություն"</string>
+    <string name="cancel" msgid="6442560571259935130">"Չեղարկել"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Համատեղելիության խոշորացման կոճակը:"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Դիտափոխել փոքրից ավելի մեծ էկրան:"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth-ը միացված է:"</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ցանց չկա"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi-ը անջատված է"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Հասանելի պահված ցանցեր չկան"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Հեռարձակում"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Հեռարձակում"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Անանուն սարք"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Պատրաստ է հեռարձակման"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 5bf8d13..bacca07 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"buka kunci"</string>
     <string name="phone_label" msgid="2320074140205331708">"buka ponsel"</string>
     <string name="camera_label" msgid="7261107956054836961">"buka kamera"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Pilih tata letak tugas baru"</string>
+    <string name="cancel" msgid="6442560571259935130">"Batal"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Tombol perbesar/perkecil kompatibilitas."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Perbesar dari layar kecil ke besar."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth tersambung."</string>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index 1ae4566..ffd3361 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"taka úr lás"</string>
     <string name="phone_label" msgid="2320074140205331708">"opna síma"</string>
     <string name="camera_label" msgid="7261107956054836961">"opna myndavél"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Velja nýtt útlit verkefna"</string>
+    <string name="cancel" msgid="6442560571259935130">"Hætta við"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Hnappur fyrir samhæfisaðdrátt."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Aðlaga forrit fyrir lítinn skjá að stærri skjá."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth tengt."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ekkert net"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Slökkt á Wi-Fi"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Engin vistuð net til staðar"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Útsending"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Sendir út"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Ónefnt tæki"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Tilbúið í útsendingu"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index c9ccf05..37cf79c 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"sblocca"</string>
     <string name="phone_label" msgid="2320074140205331708">"apri telefono"</string>
     <string name="camera_label" msgid="7261107956054836961">"apri fotocamera"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Seleziona un nuovo layout per le attività"</string>
+    <string name="cancel" msgid="6442560571259935130">"Annulla"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Pulsante zoom compatibilità."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom inferiore per schermo più grande."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth collegato."</string>
@@ -259,8 +261,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Nessuna rete"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi disattivato"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Nessuna rete salvata disponibile"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Trasmetti"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"In trasmissione"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositivo senza nome"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Pronto a trasmettere"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index ff7fc57..f7c66f7 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -90,6 +90,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"בטל את הנעילה"</string>
     <string name="phone_label" msgid="2320074140205331708">"פתח את הטלפון"</string>
     <string name="camera_label" msgid="7261107956054836961">"פתח את המצלמה"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"בחר פריסה חדשה להצגת משימות"</string>
+    <string name="cancel" msgid="6442560571259935130">"ביטול"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"לחצן מרחק מתצוגה של תאימות."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"שנה מרחק מתצוגה של מסך קטן לגדול יותר."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"‏Bluetooth מחובר."</string>
@@ -259,8 +261,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"אין רשת"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"‏Wi-Fi כבוי"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"אין רשתות שמורות זמינות"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Cast"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"מעביר"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"מכשיר ללא שם"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"מוכן להעביר"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index ca6aec9..1d8a2f7 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"ロック解除"</string>
     <string name="phone_label" msgid="2320074140205331708">"電話を起動"</string>
     <string name="camera_label" msgid="7261107956054836961">"カメラを起動"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"新しいタスクレイアウトの選択"</string>
+    <string name="cancel" msgid="6442560571259935130">"キャンセル"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"互換ズームボタン。"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"小さい画面から大きい画面に拡大。"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetoothに接続済み。"</string>
@@ -259,8 +261,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"ネットワークなし"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi OFF"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"保存されているネットワークがありません"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"キャスト"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"キャストしています"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"名前のないデバイス"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"キャスト準備完了"</string>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 100af70..9de70e8 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"განბლოკვა"</string>
     <string name="phone_label" msgid="2320074140205331708">"ტელეფონის გახსნა"</string>
     <string name="camera_label" msgid="7261107956054836961">"კამერის გახსნა"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"ახალი ამოცანის განლაგების არჩევა"</string>
+    <string name="cancel" msgid="6442560571259935130">"გაუქმება"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"თავსებადი მასშტაბირების ღილაკი."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"შეცვალეთ პატარა ეკრანი უფრო დიდით."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth დაკავშირებულია."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"ქსელი არ არის"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi გამორთულია"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"შენახული ქსელები მიუწვდომელია"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"ტრანსლირება"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"გადაიცემა"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"უსახელო მოწყობილობა"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"მზად არის სამაუწყებლოდ"</string>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index 9ed07ca..a340624 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"бекітпесін ашу"</string>
     <string name="phone_label" msgid="2320074140205331708">"телефонды ашу"</string>
     <string name="camera_label" msgid="7261107956054836961">"камераны ашу"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Жаңа тапсырма пішімін таңдау"</string>
+    <string name="cancel" msgid="6442560571259935130">"Бас тарту"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Үйлесімділік ұлғайту түймесі."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Үлкендеу экранда кішірейту."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth қосылған."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Желі жоқ"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi өшірулі"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Сақталған желілер қол жетімді емес"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Трансляциялау"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Трансляциялануда"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Атаусыз құрылғы"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Трансляциялауға дайын"</string>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index de48a23..4fb737c 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"ដោះ​សោ"</string>
     <string name="phone_label" msgid="2320074140205331708">"បើក​ទូរស័ព្ទ"</string>
     <string name="camera_label" msgid="7261107956054836961">"បើក​ម៉ាស៊ីន​ថត"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"ជ្រើសប្លង់ភារកិច្ចថ្មី"</string>
+    <string name="cancel" msgid="6442560571259935130">"បោះ​បង់​"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ប៊ូតុង​ពង្រីក​ត្រូវ​គ្នា។"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ពង្រីក/បង្រួម​​អេក្រង់​ពី​​ទៅធំ"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"បាន​តភ្ជាប់​ប៊្លូធូស។"</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"គ្មាន​បណ្ដាញ"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"វ៉ាយហ្វាយ​បានបិទ"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"មិន​មាន​បណ្ដាញ​ដែល​បាន​រក្សាទុក"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"ខាស"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"ការ​ចាត់​ថ្នាក់"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"ឧបករណ៍​​ដែល​មិន​មាន​ឈ្មោះ"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"ត្រៀម​រួចរាល់​ដើម្បី​ចាត់​ថ្នាក់"</string>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index 2687de1..87a1bb4 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"ಅನ್‌ಲಾಕ್ ಮಾಡು"</string>
     <string name="phone_label" msgid="2320074140205331708">"ಫೋನ್ ತೆರೆಯಿರಿ"</string>
     <string name="camera_label" msgid="7261107956054836961">"ಕ್ಯಾಮರಾ ತೆರೆಯಿರಿ"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"ಹೊಸ ಕಾರ್ಯ ವಿನ್ಯಾಸವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+    <string name="cancel" msgid="6442560571259935130">"ರದ್ದುಮಾಡು"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ಹೊಂದಾಣಿಕೆಯ ಝೂಮ್ ಬಟನ್."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ಚಿಕ್ಕ ಪರದೆಯಿಂದ ದೊಡ್ಡ ಪರದೆಗೆ ಝೂಮ್ ಮಾಡು."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ಬ್ಲೂಟೂತ್‌‌ ಸಂಪರ್ಕಗೊಂಡಿದೆ."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"ನೆಟ್‌ವರ್ಕ್ ಇಲ್ಲ"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi ಆಫ್"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"ಯಾವುದೇ ಉಳಿಸಲಾದ ನೆಟ್‌ವರ್ಕ್‌ಗಳು ಲಭ್ಯವಿಲ್ಲ"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"ಬಿತ್ತರಿಸುವಿಕೆ"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"ಬಿತ್ತರಿಸಲಾಗುತ್ತಿದೆ"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"ಹೆಸರಿಸದಿರುವ ಸಾಧನ"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"ಬಿತ್ತರಿಸಲು ಸಿದ್ದವಾಗಿದೆ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 993bef4..9fe24f4 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"잠금 해제"</string>
     <string name="phone_label" msgid="2320074140205331708">"휴대전화 열기"</string>
     <string name="camera_label" msgid="7261107956054836961">"카메라 열기"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"새 작업 레이아웃 선택"</string>
+    <string name="cancel" msgid="6442560571259935130">"취소"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"호환성 확대/축소 버튼입니다."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"작은 화면을 큰 화면으로 확대합니다."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"블루투스가 연결되었습니다."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"네트워크가 연결되지 않음"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi 꺼짐"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"저장된 네트워크가 없습니다."</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"전송"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"전송 중"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"이름이 없는 기기"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"전송 준비 완료"</string>
@@ -369,8 +370,8 @@
     <string name="notification_collapse_button_text" msgid="6883253262134328057">"모두 숨기기"</string>
     <string name="zen_mode_and_condition" msgid="4462471036429759903">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
     <string name="screen_pinning_title" msgid="3273740381976175811">"화면 고정됨"</string>
-    <string name="screen_pinning_description" msgid="1346522416878235405">"고정 해제하기 전까지 계속 표시됩니다. 고정 해제하려면 뒤로와 개요를 동시에 길게 터치합니다."</string>
-    <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"고정 해제하기 전까지 계속 표시됩니다. 고정 해제하려면 개요를 길게 터치합니다."</string>
+    <string name="screen_pinning_description" msgid="1346522416878235405">"고정 해제하기 전까지 계속 표시됩니다. 고정 해제하려면 뒤로와 최근 사용을 동시에 길게 터치합니다."</string>
+    <string name="screen_pinning_description_accessible" msgid="8518446209564202557">"고정 해제하기 전까지 계속 표시됩니다. 고정 해제하려면 최근 사용을 길게 터치합니다."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"확인"</string>
     <string name="screen_pinning_negative" msgid="3741602308343880268">"거부"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"<xliff:g id="TILE_LABEL">%1$s</xliff:g>을(를) 숨기시겠습니까?"</string>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index 25471e5..1a7aa8b 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -113,6 +113,9 @@
     <string name="unlock_label" msgid="8779712358041029439">"кулпуну ачуу"</string>
     <string name="phone_label" msgid="2320074140205331708">"телефонду ачуу"</string>
     <string name="camera_label" msgid="7261107956054836961">"камераны ачуу"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Жаңы тапшырманын планын тандаңыз"</string>
+    <!-- no translation found for cancel (6442560571259935130) -->
+    <skip />
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Масштабды сыйыштыруу баскычы."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Кичинекейди чоң экранга масштабдоо."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth байланышта"</string>
@@ -282,8 +285,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Желе жок"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi өчүк"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Сакталган тармактар жок"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Тышкы экранга чыгаруу"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Тышкы экранга чыгарылууда"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Аты жок түзмөк"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Тышкы экранга чыгарууга даяр"</string>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index 6ae0c71..4d215e3 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"ປົດລັອກ"</string>
     <string name="phone_label" msgid="2320074140205331708">"​ເປີດ​​ແປ້ນ​ໂທ​ລະ​ສັບ"</string>
     <string name="camera_label" msgid="7261107956054836961">"ເປີດ​ກ້ອງ"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"ເລືອກ​ແຜນ​ຜັງ​ໜ້າ​ວຽກ​ໃໝ່"</string>
+    <string name="cancel" msgid="6442560571259935130">"ຍົກເລີກ"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ປຸ່ມຊູມທີ່ໃຊ້ຮ່ວມກັນໄດ້."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ຊູມຈໍນ້ອຍໄປເປັນຈໍຂະຫນາດໃຫຍ່."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ເຊື່ອມຕໍ່ Bluetooth ແລ້ວ."</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 0fc6fbb..1cb5980 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -90,6 +90,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"atrakinti"</string>
     <string name="phone_label" msgid="2320074140205331708">"atidaryti telefoną"</string>
     <string name="camera_label" msgid="7261107956054836961">"atidaryti fotoaparatą"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Pasirinkti naują užduoties išdėstymą"</string>
+    <string name="cancel" msgid="6442560571259935130">"Atšaukti"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Suderinamumo priartinimo mygtukas."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Padidinti ekraną."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"„Bluetooth“ prijungtas."</string>
@@ -259,8 +261,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Tinklo nėra"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"„Wi-Fi“ išjungta"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Nėra pasiekiamų išsaugotų tinklų"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Perdavimas"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Perduodama"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Įrenginys be pavadinimo"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Paruošta perduoti"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 54cc36a..5fdc0cd 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -89,6 +89,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"atbloķēt"</string>
     <string name="phone_label" msgid="2320074140205331708">"atvērt tālruni"</string>
     <string name="camera_label" msgid="7261107956054836961">"atvērt kameru"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Atlasiet jaunu uzdevumu izkārtojumu"</string>
+    <string name="cancel" msgid="6442560571259935130">"Atcelt"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Saderības tālummaiņas poga."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Veikt tālummaiņu no mazāka ekrāna uz lielāku."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth savienojums ir izveidots."</string>
@@ -258,8 +260,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Nav tīkla"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi ir izslēgts"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Nav pieejams neviens saglabātais tīkls."</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Apraide"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Notiek apraide…"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Nenosaukta ierīce"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Gatavs apraidei"</string>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index 2be9c4c..83988c0 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"отклучи"</string>
     <string name="phone_label" msgid="2320074140205331708">"отвори телефон"</string>
     <string name="camera_label" msgid="7261107956054836961">"отвори камера"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Изберете нов распоред на задача"</string>
+    <string name="cancel" msgid="6442560571259935130">"Откажи"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Копче за компатибилност на зум."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Зумот е помал на поголем екран."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth е поврзан."</string>
@@ -259,8 +261,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Нема мрежа"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi е исклучено"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Нема достапни зачувани мрежи"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Емитувај"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Емитување"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Неименуван уред"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Подготвено за емитување"</string>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index c446927..ee090f9 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"അൺലോക്കുചെയ്യുക"</string>
     <string name="phone_label" msgid="2320074140205331708">"ഫോൺ തുറക്കുക"</string>
     <string name="camera_label" msgid="7261107956054836961">"ക്യാമറ തുറക്കുക"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"പുതിയ ടാസ്‌ക് ലേഔട്ട് തിരഞ്ഞെടുക്കുക"</string>
+    <string name="cancel" msgid="6442560571259935130">"റദ്ദാക്കുക"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"അനുയോജ്യതാ സൂം ബട്ടൺ."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ചെറുതിൽ നിന്ന് വലിയ സ്‌ക്രീനിലേക്ക് സൂം ചെയ്യുക."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ബ്ലൂടൂത്ത് കണക്‌റ്റുചെയ്തു."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"നെറ്റ്‌വർക്ക് ഒന്നുമില്ല"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi ഓഫുചെയ്യുക"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"സംരംക്ഷിച്ച നെറ്റ്‌വർക്കുകളൊന്നും ലഭ്യമല്ല"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"കാസ്‌റ്റുചെയ്യുക"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"കാസ്റ്റുചെയ്യുന്നു"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"പേരിടാത്ത ഉപകരണം"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"കാസ്‌റ്റ് ചെയ്യാൻ തയ്യാറാണ്"</string>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index f7268c2..66f287a 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -86,6 +86,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"тайлах"</string>
     <string name="phone_label" msgid="2320074140205331708">"утас нээх"</string>
     <string name="camera_label" msgid="7261107956054836961">"камер нээх"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Шинэ ажиллах талбарыг сонгоно уу"</string>
+    <string name="cancel" msgid="6442560571259935130">"Цуцлах"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Тохиромжтой өсгөх товч."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Жижгээс том дэлгэцрүү өсгөх."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Блютүүт холбогдсон."</string>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml
index 6023d49..f40429c 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"अनलॉक करा"</string>
     <string name="phone_label" msgid="2320074140205331708">"फोन उघडा"</string>
     <string name="camera_label" msgid="7261107956054836961">"कॅमेरा उघडा"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"नवीन कार्य लेआउट निवडा"</string>
+    <string name="cancel" msgid="6442560571259935130">"रद्द करा"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"सुसंगतता झूम बटण."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"लहानपासून मोठ्‍या स्‍क्रीनवर झूम करा."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ब कनेक्‍ट केले."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"नेटवर्क नाही"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"वाय-फाय बंद"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"कोणतीही जतन केलेली नेटवर्क उपलब्ध नाहीत"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"कास्‍ट करा"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"कास्ट करत आहे"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"निनावी डिव्हाइस"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"कास्ट करण्यास सज्ज"</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 8579e07..5ee867f 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"buka kunci"</string>
     <string name="phone_label" msgid="2320074140205331708">"buka telefon"</string>
     <string name="camera_label" msgid="7261107956054836961">"buka kamera"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Pilih reka letak tugas baharu"</string>
+    <string name="cancel" msgid="6442560571259935130">"Batal"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Butang zum keserasian."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Skrin zum lebih kecil kepada lebih besar."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth disambungkan."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Tiada Rangkaian"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Dimatikan"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Tiada rangkaian disimpan tersedia"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Hantar"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Menghantar"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Peranti tidak bernama"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Bersedia untuk menghantar"</string>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index 58e5239..8880f93 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"သော့ဖွင့်ရန်"</string>
     <string name="phone_label" msgid="2320074140205331708">"ဖုန်းကို ဖွင့်ရန်"</string>
     <string name="camera_label" msgid="7261107956054836961">"ကင်မရာ ဖွင့်ရန်"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"အလုပ်သစ်စီစဥ်မှုကို ရွေးပါ။"</string>
+    <string name="cancel" msgid="6442560571259935130">"ထားတော့"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"အံ့ဝင်သောချုံ့ချဲ့ခလုတ်"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ဖန်သားပြင်ပေါ်တွင် အသေးမှအကြီးသို့ချဲ့ခြင်း"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ဘလူးတုသ်ချိတ်ဆက်ထားမှု"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 0a67650..07cf096 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"lås opp"</string>
     <string name="phone_label" msgid="2320074140205331708">"åpne telefonen"</string>
     <string name="camera_label" msgid="7261107956054836961">"åpne kamera"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Velg en ny utforming for oppgaver"</string>
+    <string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Zoomknapp for kompatibilitet."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom fra mindre til større skjerm."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth er tilkoblet."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ingen nettverk"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi er av"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Ingen lagrede nettverk er tilgjengelige"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Cast"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Casting"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Enhet uten navn"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Klar til å caste"</string>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index 3d5d213..549bc8b 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"खोल्नुहोस्"</string>
     <string name="phone_label" msgid="2320074140205331708">"फोन खोल्नुहोस्"</string>
     <string name="camera_label" msgid="7261107956054836961">"क्यामेरा खोल्नुहोस्"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"नयाँ कार्य लेआउट चयन गर्नुहोस्"</string>
+    <string name="cancel" msgid="6442560571259935130">"रद्द गर्नुहोस्"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"मिलाउने जुम बटन।"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"स्क्रिनलाई सानोबाट ठूलो पार्नुहोस्।"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"ब्लुटुथ जडान भयो।"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 16d4eea..4f2bcc5 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"ontgrendelen"</string>
     <string name="phone_label" msgid="2320074140205331708">"telefoon openen"</string>
     <string name="camera_label" msgid="7261107956054836961">"camera openen"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Nieuwe taakindeling selecteren"</string>
+    <string name="cancel" msgid="6442560571259935130">"Annuleren"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Knop voor compatibiliteitszoom."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Kleiner scherm uitzoomen naar groter scherm."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth-verbinding ingesteld."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Geen netwerk"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wifi uit"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Geen opgeslagen netwerken beschikbaar"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Casten"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Casten"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Naamloos apparaat"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Klaar om te casten"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 3b9efe7..7e7253c 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -90,6 +90,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"odblokuj"</string>
     <string name="phone_label" msgid="2320074140205331708">"otwórz telefon"</string>
     <string name="camera_label" msgid="7261107956054836961">"otwórz aparat"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Wybierz nowy układ zadań"</string>
+    <string name="cancel" msgid="6442560571259935130">"Anuluj"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Przycisk powiększenia na potrzeby zgodności."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Powiększa mniejszy ekran do większego."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth połączony."</string>
@@ -259,8 +261,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Brak sieci"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi wyłączone"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Brak dostępnych zapisanych sieci"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Przesyłanie"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Przesyłam"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Urządzenie bez nazwy"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Wszystko gotowe do przesyłania"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 03ca5ee..14f60dc 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"desbloquear"</string>
     <string name="phone_label" msgid="2320074140205331708">"abrir telemóvel"</string>
     <string name="camera_label" msgid="7261107956054836961">"abrir câmara"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Selecionar novo esquema de tarefa"</string>
+    <string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botão zoom de compatibilidade."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom menor para ecrã maior."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth ligado."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Sem Rede"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Desligado"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Sem redes guardadas disponíveis"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Transmitir"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Transmissão"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositivo sem nome"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Pronto para transmitir"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 5a68f7d..15d869a 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"desbloquear"</string>
     <string name="phone_label" msgid="2320074140205331708">"abrir telefone"</string>
     <string name="camera_label" msgid="7261107956054836961">"abrir câmera"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Selecionar novo layout da tarefa"</string>
+    <string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botão de zoom da compatibilidade."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Aumentar a tela com zoom."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth conectado."</string>
@@ -259,8 +261,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Sem rede"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi desligado"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Não há redes salvas disponíveis"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Transmitir"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Transmitindo"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispositivo sem nome"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Pronto para transmitir"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 5887c79..d517a42 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -89,6 +89,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"deblocați"</string>
     <string name="phone_label" msgid="2320074140205331708">"deschideți telefonul"</string>
     <string name="camera_label" msgid="7261107956054836961">"deschideți camera foto"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Selectați noul aspect pentru activitate"</string>
+    <string name="cancel" msgid="6442560571259935130">"Anulați"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Buton zoom pentru compatibilitate."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Faceţi zoom de la o imagine mai mică la una mai mare."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Conectat prin Bluetooth."</string>
@@ -258,8 +260,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Nicio reţea"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi deconectat"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Nicio rețea salvată disponibilă"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Proiectați"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Se proiectează"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Dispozitiv nedenumit"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Pregătit pentru proiecție"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 23e8b3a..b53b581 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -90,6 +90,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"Разблокировать."</string>
     <string name="phone_label" msgid="2320074140205331708">"Открыть телефон."</string>
     <string name="camera_label" msgid="7261107956054836961">"Открыть камеру."</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Выберите другой макет"</string>
+    <string name="cancel" msgid="6442560571259935130">"Отмена"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Кнопка масштабирования (режим совместимости)"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Уменьшение изображения для увеличения свободного места на экране."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth-соединение установлено."</string>
@@ -261,8 +263,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Нет сети"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi выкл."</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Нет доступных сохраненных сетей"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Wi-Fi-монитор"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Передача изображения"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Безымянное устройство"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Готово к передаче"</string>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index ef864de..8bf3a29 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"අඟුල අරින්න"</string>
     <string name="phone_label" msgid="2320074140205331708">"දුරකථනය විවෘත කරන්න"</string>
     <string name="camera_label" msgid="7261107956054836961">"කැමරාව විවෘත කරන්න"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"නව කාර්යය සැකැස්ම තෝරන්න"</string>
+    <string name="cancel" msgid="6442560571259935130">"අවලංගු කරන්න"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ගැළපෙන විශාලන බොත්තම."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"විශාල තිරය වෙත කුඩාව විශාලනය කරන්න."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"බ්ලූටූත් සම්බන්ධිතයි."</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 4fc086d..a17b8ec 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -90,6 +90,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"odomknúť"</string>
     <string name="phone_label" msgid="2320074140205331708">"otvoriť telefón"</string>
     <string name="camera_label" msgid="7261107956054836961">"spustiť fotoaparát"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Vyberte nové rozloženie úlohy"</string>
+    <string name="cancel" msgid="6442560571259935130">"Zrušiť"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Tlačidlo úpravy veľkosti z dôvodu kompatibility."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zväčšiť menší obrázok na väčšiu obrazovku."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth pripojené."</string>
@@ -261,8 +263,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Žiadna sieť"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Sieť Wi-Fi je vypnutá"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Nie sú k dispozícii žiadne uložené siete"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Prenášanie"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Prenáša sa"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Nepomenované zariadenie"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Pripravené na prenášanie"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index f20596b..177ef1e 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -90,6 +90,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"odkleni"</string>
     <string name="phone_label" msgid="2320074140205331708">"odpri telefon"</string>
     <string name="camera_label" msgid="7261107956054836961">"odpri fotoaparat"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Izberite novo postavitev opravil"</string>
+    <string name="cancel" msgid="6442560571259935130">"Prekliči"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Gumb povečave za združljivost."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Povečava manjšega na večji zaslon."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Povezava Bluetooth vzpostavljena."</string>
@@ -259,8 +261,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ni omrežja"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi izklopljen"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Na voljo ni nobeno shranjeno omrežje"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Predvajanje"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Predvajanje"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Neimenovana naprava"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Pripravljeno za predvajanje"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 74b44f4..0262dd7 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -89,6 +89,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"откључај"</string>
     <string name="phone_label" msgid="2320074140205331708">"отвори телефон"</string>
     <string name="camera_label" msgid="7261107956054836961">"отвори камеру"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Изабери нови распоред задатака"</string>
+    <string name="cancel" msgid="6442560571259935130">"Откажи"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Дугме Зум компатибилности."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Зумирање са мањег на већи екран."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth је прикључен."</string>
@@ -258,8 +260,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Нема мреже"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi је искључен"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Није доступна ниједна сачувана мрежа"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Пребацивање"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Пребацивање"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Неименовани уређај"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Спремно за пребацивање"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index de4b84c..71d0596 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"lås upp"</string>
     <string name="phone_label" msgid="2320074140205331708">"öppna mobilen"</string>
     <string name="camera_label" msgid="7261107956054836961">"öppna kameran"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Välj en ny layout för uppgiften"</string>
+    <string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Knapp för kompatibilitetszoom."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zooma mindre skärm till större."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth ansluten."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Inget nätverk"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi av"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Inga sparade nätverk tillgängliga"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Casta"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Castar"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Namnlös enhet"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Redo att casta"</string>
@@ -301,7 +302,7 @@
     <string name="description_direction_left" msgid="7207478719805562165">"Dra åt vänster för <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ."</string>
     <string name="zen_no_interruptions_with_warning" msgid="4396898053735625287">"Inga avbrott. Inte ens alarm."</string>
     <string name="zen_no_interruptions" msgid="7970973750143632592">"Inga avbrott"</string>
-    <string name="zen_important_interruptions" msgid="3477041776609757628">"Endast prioriterade samtal och aviseringar"</string>
+    <string name="zen_important_interruptions" msgid="3477041776609757628">"Bara prioriterade samtal och aviseringar"</string>
     <string name="zen_alarm_information_time" msgid="5235772206174372272">"Nästa alarm är kl. <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
     <string name="zen_alarm_information_day_time" msgid="8422733576255047893">"Nästa alarm är <xliff:g id="ALARM_DAY_AND_TIME">%s</xliff:g>"</string>
     <string name="zen_alarm_warning" msgid="6873910860111498041">"Alarmet kommer inte att höras kl. <xliff:g id="ALARM_TIME">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index c6c4ff0..1811864 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"fungua"</string>
     <string name="phone_label" msgid="2320074140205331708">"fungua simu"</string>
     <string name="camera_label" msgid="7261107956054836961">"fungua kamera"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Chagua muundo mpya wa kazi"</string>
+    <string name="cancel" msgid="6442560571259935130">"Ghairi"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Kichupo cha kukuza kwa utangamanifu"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Kuza kidogo kwa skrini kubwa."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth imeunganishwa."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Hakuna Mtandao"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Imezimwa"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Hakuna mitandao iliyohifadhiwa inayopatikana"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Tuma"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Inatuma"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Kifaa hakina jina"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Tayari kutuma"</string>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index c9da1b8..1c246c5 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"திற"</string>
     <string name="phone_label" msgid="2320074140205331708">"ஃபோனைத் திற"</string>
     <string name="camera_label" msgid="7261107956054836961">"கேமராவைத் திற"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"புதிய பணி தளவமைப்பைத் தேர்ந்தெடுக்கவும்"</string>
+    <string name="cancel" msgid="6442560571259935130">"ரத்துசெய்"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"பொருந்துமாறு அளவை மாற்றும் பொத்தான்."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"சிறியதிலிருந்து பெரிய திரைக்கு அளவை மாற்றும்."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"புளூடூத் இணைக்கப்பட்டது."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"நெட்வொர்க் இல்லை"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"வைஃபையை முடக்கு"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"சேமித்த நெட்வொர்க்குகள் இல்லை"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"அனுப்பு"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"அனுப்புகிறது"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"பெயரிடப்படாத சாதனம்"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"அனுப்பத் தயார்"</string>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index 62c377e..cc5d2ad 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"అన్‌లాక్ చేయి"</string>
     <string name="phone_label" msgid="2320074140205331708">"ఫోన్‌ను తెరువు"</string>
     <string name="camera_label" msgid="7261107956054836961">"కెమెరాను తెరువు"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"కొత్త విధి లేఅవుట్‌ను ఎంచుకోండి"</string>
+    <string name="cancel" msgid="6442560571259935130">"రద్దు చేయండి"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"అనుకూలత జూమ్ బటన్."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"చిన్న స్క్రీన్ నుండి పెద్దదానికి జూమ్ చేయండి."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"బ్లూటూత్ కనెక్ట్ చేయబడింది."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"నెట్‌వర్క్ లేదు"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi ఆఫ్‌లో ఉంది"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"సేవ్ చేసిన నెట్‌వర్క్‌లు ఏవీ అందుబాటులో లేవు"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"ప్రసారం చేయండి"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"ప్రసారం చేస్తోంది"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"పేరులేని పరికరం"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"ప్రసారం చేయడానికి సిద్ధంగా ఉంది"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 6ef2791..1c064e5 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"ปลดล็อก"</string>
     <string name="phone_label" msgid="2320074140205331708">"เปิดโทรศัพท์"</string>
     <string name="camera_label" msgid="7261107956054836961">"เปิดกล้อง"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"เลือกรูปแบบงานใหม่"</string>
+    <string name="cancel" msgid="6442560571259935130">"ยกเลิก"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ปุ่มซูมที่ใช้งานร่วมกันได้"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ซูมหน้าจอให้มีขนาดใหญ่ขึ้น"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"เชื่อมต่อบลูทูธแล้ว"</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"ไม่มีเครือข่าย"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"ปิด WiFi"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"เครือข่ายที่บันทึกไว้ทั้งหมดไม่พร้อมใช้งาน"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"ส่ง"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"กำลังส่ง"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"อุปกรณ์ที่ไม่มีชื่อ"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"พร้อมที่จะส่ง"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index f60a85a..6ecd402 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"i-unlock"</string>
     <string name="phone_label" msgid="2320074140205331708">"buksan ang telepono"</string>
     <string name="camera_label" msgid="7261107956054836961">"buksan ang camera"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Pumili ng bagong layout ng gawain"</string>
+    <string name="cancel" msgid="6442560571259935130">"Kanselahin"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Button ng zoom ng pagiging tugma."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Mag-zoom nang mas maliit sa mas malaking screen."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Nakakonekta ang Bluetooth."</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index df07342..7209af0 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"kilidi aç"</string>
     <string name="phone_label" msgid="2320074140205331708">"telefonu aç"</string>
     <string name="camera_label" msgid="7261107956054836961">"kamerayı aç"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Yeni görev düzenini seçin"</string>
+    <string name="cancel" msgid="6442560571259935130">"İptal"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Uyumluluk zum düğmesi."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Daha büyük ekrana daha küçük yakınlaştır."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth bağlandı."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ağ yok"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Kablosuz Kapalı"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Kullanılabilir kaydedilmiş ağ yok"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Yayınlama"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Yayınlanıyor"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Adsız cihaz"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Yayın için hazır"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 879bbbc..8c582f8 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -90,6 +90,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"розблокувати"</string>
     <string name="phone_label" msgid="2320074140205331708">"відкрити телефон"</string>
     <string name="camera_label" msgid="7261107956054836961">"відкрити камеру"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Виберіть новий макет завдання"</string>
+    <string name="cancel" msgid="6442560571259935130">"Скасувати"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Кнопка масштабування сумісності."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Збільшення екрана."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth під’єднано."</string>
@@ -259,8 +261,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Немає мережі"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi вимкнено"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Немає збережених мереж"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Трансляція"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Трансляція"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Пристрій без назви"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Можна транслювати"</string>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index 25cdd2d..f402071 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"غیر مقفل کریں"</string>
     <string name="phone_label" msgid="2320074140205331708">"فون کھولیں"</string>
     <string name="camera_label" msgid="7261107956054836961">"کیمرا کھولیں"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"نئے کام کا لے آؤٹ منتخب کریں"</string>
+    <string name="cancel" msgid="6442560571259935130">"منسوخ کریں"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"مطابقت پذیری زوم بٹن۔"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"چھوٹی سے بڑی اسکرین پر زوم کریں۔"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"بلوٹوتھ مربوط ہے۔"</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"کوئی نیٹ ورک نہیں ہے"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"‏Wi-Fi آف ہے"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"کوئی محفوظ کردہ نیٹ ورکس دستیاب نہیں ہیں"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"کاسٹ کریں"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"کاسٹنگ"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"بغیر نام والا آلہ"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"کاسٹ کرنے کیلئے تیار"</string>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index 7d7df5f..782d5e1 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"qulfdan chiqarish"</string>
     <string name="phone_label" msgid="2320074140205331708">"telefonni ochish"</string>
     <string name="camera_label" msgid="7261107956054836961">"kamerani ochish"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Yangi vazifa tartibini tanlash"</string>
+    <string name="cancel" msgid="6442560571259935130">"Bekor qilish"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Kattalashtirish tugmasi mosligi."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Kattaroq ekran uchun kichikroqni kattalashtirish."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth ulandi."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Tarmoq mavjud emas"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi o‘chirilgan"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Saqlangan tarmoqlar mavjud emas"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Translatsiya"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Translatsiya qilinmoqda"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Nomsiz qurilma"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Tarqatish uchun tayyor"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 14b8e1e..7343c6f 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"mở khóa"</string>
     <string name="phone_label" msgid="2320074140205331708">"mở điện thoại"</string>
     <string name="camera_label" msgid="7261107956054836961">"mở máy ảnh"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Chọn bố cục tác vụ mới"</string>
+    <string name="cancel" msgid="6442560571259935130">"Hủy"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Nút thu phóng khả năng tương thích."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Thu phóng màn hình lớn hơn hoặc nhỏ hơn."</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Đã kết nối bluetooth."</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Không có mạng nào"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Tắt Wi-Fi"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Không có mạng nào được lưu"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Truyền"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Đang truyền"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Thiết bị không có tên"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Sẵn sàng truyền"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 43dd4f1..ca2cfef 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"解锁"</string>
     <string name="phone_label" msgid="2320074140205331708">"打开电话"</string>
     <string name="camera_label" msgid="7261107956054836961">"打开相机"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"选择新的任务布局"</string>
+    <string name="cancel" msgid="6442560571259935130">"取消"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"兼容性缩放按钮。"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"将小屏幕的图片放大在较大屏幕上显示。"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"蓝牙已连接。"</string>
@@ -259,8 +261,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"无网络"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"WLAN:关闭"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"没有可用的已保存网络"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"投射"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"正在投射"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"未命名设备"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"已准备好投射"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 6636693..04a841d 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"解鎖"</string>
     <string name="phone_label" msgid="2320074140205331708">"開啟電話"</string>
     <string name="camera_label" msgid="7261107956054836961">"開啟相機"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"選取新的工作版面配置"</string>
+    <string name="cancel" msgid="6442560571259935130">"取消"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"相容性縮放按鈕。"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"將較小螢幕的畫面放大在較大螢幕上顯示。"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"藍牙連線已建立。"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 5505db4..98ae37c 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"解除鎖定"</string>
     <string name="phone_label" msgid="2320074140205331708">"開啟電話"</string>
     <string name="camera_label" msgid="7261107956054836961">"開啟攝影機"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"選取新工作版面配置"</string>
+    <string name="cancel" msgid="6442560571259935130">"取消"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"相容性縮放按鈕。"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"將較小螢幕的畫面放大在較大螢幕上顯示。"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"藍牙連線已建立。"</string>
@@ -259,8 +261,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"沒有網路"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi 已關閉"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"找不到已儲存的網路"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"投放"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"投放"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"未命名的裝置"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"可以開始投放了"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index a020f21..0136eb1 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -88,6 +88,8 @@
     <string name="unlock_label" msgid="8779712358041029439">"vula"</string>
     <string name="phone_label" msgid="2320074140205331708">"vula ifoni"</string>
     <string name="camera_label" msgid="7261107956054836961">"vula ikhamera"</string>
+    <string name="recents_caption_resize" msgid="3517056471774958200">"Khetha isakhiwo somsebenzi omusha"</string>
+    <string name="cancel" msgid="6442560571259935130">"Khansela"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Inkinobho evumelekile yokusondeza"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Sondeza kancane esikrinini esikhudlwana"</string>
     <string name="accessibility_bluetooth_connected" msgid="2707027633242983370">"Bluetooth ixhunyiwe"</string>
@@ -257,8 +259,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Ayikho inethiwekhi"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"I-Wi-Fi icimile"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Awekho amanethiwekhi alondoloziwe atholakalayo"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Abalingisi"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Ukusakaza"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Idivayisi engenalo igama"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Ilungele ukusakaza"</string>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 6ecdca3..24f92ef 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -72,5 +72,15 @@
         <attr name="verticalSpacing" format="dimension" />
         <attr name="horizontalSpacing" format="dimension" />
     </declare-styleable>
+
+    <!-- Theme for icons in the status bar (light/dark). background/fillColor is used for dual tone
+         icons like wifi and signal, and singleToneColor is used for icons with only one tone.
+         Contract: Pixel with fillColor blended over backgroundColor blended over translucent should
+         equal to singleToneColor blended over translucent. -->
+    <declare-styleable name="TonedIcon">
+        <attr name="backgroundColor" format="integer" />
+        <attr name="fillColor" format="integer" />
+        <attr name="singleToneColor" format="integer" />
+    </declare-styleable>
 </resources>
 
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index d4aeab6..b319344 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -130,5 +130,12 @@
     <color name="segmented_button_selected">#FFFFFFFF</color>
     <color name="segmented_button_unselected">#B3B0BEC5</color><!-- 70% blue grey 200 -->
     <color name="volume_panel_divider">#1FFFFFFF</color><!-- 12% white -->
-    <color name="light_mode_icon_color">#FF616161</color><!-- grey 700 -->
+
+    <color name="dark_mode_icon_color_single_tone">#99000000</color>
+    <color name="dark_mode_icon_color_dual_tone_background">#3d000000</color>
+    <color name="dark_mode_icon_color_dual_tone_fill">#7a000000</color>
+
+    <color name="light_mode_icon_color_single_tone">#ffffff</color>
+    <color name="light_mode_icon_color_dual_tone_background">#4dffffff</color>
+    <color name="light_mode_icon_color_dual_tone_fill">#ffffff</color>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 66d494b..1c8142f 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -100,9 +100,6 @@
     <!-- Name of the button that links to the Wifi settings screen. [CHAR LIMIT=NONE] -->
     <string name="status_bar_settings_wifi_button">Wi-Fi</string>
 
-    <!-- Label in the system panel for airplane mode (all radios are turned off)[CHAR LIMIT=30] -->
-    <string name="status_bar_settings_airplane">Airplane mode</string>
-
     <!-- Label in system panel saying the device will use the orientation sensor to rotate [CHAR LIMIT=30] -->
     <string name="status_bar_settings_auto_rotation">Auto-rotate screen</string>
 
@@ -216,6 +213,10 @@
     <string name="phone_label">open phone</string>
     <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
     <string name="camera_label">open camera</string>
+    <!-- Caption for "Recents resize" developer debug feature. [CHAR LIMIT=NONE] -->
+    <string name="recents_caption_resize">Select new task layout</string>
+    <!-- Button name for "Cancel". [CHAR LIMIT=NONE] -->
+    <string name="cancel">Cancel</string>
 
     <!-- Content description of the compatibility zoom button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_compatibility_zoom_button">Compatibility zoom button.</string>
@@ -562,8 +563,6 @@
     <!-- Textual description of Ethernet connections -->
     <string name="ethernet_label">Ethernet</string>
 
-    <!-- QuickSettings: Airplane mode [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_airplane_mode_label">Airplane mode</string>
     <!-- QuickSettings: Do not disturb [CHAR LIMIT=NONE] -->
     <string name="quick_settings_dnd_label">Do not disturb</string>
     <!-- QuickSettings: Do not disturb - Priority only [CHAR LIMIT=NONE] -->
@@ -754,10 +753,10 @@
     <string name="camera_hint">Swipe left for camera</string>
 
     <!-- Interruption level: None. [CHAR LIMIT=20] -->
-    <string name="interruption_level_none">None</string>
+    <string name="interruption_level_none">No interruptions</string>
 
     <!-- Interruption level: Priority. [CHAR LIMIT=20] -->
-    <string name="interruption_level_priority">Priority</string>
+    <string name="interruption_level_priority">Priority only</string>
 
     <!-- Interruption level: All. [CHAR LIMIT=20] -->
     <string name="interruption_level_all">All</string>
@@ -961,4 +960,7 @@
 
     <!-- VolumeUI restoration notification: text -->
     <string name="volumeui_notification_text">Touch to restore the original.</string>
+
+    <!-- Volume dialog zen toggle switch title -->
+    <string name="volume_zen_switch_text">Block interruptions</string>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 07fcb82..83dceae 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -265,4 +265,15 @@
         <item name="android:backgroundDimEnabled">false</item>
         <item name="android:alertDialogTheme">@style/Theme.SystemUI.Dialog.Alert</item>
     </style>
+
+    <style name="DualToneLightTheme">
+        <item name="backgroundColor">@color/light_mode_icon_color_dual_tone_background</item>
+        <item name="fillColor">@color/light_mode_icon_color_dual_tone_fill</item>
+        <item name="singleToneColor">@color/light_mode_icon_color_single_tone</item>
+    </style>
+    <style name="DualToneDarkTheme">
+        <item name="backgroundColor">@color/dark_mode_icon_color_dual_tone_background</item>
+        <item name="fillColor">@color/dark_mode_icon_color_dual_tone_fill</item>
+        <item name="singleToneColor">@color/dark_mode_icon_color_single_tone</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/res/values/volume.xml b/packages/SystemUI/res/values/volume.xml
new file mode 100644
index 0000000..f516104
--- /dev/null
+++ b/packages/SystemUI/res/values/volume.xml
@@ -0,0 +1,87 @@
+<!--
+     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.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item name="volume_expand_animation_duration" type="integer">300</item>
+
+    <color name="volume_icon_color">#ffffffff</color>
+    <color name="volume_settings_icon_color">#7fffffff</color>
+
+    <dimen name="volume_slider_interspacing">2dp</dimen>
+    <dimen name="volume_offset_top">0dp</dimen>
+    <dimen name="volume_button_size">48dp</dimen>
+
+    <item name="volume_secondary_alpha" format="float" type="dimen">0.3</item>
+
+    <style name="VolumeDialogAnimations">
+        <item name="android:windowEnterAnimation">@android:anim/fade_in</item>
+        <item name="android:windowExitAnimation">@android:anim/fade_out</item>
+    </style>
+
+    <style name="VolumeButtons" parent="@android:style/Widget.Material.Button.Borderless">
+        <item name="android:background">@drawable/btn_borderless_rect</item>
+    </style>
+
+    <style name="TextAppearance" />
+
+    <style name="TextAppearance.Volume">
+        <item name="android:textStyle">normal</item>
+        <item name="android:textColor">#ffffffff</item>
+        <item name="android:fontFamily">sans-serif</item>
+    </style>
+
+    <style name="TextAppearance.Volume.ZenSwitch">
+        <item name="android:textSize">16sp</item>
+        <item name="android:fontFamily">sans-serif-medium</item>
+    </style>
+
+    <style name="TextAppearance.Volume.ZenSwitchSummary">
+        <item name="android:textSize">14sp</item>
+        <item name="android:fontFamily">sans-serif-medium</item>
+    </style>
+
+    <style name="TextAppearance.Volume.ZenSwitchDetail">
+        <item name="android:textSize">14sp</item>
+        <item name="android:fontFamily">sans-serif</item>
+        <item name="android:textColor">#ffb0b3c5</item>
+    </style>
+
+    <string-array name="volume_stream_titles" translatable="false">
+        <item>Voice calls</item> <!-- STREAM_VOICE_CALL -->
+        <item>System</item> <!-- STREAM_SYSTEM -->
+        <item>Notifications</item> <!-- STREAM_RING -->
+        <item>Media</item> <!-- STREAM_MUSIC -->
+        <item>Alarms</item> <!-- STREAM_ALARM -->
+        <item></item> <!-- STREAM_NOTIFICATION -->
+        <item>Bluetooth calls</item> <!-- STREAM_BLUETOOTH_SCO -->
+        <item></item> <!-- STREAM_SYSTEM_ENFORCED -->
+        <item></item> <!-- STREAM_DTMF -->
+        <item></item> <!-- STREAM_TTS -->
+    </string-array>
+
+    <string name="volume_dnd_is_on" translatable="false">Do not disturb is on</string>
+    <string name="volume_turn_off" translatable="false">Turn off</string>
+    <string name="volume_stream_muted" translatable="false">%s silent</string>
+    <string name="volume_stream_vibrate" translatable="false">%s vibrate</string>
+    <string name="volume_stream_suppressed" translatable="false">%1$s silent — %2$s</string>
+    <string name="volume_stream_muted_dnd" translatable="false">%s silent — No interruptions</string>
+    <string name="volume_stream_limited_dnd" translatable="false">%s — Priority only</string>
+    <string name="volume_stream_vibrate_dnd" translatable="false">%s vibrate — Priority only</string>
+    <string name="volume_dnd_ends_in" translatable="false">Do not disturb ends in %s</string>
+    <string name="volume_dnd_ends_at" translatable="false">Do not disturb ends at %s</string>
+    <string name="volume_end_now" translatable="false">End now</string>
+
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 2e95498..292c9c2 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui;
 
+import android.animation.ArgbEvaluator;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -80,6 +81,12 @@
     private BatteryController mBatteryController;
     private boolean mPowerSaveEnabled;
 
+    private int mDarkModeBackgroundColor;
+    private int mDarkModeFillColor;
+
+    private int mLightModeBackgroundColor;
+    private int mLightModeFillColor;
+
     private class BatteryTracker extends BroadcastReceiver {
         public static final int UNKNOWN_LEVEL = -1;
 
@@ -245,6 +252,13 @@
         mBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
         mBoltPaint.setColor(context.getColor(R.color.batterymeter_bolt_color));
         mBoltPoints = loadBoltPoints(res);
+
+        mDarkModeBackgroundColor =
+                context.getColor(R.color.dark_mode_icon_color_dual_tone_background);
+        mDarkModeFillColor = context.getColor(R.color.dark_mode_icon_color_dual_tone_fill);
+        mLightModeBackgroundColor =
+                context.getColor(R.color.light_mode_icon_color_dual_tone_background);
+        mLightModeFillColor = context.getColor(R.color.light_mode_icon_color_dual_tone_fill);
     }
 
     public void setBatteryController(BatteryController batteryController) {
@@ -309,14 +323,30 @@
         return color;
     }
 
-    public void setIconTint(int tint) {
-        mIconTint = tint;
-        mFramePaint.setColorFilter(new PorterDuffColorFilter(tint, PorterDuff.Mode.SRC_ATOP));
-        mBoltPaint.setColor(tint);
-        mChargeColor = tint;
+    public void setDarkIntensity(float darkIntensity) {
+        int backgroundColor = getBackgroundColor(darkIntensity);
+        int fillColor = getFillColor(darkIntensity);
+        mIconTint = fillColor;
+        mFramePaint.setColor(backgroundColor);
+        mBoltPaint.setColor(backgroundColor);
+        mChargeColor = fillColor;
         invalidate();
     }
 
+    private int getBackgroundColor(float darkIntensity) {
+        return getColorForDarkIntensity(
+                darkIntensity, mLightModeBackgroundColor, mDarkModeBackgroundColor);
+    }
+
+    private int getFillColor(float darkIntensity) {
+        return getColorForDarkIntensity(
+                darkIntensity, mLightModeFillColor, mDarkModeFillColor);
+    }
+
+    private int getColorForDarkIntensity(float darkIntensity, int lightColor, int darkColor) {
+        return (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, lightColor, darkColor);
+    }
+
     @Override
     public void draw(Canvas c) {
         BatteryTracker tracker = mDemoMode ? mDemoTracker : mTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 275a6be..dd28734 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -436,7 +436,8 @@
             }
         }
 
-        public void onFingerprintRecognized(int userId) {
+        @Override
+        public void onFingerprintAuthenticated(int userId) {
             if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
                 mViewMediatorCallback.keyguardDone(true);
             } else {
@@ -636,7 +637,7 @@
                 doKeyguardLocked(null);
             }
         }
-        KeyguardUpdateMonitor.getInstance(mContext).dispatchScreenTurndOff(why);
+        KeyguardUpdateMonitor.getInstance(mContext).dispatchScreenTurnedOff(why);
     }
 
     private void doKeyguardLaterLocked() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 2dd02a5..2bc31fc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -72,7 +72,7 @@
         final boolean airplaneMode = value != 0;
         state.value = airplaneMode;
         state.visible = true;
-        state.label = mContext.getString(R.string.quick_settings_airplane_mode_label);
+        state.label = mContext.getString(R.string.airplane_mode);
         if (airplaneMode) {
             state.icon = mEnable;
             state.contentDescription =  mContext.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 79600f5..182a2dd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -40,6 +40,7 @@
     private static final String ACTION_SET_VISIBLE = "com.android.systemui.dndtile.SET_VISIBLE";
     private static final String EXTRA_VISIBLE = "visible";
     private static final String PREF_KEY_VISIBLE = "DndTileVisible";
+    private static final String PREF_KEY_COMBINED_ICON = "DndTileCombinedIcon";
 
     private final ZenModeController mController;
     private final DndDetailAdapter mDetailAdapter;
@@ -52,7 +53,7 @@
         super(host);
         mController = host.getZenModeController();
         mDetailAdapter = new DndDetailAdapter();
-        mVisible = getSharedPrefs(mContext).getBoolean(PREF_KEY_VISIBLE, false);
+        mVisible = isVisible(host.getContext());
         mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_SET_VISIBLE));
     }
 
@@ -65,6 +66,14 @@
         return getSharedPrefs(context).getBoolean(PREF_KEY_VISIBLE, false);
     }
 
+    public static void setCombinedIcon(Context context, boolean combined) {
+        getSharedPrefs(context).edit().putBoolean(PREF_KEY_COMBINED_ICON, combined).commit();
+    }
+
+    public static boolean isCombinedIcon(Context context) {
+        return getSharedPrefs(context).getBoolean(PREF_KEY_COMBINED_ICON, false);
+    }
+
     @Override
     public DetailAdapter getDetailAdapter() {
         return mDetailAdapter;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 5c1a317..cb78deb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -17,7 +17,6 @@
 package com.android.systemui.qs.tiles;
 
 import android.app.ActivityManager;
-import android.os.SystemClock;
 
 import com.android.systemui.R;
 import com.android.systemui.qs.QSTile;
@@ -27,16 +26,11 @@
 public class FlashlightTile extends QSTile<QSTile.BooleanState> implements
         FlashlightController.FlashlightListener {
 
-    /** Grace period for which we consider the flashlight
-     * still available because it was recently on. */
-    private static final long RECENTLY_ON_DURATION_MILLIS = 500;
-
     private final AnimationIcon mEnable
             = new AnimationIcon(R.drawable.ic_signal_flashlight_enable_animation);
     private final AnimationIcon mDisable
             = new AnimationIcon(R.drawable.ic_signal_flashlight_disable_animation);
     private final FlashlightController mFlashlightController;
-    private long mWasLastOn;
 
     public FlashlightTile(Host host) {
         super(host);
@@ -69,33 +63,17 @@
             return;
         }
         boolean newState = !mState.value;
-        mFlashlightController.setFlashlight(newState);
         refreshState(newState ? UserBoolean.USER_TRUE : UserBoolean.USER_FALSE);
+        mFlashlightController.setFlashlight(newState);
     }
 
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
-        if (state.value) {
-            mWasLastOn = SystemClock.uptimeMillis();
-        }
-
+        state.visible = mFlashlightController.isAvailable();
+        state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
         if (arg instanceof UserBoolean) {
             state.value = ((UserBoolean) arg).value;
         }
-
-        if (!state.value && mWasLastOn != 0) {
-            if (SystemClock.uptimeMillis() > mWasLastOn + RECENTLY_ON_DURATION_MILLIS) {
-                mWasLastOn = 0;
-            } else {
-                mHandler.removeCallbacks(mRecentlyOnTimeout);
-                mHandler.postAtTime(mRecentlyOnTimeout, mWasLastOn + RECENTLY_ON_DURATION_MILLIS);
-            }
-        }
-
-        // Always show the tile when the flashlight is or was recently on. This is needed because
-        // the camera is not available while it is being used for the flashlight.
-        state.visible = mWasLastOn != 0 || mFlashlightController.isAvailable();
-        state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
         final AnimationIcon icon = state.value ? mEnable : mDisable;
         icon.setAllowAnimation(arg instanceof UserBoolean && ((UserBoolean) arg).userInitiated);
         state.icon = icon;
@@ -115,8 +93,8 @@
     }
 
     @Override
-    public void onFlashlightOff() {
-        refreshState(UserBoolean.BACKGROUND_FALSE);
+    public void onFlashlightChanged(boolean enabled) {
+        refreshState(enabled ? UserBoolean.BACKGROUND_TRUE : UserBoolean.BACKGROUND_FALSE);
     }
 
     @Override
@@ -128,11 +106,4 @@
     public void onFlashlightAvailabilityChanged(boolean available) {
         refreshState();
     }
-
-    private Runnable mRecentlyOnTimeout = new Runnable() {
-        @Override
-        public void run() {
-            refreshState();
-        }
-    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 7f1e876..2d1fab0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -385,7 +385,7 @@
         }
 
         // Return early if there are no tasks in the focused stack
-        if (focusedStack.getTaskCount() == 0) return;
+        if (focusedStack == null || focusedStack.getTaskCount() == 0) return;
 
         ActivityManager.RunningTaskInfo runningTask = mSystemServicesProxy.getTopMostTask();
         // Return early if there is no running task (can't determine affiliated tasks in this case)
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index c259c9d..f014f09 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -592,14 +592,14 @@
 
     private RecentsResizeTaskDialog getResizeTaskDebugDialog() {
         if (mResizeTaskDebugDialog == null) {
-            mResizeTaskDebugDialog = new RecentsResizeTaskDialog(getFragmentManager());
+            mResizeTaskDebugDialog = new RecentsResizeTaskDialog(getFragmentManager(), this);
         }
         return mResizeTaskDebugDialog;
     }
 
     @Override
     public void onTaskResize(Task t) {
-        getResizeTaskDebugDialog().showResizeTaskDialog(t);
+        getResizeTaskDebugDialog().showResizeTaskDialog(t, mRecentsView);
     }
 
     /**** RecentsView.RecentsViewCallbacks Implementation ****/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 9263543..abeb2b0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -292,7 +292,7 @@
                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED) != 0;
         lockToAppEnabled = ssp.getSystemSetting(context,
                 Settings.System.LOCK_TO_APP_ENABLED) != 0;
-        multiStackEnabled = "1".equals(ssp.getSystemProperty("overview.enableMultiStack"));
+        multiStackEnabled = "true".equals(ssp.getSystemProperty("persist.sys.debug.multi_window"));
     }
 
     /** Called when the configuration has changed, and we want to reset any configuration specific
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
index d67eceb..4cd577d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java
@@ -16,31 +16,26 @@
 
 package com.android.systemui.recents;
 
-import android.app.ActivityManager;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.app.DialogFragment;
 import android.app.FragmentManager;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.util.MutableInt;
-import android.util.SparseArray;
+import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.widget.EditText;
-import android.widget.Toast;
+import android.widget.Button;
 import com.android.systemui.R;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
+import com.android.systemui.recents.RecentsActivity;
+import com.android.systemui.recents.views.RecentsView;
 
-import java.util.List;
+import java.util.ArrayList;
 
 /**
  * A helper for the dialogs that show when task debugging is on.
@@ -49,80 +44,197 @@
 
     static final String TAG = "RecentsResizeTaskDialog";
 
+    // The various window arrangements we can handle.
+    private static final int PLACE_LEFT = 1;
+    private static final int PLACE_RIGHT = 2;
+    private static final int PLACE_TOP = 3;
+    private static final int PLACE_BOTTOM = 4;
+    private static final int PLACE_TOP_LEFT = 5;
+    private static final int PLACE_TOP_RIGHT = 6;
+    private static final int PLACE_BOTTOM_LEFT = 7;
+    private static final int PLACE_BOTTOM_RIGHT = 8;
+    private static final int PLACE_FULL = 9;
+
+    // The button resource ID combined with the arrangement command.
+    private static final int[][] BUTTON_DEFINITIONS =
+           {{R.id.place_left, PLACE_LEFT},
+            {R.id.place_right, PLACE_RIGHT},
+            {R.id.place_top, PLACE_TOP},
+            {R.id.place_bottom, PLACE_BOTTOM},
+            {R.id.place_top_left, PLACE_TOP_LEFT},
+            {R.id.place_top_right, PLACE_TOP_RIGHT},
+            {R.id.place_bottom_left, PLACE_BOTTOM_LEFT},
+            {R.id.place_bottom_right, PLACE_BOTTOM_RIGHT},
+            {R.id.place_full, PLACE_FULL}};
+
     // The task we want to resize.
-    Task mTaskToResize;
-    FragmentManager mFragmentManager;
-    View mResizeTaskDialogContent;
+    private FragmentManager mFragmentManager;
+    private View mResizeTaskDialogContent;
+    private RecentsActivity mRecentsActivity;
+    private RecentsView mRecentsView;
+    private SystemServicesProxy mSsp;
+    private Rect[] mBounds = {new Rect(), new Rect(), new Rect(), new Rect()};
+    private Task[] mTasks = {null, null, null, null};
 
-    public RecentsResizeTaskDialog() {}
-
-    public RecentsResizeTaskDialog(FragmentManager mgr) {
+    public RecentsResizeTaskDialog(FragmentManager mgr, RecentsActivity activity) {
         mFragmentManager = mgr;
+        mRecentsActivity = activity;
+        mSsp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
     }
 
     /** Shows the resize-task dialog. */
-    void showResizeTaskDialog(Task t) {
-        mTaskToResize = t;
+    void showResizeTaskDialog(Task mainTask, RecentsView rv) {
+        mTasks[0] = mainTask;
+        mRecentsView = rv;
+
         show(mFragmentManager, TAG);
     }
 
     /** Creates a new resize-task dialog. */
     private void createResizeTaskDialog(final Context context, LayoutInflater inflater,
-            AlertDialog.Builder builder, final SystemServicesProxy ssp) {
-        builder.setTitle("Resize Task - Enter new dimensions");
+            AlertDialog.Builder builder) {
+        builder.setTitle(R.string.recents_caption_resize);
         mResizeTaskDialogContent =
-                inflater.inflate(R.layout.recents_multistack_stack_size_dialog, null, false);
-        Rect bounds = ssp.getTaskBounds(mTaskToResize.key.stackId);
-        setDimensionInEditText(mResizeTaskDialogContent, R.id.inset_left, bounds.left);
-        setDimensionInEditText(mResizeTaskDialogContent, R.id.inset_top, bounds.top);
-        setDimensionInEditText(mResizeTaskDialogContent, R.id.inset_right, bounds.right);
-        setDimensionInEditText(mResizeTaskDialogContent, R.id.inset_bottom, bounds.bottom);
-        builder.setView(mResizeTaskDialogContent);
-        builder.setPositiveButton("Resize Task", new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                int left = getDimensionFromEditText(mResizeTaskDialogContent, R.id.inset_left);
-                int top = getDimensionFromEditText(mResizeTaskDialogContent, R.id.inset_top);
-                int right = getDimensionFromEditText(mResizeTaskDialogContent, R.id.inset_right);
-                int bottom = getDimensionFromEditText(mResizeTaskDialogContent, R.id.inset_bottom);
-                if (bottom <= top || right <= left) {
-                    Toast.makeText(context, "Invalid dimensions", Toast.LENGTH_SHORT).show();
-                    dismiss();
-                    return;
-                }
-                ssp.resizeTask(mTaskToResize.key.id, new Rect(left, top, right, bottom));
-                dismiss();
-            }
-        });
-        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                dismiss();
-            }
-        });
-    }
+                inflater.inflate(R.layout.recents_task_resize_dialog, null, false);
 
-    /** Helper to get an integer value from an edit text. */
-    private int getDimensionFromEditText(View container, int id) {
-        String text = ((EditText) container.findViewById(id)).getText().toString();
-        if (text.trim().length() != 0) {
-            return Integer.parseInt(text.trim());
+        for (int i = 0; i < BUTTON_DEFINITIONS.length; i++) {
+            Button b = (Button)mResizeTaskDialogContent.findViewById(BUTTON_DEFINITIONS[i][0]);
+            if (b != null) {
+                final int action = BUTTON_DEFINITIONS[i][1];
+                b.setOnClickListener(
+                        new View.OnClickListener() {
+                            public void onClick(View v) {
+                                placeTasks(action);
+                            }
+                        });
+            }
         }
-        return 0;
+
+        builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                dismiss();
+            }
+        });
+
+        builder.setView(mResizeTaskDialogContent);
     }
 
-    /** Helper to set an integer value to an edit text. */
-    private void setDimensionInEditText(View container, int id, int value) {
-        ((EditText) container.findViewById(id)).setText("" + value);
+    /** Helper function to place window(s) on the display according to an arrangement request. */
+    private void placeTasks(int arrangement) {
+        Rect rect = mSsp.getWindowRect();
+        for (int i = 0; i < mBounds.length; ++i) {
+            mBounds[i].set(rect);
+            if (i != 0) {
+                mTasks[i] = null;
+            }
+        }
+        int additionalTasks = 0;
+        switch (arrangement) {
+            case PLACE_LEFT:
+                mBounds[0].right = mBounds[0].centerX();
+                mBounds[1].left = mBounds[0].right;
+                additionalTasks = 1;
+                break;
+            case PLACE_RIGHT:
+                mBounds[1].right = mBounds[1].centerX();
+                mBounds[0].left = mBounds[1].right;
+                additionalTasks = 1;
+                break;
+            case PLACE_TOP:
+                mBounds[0].bottom = mBounds[0].centerY();
+                mBounds[1].top = mBounds[0].bottom;
+                additionalTasks = 1;
+                break;
+            case PLACE_BOTTOM:
+                mBounds[1].bottom = mBounds[1].centerY();
+                mBounds[0].top = mBounds[1].bottom;
+                additionalTasks = 1;
+                break;
+            case PLACE_TOP_LEFT:  // TL, TR, BL, BR
+                mBounds[0].right = mBounds[0].centerX();
+                mBounds[0].bottom = mBounds[0].centerY();
+                mBounds[1].left = mBounds[0].right;
+                mBounds[1].bottom = mBounds[0].bottom;
+                mBounds[2].right = mBounds[0].right;
+                mBounds[2].top = mBounds[0].bottom;
+                mBounds[3].left = mBounds[0].right;
+                mBounds[3].top = mBounds[0].bottom;
+                additionalTasks = 3;
+                break;
+            case PLACE_TOP_RIGHT:  // TR, TL, BR, BL
+                mBounds[0].left = mBounds[0].centerX();
+                mBounds[0].bottom = mBounds[0].centerY();
+                mBounds[1].right = mBounds[0].left;
+                mBounds[1].bottom = mBounds[0].bottom;
+                mBounds[2].left = mBounds[0].left;
+                mBounds[2].top = mBounds[0].bottom;
+                mBounds[3].right = mBounds[0].left;
+                mBounds[3].top = mBounds[0].bottom;
+                additionalTasks = 3;
+                break;
+            case PLACE_BOTTOM_LEFT:  // BL, BR, TL, TR
+                mBounds[0].right = mBounds[0].centerX();
+                mBounds[0].top = mBounds[0].centerY();
+                mBounds[1].left = mBounds[0].right;
+                mBounds[1].top = mBounds[0].top;
+                mBounds[2].right = mBounds[0].right;
+                mBounds[2].bottom = mBounds[0].top;
+                mBounds[3].left = mBounds[0].right;
+                mBounds[3].bottom = mBounds[0].top;
+                additionalTasks = 3;
+                break;
+            case PLACE_BOTTOM_RIGHT:  // BR, BL, TR, TL
+                mBounds[0].left = mBounds[0].centerX();
+                mBounds[0].top = mBounds[0].centerY();
+                mBounds[1].right = mBounds[0].left;
+                mBounds[1].top = mBounds[0].top;
+                mBounds[2].left = mBounds[0].left;
+                mBounds[2].bottom = mBounds[0].top;
+                mBounds[3].right = mBounds[0].left;
+                mBounds[3].bottom = mBounds[0].top;
+                additionalTasks = 3;
+                break;
+            case PLACE_FULL:
+                // Nothing to change.
+                break;
+        }
+
+        // Get the other tasks.
+        for (int i = 1; i <= additionalTasks && mTasks[i - 1] != null; ++i) {
+            mTasks[i] = mRecentsView.getNextTaskOrTopTask(mTasks[i - 1]);
+            // Do stop if we circled back to the first item.
+            if (mTasks[i] == mTasks[0]) {
+                mTasks[i] = null;
+            }
+        }
+
+        // Resize all tasks beginning from the "oldest" one.
+        for (int i = additionalTasks; i >= 0; --i) {
+            if (mTasks[i] != null) {
+               mSsp.resizeTask(mTasks[i].key.id, mBounds[i]);
+            }
+        }
+
+        // Get rid of the dialog.
+        dismiss();
+        mRecentsActivity.dismissRecentsToHomeRaw(false);
+
+        // Show tasks - beginning with the oldest so that the focus ends on the selected one.
+        // TODO: Remove this once issue b/19893373 is resolved.
+        for (int i = additionalTasks; i >= 0; --i) {
+            if (mTasks[i] != null) {
+                mRecentsView.launchTask(mTasks[i]);
+            }
+        }
     }
 
     @Override
     public Dialog onCreateDialog(Bundle args) {
         final Context context = this.getActivity();
-        final SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
         LayoutInflater inflater = getActivity().getLayoutInflater();
         AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
-        createResizeTaskDialog(context, inflater, builder, ssp);
+        createResizeTaskDialog(context, inflater, builder);
         return builder.create();
     }
 }
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 a473a29..d60df9c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -259,7 +259,7 @@
     public Rect getTaskBounds(int stackId) {
         ActivityManager.StackInfo info = getAllStackInfos().get(stackId);
         if (info != null)
-          return getAllStackInfos().get(stackId).bounds;
+          return info.bounds;
         return new Rect();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 448a7a9..abed7a5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -149,6 +149,31 @@
         return mTaskStackViews;
     }
 
+    /** Gets the next task in the stack - or if the last - the top task */
+    public Task getNextTaskOrTopTask(Task taskToSearch) {
+        Task returnTask = null; 
+        boolean found = false;
+        List<TaskStackView> stackViews = getTaskStackViews();
+        int stackCount = stackViews.size();
+        for (int i = stackCount - 1; i >= 0; --i) {
+            TaskStack stack = stackViews.get(i).getStack();
+            ArrayList<Task> taskList = stack.getTasks();
+            // Iterate the stack views and try and find the focused task
+            for (int j = taskList.size() - 1; j >= 0; --j) {
+                Task task = taskList.get(j);
+                // Return the next task in the line.
+                if (found)
+                    return task;
+                // Remember the first possible task as the top task.
+                if (returnTask == null)
+                    returnTask = task;
+                if (task == taskToSearch)
+                    found = true;
+            }
+        }
+        return returnTask;
+    }
+
     /** Launches the focused task from the first stack if possible */
     public boolean launchFocusedTask() {
         // Get the first stack view
@@ -172,6 +197,28 @@
         return false;
     }
 
+    /** Launches a given task. */
+    public boolean launchTask(Task task) {
+        // Get the first stack view
+        List<TaskStackView> stackViews = getTaskStackViews();
+        int stackCount = stackViews.size();
+        for (int i = 0; i < stackCount; i++) {
+            TaskStackView stackView = stackViews.get(i);
+            TaskStack stack = stackView.getStack();
+            // Iterate the stack views and try and find the given task.
+            List<TaskView> taskViews = stackView.getTaskViews();
+            int taskViewCount = taskViews.size();
+            for (int j = 0; j < taskViewCount; j++) {
+                TaskView tv = taskViews.get(j);
+                if (tv.getTask() == task) {
+                    onTaskViewClicked(stackView, tv, stack, task, false);
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     /** Launches the task that Recents was launched from, if possible */
     public boolean launchPreviousTask() {
         // Get the first stack view
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index e81a1d0..3a97a41 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -815,7 +815,9 @@
         }
 
         // Start dozing
-        mUIDozeTrigger.startDozing();
+        if (!mConfig.multiStackEnabled) {
+            mUIDozeTrigger.startDozing();
+        }
     }
 
     /** Requests this task stacks to start it's enter-recents animation */
@@ -1219,7 +1221,7 @@
         RecentsTaskLoader.getInstance().loadTaskData(task);
 
         // If the doze trigger has already fired, then update the state for this task view
-        if (mUIDozeTrigger.hasTriggered()) {
+        if (mConfig.multiStackEnabled || mUIDozeTrigger.hasTriggered()) {
             tv.setNoUserInteractionState();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index ca08319..60a91bf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -26,14 +26,15 @@
 import android.content.res.ColorStateList;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.RippleDrawable;
 import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.PorterDuffXfermode;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.RippleDrawable;
+import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewOutlineProvider;
@@ -43,7 +44,9 @@
 import com.android.systemui.R;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
+import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.misc.Utilities;
+import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
 
 
@@ -51,6 +54,7 @@
 public class TaskViewHeader extends FrameLayout {
 
     RecentsConfiguration mConfig;
+    private SystemServicesProxy mSsp;
 
     // Header views
     ImageView mMoveTaskButton;
@@ -91,6 +95,7 @@
     public TaskViewHeader(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
         mConfig = RecentsConfiguration.getInstance();
+        mSsp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
         setWillNotDraw(false);
         setClipToOutline(true);
         setOutlineProvider(new ViewOutlineProvider() {
@@ -124,9 +129,6 @@
         mActivityDescription = (TextView) findViewById(R.id.activity_description);
         mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
         mMoveTaskButton = (ImageView) findViewById(R.id.move_task);
-        if (mConfig.multiStackEnabled) {
-            mMoveTaskButton.setVisibility(View.VISIBLE);
-        }
 
         // Hide the backgrounds if they are ripple drawables
         if (!Constants.DebugFlags.App.EnableTaskFiltering) {
@@ -189,10 +191,7 @@
             mApplicationIcon.setImageDrawable(t.applicationIcon);
         }
         mApplicationIcon.setContentDescription(t.activityLabel);
-        // Always update when multi stack debugging is enabled as the stack id can change
-        if (mConfig.multiStackEnabled) {
-            mActivityDescription.setText("[" + t.key.stackId + "] " + t.activityLabel);
-        } else if (!mActivityDescription.getText().toString().equals(t.activityLabel)) {
+        if (!mActivityDescription.getText().toString().equals(t.activityLabel)) {
             mActivityDescription.setText(t.activityLabel);
         }
         // Try and apply the system ui tint
@@ -209,6 +208,43 @@
                 mLightDismissDrawable : mDarkDismissDrawable);
         mDismissButton.setContentDescription(String.format(mDismissContentDescription,
                 t.activityLabel));
+        mMoveTaskButton.setVisibility((mConfig.multiStackEnabled) ? View.VISIBLE : View.INVISIBLE);
+        if (mConfig.multiStackEnabled) {
+            updateResizeTaskBarIcon(t);
+        }
+    }
+
+    /** Updates the resize task bar button. */
+    void updateResizeTaskBarIcon(Task t) {
+        Rect display = mSsp.getWindowRect();
+        Rect taskRect = mSsp.getTaskBounds(t.key.stackId);
+        int resId = R.drawable.star;
+        if (display.equals(taskRect) || taskRect.isEmpty()) {
+            resId = R.drawable.vector_drawable_place_fullscreen;
+        } else {
+            boolean top = display.top == taskRect.top;
+            boolean bottom = display.bottom == taskRect.bottom;
+            boolean left = display.left == taskRect.left;
+            boolean right = display.right == taskRect.right;
+            if (top && bottom && left) {
+                resId = R.drawable.vector_drawable_place_left;
+            } else if (top && bottom && right) {
+                resId = R.drawable.vector_drawable_place_right;
+            } else if (top && left && right) {
+                resId = R.drawable.vector_drawable_place_top;
+            } else if (bottom && left && right) {
+                resId = R.drawable.vector_drawable_place_bottom;
+            } else if (top && right) {
+                resId = R.drawable.vector_drawable_place_top_right;
+            } else if (top && left) {
+                resId = R.drawable.vector_drawable_place_top_left;
+            } else if (bottom && right) {
+                resId = R.drawable.vector_drawable_place_bottom_right;
+            } else if (bottom && left) {
+                resId = R.drawable.vector_drawable_place_bottom_left;
+            }
+        }
+        mMoveTaskButton.setImageResource(resId);
     }
 
     /** Unbinds the bar view from the task */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index bba7682..a55e026 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -132,6 +132,8 @@
 
     /** Reset the transform on a view. */
     public static void reset(View v) {
+        // Cancel any running animations
+        v.animate().cancel();
         v.setTranslationX(0f);
         v.setTranslationY(0f);
         v.setTranslationZ(0f);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 7286907..a82afcf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -59,9 +59,10 @@
     private String mWifiDescription;
     private ArrayList<PhoneState> mPhoneStates = new ArrayList<PhoneState>();
     private int mIconTint = Color.WHITE;
+    private float mDarkIntensity;
 
     ViewGroup mWifiGroup;
-    ImageView mVpn, mWifi, mAirplane, mNoSims;
+    ImageView mVpn, mWifi, mAirplane, mNoSims, mWifiDark, mNoSimsDark;
     View mWifiAirplaneSpacer;
     View mWifiSignalSpacer;
     LinearLayout mMobileSignalGroup;
@@ -115,8 +116,10 @@
         mVpn            = (ImageView) findViewById(R.id.vpn);
         mWifiGroup      = (ViewGroup) findViewById(R.id.wifi_combo);
         mWifi           = (ImageView) findViewById(R.id.wifi_signal);
+        mWifiDark       = (ImageView) findViewById(R.id.wifi_signal_dark);
         mAirplane       = (ImageView) findViewById(R.id.airplane);
         mNoSims         = (ImageView) findViewById(R.id.no_sims);
+        mNoSimsDark     = (ImageView) findViewById(R.id.no_sims_dark);
         mWifiAirplaneSpacer =         findViewById(R.id.wifi_airplane_spacer);
         mWifiSignalSpacer =           findViewById(R.id.wifi_signal_spacer);
         mMobileSignalGroup = (LinearLayout) findViewById(R.id.mobile_signal_group);
@@ -273,6 +276,7 @@
         if (DEBUG) Log.d(TAG, String.format("vpn: %s", mVpnVisible ? "VISIBLE" : "GONE"));
         if (mWifiVisible) {
             mWifi.setImageResource(mWifiStrengthId);
+            mWifiDark.setImageResource(mWifiStrengthId);
             mWifiGroup.setContentDescription(mWifiDescription);
             mWifiGroup.setVisibility(View.VISIBLE);
         } else {
@@ -317,15 +321,17 @@
         }
 
         mNoSims.setVisibility(mNoSimsVisible ? View.VISIBLE : View.GONE);
+        mNoSimsDark.setVisibility(mNoSimsVisible ? View.VISIBLE : View.GONE);
 
         boolean anythingVisible = mNoSimsVisible || mWifiVisible || mIsAirplaneMode
                 || anyMobileVisible || mVpnVisible;
         setPaddingRelative(0, 0, anythingVisible ? mEndPadding : mEndPaddingNothingVisible, 0);
     }
 
-    public void setIconTint(int tint) {
-        boolean changed = tint != mIconTint;
+    public void setIconTint(int tint, float darkIntensity) {
+        boolean changed = tint != mIconTint || darkIntensity != mDarkIntensity;
         mIconTint = tint;
+        mDarkIntensity = darkIntensity;
         if (changed && isAttachedToWindow()) {
             applyIconTint();
         }
@@ -333,14 +339,19 @@
 
     private void applyIconTint() {
         setTint(mVpn, mIconTint);
-        setTint(mWifi, mIconTint);
-        setTint(mNoSims, mIconTint);
         setTint(mAirplane, mIconTint);
+        applyDarkIntensity(mDarkIntensity, mNoSims, mNoSimsDark);
+        applyDarkIntensity(mDarkIntensity, mWifi, mWifiDark);
         for (int i = 0; i < mPhoneStates.size(); i++) {
-            mPhoneStates.get(i).setIconTint(mIconTint);
+            mPhoneStates.get(i).setIconTint(mIconTint, mDarkIntensity);
         }
     }
 
+    private void applyDarkIntensity(float darkIntensity, View lightIcon, View darkIcon) {
+        lightIcon.setAlpha(1 - darkIntensity);
+        darkIcon.setAlpha(darkIntensity);
+    }
+
     private void setTint(ImageView v, int tint) {
         v.setImageTintMode(PorterDuff.Mode.SRC_ATOP);
         v.setImageTintList(ColorStateList.valueOf(tint));
@@ -354,7 +365,7 @@
         private String mMobileDescription, mMobileTypeDescription;
 
         private ViewGroup mMobileGroup;
-        private ImageView mMobile, mMobileType;
+        private ImageView mMobile, mMobileDark, mMobileType;
 
         public PhoneState(int subId, Context context) {
             ViewGroup root = (ViewGroup) LayoutInflater.from(context)
@@ -366,12 +377,14 @@
         public void setViews(ViewGroup root) {
             mMobileGroup    = root;
             mMobile         = (ImageView) root.findViewById(R.id.mobile_signal);
+            mMobileDark     = (ImageView) root.findViewById(R.id.mobile_signal_dark);
             mMobileType     = (ImageView) root.findViewById(R.id.mobile_type);
         }
 
         public boolean apply(boolean isSecondaryIcon) {
             if (mMobileVisible && !mIsAirplaneMode) {
                 mMobile.setImageResource(mMobileStrengthId);
+                mMobileDark.setImageResource(mMobileStrengthId);
                 mMobileType.setImageResource(mMobileTypeId);
                 mMobileGroup.setContentDescription(mMobileTypeDescription
                         + " " + mMobileDescription);
@@ -385,6 +398,8 @@
                     0, 0, 0);
             mMobile.setPaddingRelative(mIsMobileTypeIconWide ? mWideTypeIconStartPadding : 0,
                     0, 0, 0);
+            mMobileDark.setPaddingRelative(mIsMobileTypeIconWide ? mWideTypeIconStartPadding : 0,
+                    0, 0, 0);
 
             if (DEBUG) Log.d(TAG, String.format("mobile: %s sig=%d typ=%d",
                         (mMobileVisible ? "VISIBLE" : "GONE"), mMobileStrengthId, mMobileTypeId));
@@ -401,8 +416,8 @@
             }
         }
 
-        public void setIconTint(int tint) {
-            setTint(mMobile, tint);
+        public void setIconTint(int tint, float darkIntensity) {
+            applyDarkIntensity(darkIntensity, mMobile, mMobileDark);
             setTint(mMobileType, tint);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 0c21b20..a247c8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -336,7 +336,6 @@
     }
 
     public void launchCamera() {
-        mFlashlightController.killFlashlight();
         Intent intent = getCameraIntent();
         boolean wouldLaunchResolverActivity = PreviewInflater.wouldLaunchResolverActivity(
                 mContext, intent, mLockPatternUtils.getCurrentUser());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 2c389fb..f046c63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -49,7 +49,6 @@
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
@@ -225,8 +224,6 @@
     /** Allow some time inbetween the long press for back and recents. */
     private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
 
-    private int mLightModeIconColor;
-
     PhoneStatusBarPolicy mIconPolicy;
 
     // These are no longer handled by the policy, because we need custom strategies for them
@@ -535,7 +532,6 @@
         updateDisplaySize();
         mScrimSrcModeEnabled = mContext.getResources().getBoolean(
                 R.bool.config_status_bar_scrim_behind_use_src);
-        mLightModeIconColor = mContext.getColor(R.color.light_mode_icon_color);
 
         super.start(); // calls createAndAddWindows()
 
@@ -2324,9 +2320,7 @@
                 boolean allowLight = isTransparentBar && !mBatteryController.isPowerSave();
                 boolean light = (vis & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0;
 
-                mIconController.setIconTint(
-                        (allowLight && light) ? mLightModeIconColor : Color.WHITE);
-
+                mIconController.setIconsDark(allowLight && light);
             }
             // restore the recents bit
             if (wasRecentsVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 2236aae..1938c6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -199,7 +199,7 @@
         int volumeIconId = 0;
         String volumeDescription = null;
 
-        if (DndTile.isVisible(mContext)) {
+        if (DndTile.isVisible(mContext) || DndTile.isCombinedIcon(mContext)) {
             zenVisible = mZen != Global.ZEN_MODE_OFF;
             zenIconId = R.drawable.stat_sys_dnd;
             zenDescription = mContext.getString(R.string.quick_settings_dnd_label);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index c49f620..45da297 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -16,11 +16,11 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.animation.ArgbEvaluator;
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.Color;
-import android.graphics.PorterDuff;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.SystemClock;
@@ -74,12 +74,16 @@
     private int mIconHPadding;
 
     private int mIconTint = Color.WHITE;
+    private float mDarkIntensity;
 
     private boolean mTransitionPending;
     private boolean mTintChangePending;
-    private int mPendingIconTint;
+    private float mPendingDarkIntensity;
     private ValueAnimator mTintAnimator;
 
+    private int mDarkModeIconColorSingleTone;
+    private int mLightModeIconColorSingleTone;
+
     private final Handler mHandler;
     private boolean mTransitionDeferring;
     private long mTransitionDeferringStartTime;
@@ -111,6 +115,8 @@
                 android.R.interpolator.linear_out_slow_in);
         mFastOutSlowIn = AnimationUtils.loadInterpolator(mContext,
                 android.R.interpolator.fast_out_slow_in);
+        mDarkModeIconColorSingleTone = context.getColor(R.color.dark_mode_icon_color_single_tone);
+        mLightModeIconColorSingleTone = context.getColor(R.color.light_mode_icon_color_single_tone);
         mHandler = new Handler();
         updateResources();
     }
@@ -296,30 +302,31 @@
         }
     }
 
-    public void setIconTint(int tint) {
+    public void setIconsDark(boolean dark) {
         if (mTransitionPending) {
-            deferIconTintChange(tint);
+            deferIconTintChange(dark ? 1.0f : 0.0f);
         } else if (mTransitionDeferring) {
-            animateIconTint(tint,
+            animateIconTint(dark ? 1.0f : 0.0f,
                     Math.max(0, mTransitionDeferringStartTime - SystemClock.uptimeMillis()),
                     mTransitionDeferringDuration);
         } else {
-            animateIconTint(tint, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION);
+            animateIconTint(dark ? 1.0f : 0.0f, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION);
         }
     }
 
-    private void animateIconTint(int targetTint, long delay, long duration) {
+    private void animateIconTint(float targetDarkIntensity, long delay,
+            long duration) {
         if (mTintAnimator != null) {
             mTintAnimator.cancel();
         }
-        if (mIconTint == targetTint) {
+        if (mDarkIntensity == targetDarkIntensity) {
             return;
         }
-        mTintAnimator = ValueAnimator.ofArgb(mIconTint, targetTint);
+        mTintAnimator = ValueAnimator.ofFloat(mDarkIntensity, targetDarkIntensity);
         mTintAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
-                setIconTintInternal((Integer) animation.getAnimatedValue());
+                setIconTintInternal((Float) animation.getAnimatedValue());
             }
         });
         mTintAnimator.setDuration(duration);
@@ -327,17 +334,20 @@
         mTintAnimator.setInterpolator(mFastOutSlowIn);
         mTintAnimator.start();
     }
-    private void setIconTintInternal(int tint) {
-        mIconTint = tint;
+
+    private void setIconTintInternal(float darkIntensity) {
+        mDarkIntensity = darkIntensity;
+        mIconTint = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity,
+                mLightModeIconColorSingleTone, mDarkModeIconColorSingleTone);
         applyIconTint();
     }
 
-    private void deferIconTintChange(int tint) {
-        if (mTintChangePending && tint == mPendingIconTint) {
+    private void deferIconTintChange(float darkIntensity) {
+        if (mTintChangePending && darkIntensity == mPendingDarkIntensity) {
             return;
         }
         mTintChangePending = true;
-        mPendingIconTint = tint;
+        mPendingDarkIntensity = darkIntensity;
     }
 
     private void applyIconTint() {
@@ -345,9 +355,9 @@
             StatusBarIconView v = (StatusBarIconView) mStatusIcons.getChildAt(i);
             v.setImageTintList(ColorStateList.valueOf(mIconTint));
         }
-        mSignalCluster.setIconTint(mIconTint);
+        mSignalCluster.setIconTint(mIconTint, mDarkIntensity);
         mMoreIcon.setImageTintList(ColorStateList.valueOf(mIconTint));
-        mBatteryMeterView.setIconTint(mIconTint);
+        mBatteryMeterView.setDarkIntensity(mDarkIntensity);
         mClock.setTextColor(mIconTint);
         applyNotificationIconsTint();
     }
@@ -358,7 +368,6 @@
             boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L));
             boolean colorize = !isPreL || isGrayscale(v);
             if (colorize) {
-                v.setImageTintMode(PorterDuff.Mode.SRC_ATOP);
                 v.setImageTintList(ColorStateList.valueOf(mIconTint));
             }
         }
@@ -381,7 +390,7 @@
     public void appTransitionCancelled() {
         if (mTransitionPending && mTintChangePending) {
             mTintChangePending = false;
-            animateIconTint(mPendingIconTint, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION);
+            animateIconTint(mPendingDarkIntensity, 0 /* delay */, DEFAULT_TINT_ANIMATION_DURATION);
         }
         mTransitionPending = false;
     }
@@ -389,7 +398,7 @@
     public void appTransitionStarting(long startTime, long duration) {
         if (mTransitionPending && mTintChangePending) {
             mTintChangePending = false;
-            animateIconTint(mPendingIconTint,
+            animateIconTint(mPendingDarkIntensity,
                     Math.max(0, startTime - SystemClock.uptimeMillis()),
                     duration);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
index 5ef345b..65cd268 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
@@ -125,7 +125,7 @@
         }
 
         @Override
-        public void onFingerprintRecognized(int userId) {
+        public void onFingerprintAuthenticated(int userId) {
             update(false /* updateAlways */);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
index c9ba8f6..cd1914c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
@@ -17,20 +17,14 @@
 package com.android.systemui.statusbar.policy;
 
 import android.content.Context;
-import android.graphics.SurfaceTexture;
 import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraManager;
-import android.hardware.camera2.CameraMetadata;
-import android.hardware.camera2.CaptureRequest;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Process;
+import android.text.TextUtils;
 import android.util.Log;
-import android.util.Size;
-import android.view.Surface;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -44,7 +38,7 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final int DISPATCH_ERROR = 0;
-    private static final int DISPATCH_OFF = 1;
+    private static final int DISPATCH_CHANGED = 1;
     private static final int DISPATCH_AVAILABILITY_CHANGED = 2;
 
     private final CameraManager mCameraManager;
@@ -57,52 +51,50 @@
     /** Lock on {@code this} when accessing */
     private boolean mFlashlightEnabled;
 
-    private String mCameraId;
-    private boolean mCameraAvailable;
-    private CameraDevice mCameraDevice;
-    private CaptureRequest mFlashlightRequest;
-    private CameraCaptureSession mSession;
-    private SurfaceTexture mSurfaceTexture;
-    private Surface mSurface;
+    private final String mCameraId;
+    private boolean mTorchAvailable;
 
     public FlashlightController(Context mContext) {
         mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
-        initialize();
-    }
 
-    public void initialize() {
+        String cameraId = null;
         try {
-            mCameraId = getCameraId();
+            cameraId = getCameraId();
         } catch (Throwable e) {
             Log.e(TAG, "Couldn't initialize.", e);
             return;
+        } finally {
+            mCameraId = cameraId;
         }
 
         if (mCameraId != null) {
             ensureHandler();
-            mCameraManager.registerAvailabilityCallback(mAvailabilityCallback, mHandler);
+            mCameraManager.registerTorchCallback(mTorchCallback, mHandler);
         }
     }
 
-    public synchronized void setFlashlight(boolean enabled) {
-        if (mFlashlightEnabled != enabled) {
-            mFlashlightEnabled = enabled;
-            postUpdateFlashlight();
-        }
-    }
-
-    public void killFlashlight() {
-        boolean enabled;
+    public void setFlashlight(boolean enabled) {
+        boolean pendingError = false;
         synchronized (this) {
-            enabled = mFlashlightEnabled;
+            if (mFlashlightEnabled != enabled) {
+                mFlashlightEnabled = enabled;
+                try {
+                    mCameraManager.setTorchMode(mCameraId, enabled);
+                } catch (CameraAccessException e) {
+                    Log.e(TAG, "Couldn't set torch mode", e);
+                    mFlashlightEnabled = false;
+                    pendingError = true;
+                }
+            }
         }
-        if (enabled) {
-            mHandler.post(mKillFlashlightRunnable);
+        dispatchModeChanged(mFlashlightEnabled);
+        if (pendingError) {
+            dispatchError();
         }
     }
 
     public synchronized boolean isAvailable() {
-        return mCameraAvailable;
+        return mTorchAvailable;
     }
 
     public void addListener(FlashlightListener l) {
@@ -126,42 +118,6 @@
         }
     }
 
-    private void startDevice() throws CameraAccessException {
-        mCameraManager.openCamera(getCameraId(), mCameraListener, mHandler);
-    }
-
-    private void startSession() throws CameraAccessException {
-        mSurfaceTexture = new SurfaceTexture(false);
-        Size size = getSmallestSize(mCameraDevice.getId());
-        mSurfaceTexture.setDefaultBufferSize(size.getWidth(), size.getHeight());
-        mSurface = new Surface(mSurfaceTexture);
-        ArrayList<Surface> outputs = new ArrayList<>(1);
-        outputs.add(mSurface);
-        mCameraDevice.createCaptureSession(outputs, mSessionListener, mHandler);
-    }
-
-    private Size getSmallestSize(String cameraId) throws CameraAccessException {
-        Size[] outputSizes = mCameraManager.getCameraCharacteristics(cameraId)
-                .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
-                .getOutputSizes(SurfaceTexture.class);
-        if (outputSizes == null || outputSizes.length == 0) {
-            throw new IllegalStateException(
-                    "Camera " + cameraId + "doesn't support any outputSize.");
-        }
-        Size chosen = outputSizes[0];
-        for (Size s : outputSizes) {
-            if (chosen.getWidth() >= s.getWidth() && chosen.getHeight() >= s.getHeight()) {
-                chosen = s;
-            }
-        }
-        return chosen;
-    }
-
-    private void postUpdateFlashlight() {
-        ensureHandler();
-        mHandler.post(mUpdateFlashlightRunnable);
-    }
-
     private String getCameraId() throws CameraAccessException {
         String[] ids = mCameraManager.getCameraIdList();
         for (String id : ids) {
@@ -176,70 +132,12 @@
         return null;
     }
 
-    private void updateFlashlight(boolean forceDisable) {
-        try {
-            boolean enabled;
-            synchronized (this) {
-                enabled = mFlashlightEnabled && !forceDisable;
-            }
-            if (enabled) {
-                if (mCameraDevice == null) {
-                    startDevice();
-                    return;
-                }
-                if (mSession == null) {
-                    startSession();
-                    return;
-                }
-                if (mFlashlightRequest == null) {
-                    CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(
-                            CameraDevice.TEMPLATE_PREVIEW);
-                    builder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_TORCH);
-                    builder.addTarget(mSurface);
-                    CaptureRequest request = builder.build();
-                    mSession.capture(request, null, mHandler);
-                    mFlashlightRequest = request;
-                }
-            } else {
-                if (mCameraDevice != null) {
-                    mCameraDevice.close();
-                    teardown();
-                }
-            }
-
-        } catch (CameraAccessException|IllegalStateException|UnsupportedOperationException e) {
-            Log.e(TAG, "Error in updateFlashlight", e);
-            handleError();
-        }
-    }
-
-    private void teardown() {
-        mCameraDevice = null;
-        mSession = null;
-        mFlashlightRequest = null;
-        if (mSurface != null) {
-            mSurface.release();
-            mSurfaceTexture.release();
-        }
-        mSurface = null;
-        mSurfaceTexture = null;
-    }
-
-    private void handleError() {
-        synchronized (this) {
-            mFlashlightEnabled = false;
-        }
-        dispatchError();
-        dispatchOff();
-        updateFlashlight(true /* forceDisable */);
-    }
-
-    private void dispatchOff() {
-        dispatchListeners(DISPATCH_OFF, false /* argument (ignored) */);
+    private void dispatchModeChanged(boolean enabled) {
+        dispatchListeners(DISPATCH_CHANGED, enabled);
     }
 
     private void dispatchError() {
-        dispatchListeners(DISPATCH_ERROR, false /* argument (ignored) */);
+        dispatchListeners(DISPATCH_CHANGED, false /* argument (ignored) */);
     }
 
     private void dispatchAvailabilityChanged(boolean available) {
@@ -255,8 +153,8 @@
                 if (l != null) {
                     if (message == DISPATCH_ERROR) {
                         l.onFlashlightError();
-                    } else if (message == DISPATCH_OFF) {
-                        l.onFlashlightOff();
+                    } else if (message == DISPATCH_CHANGED) {
+                        l.onFlashlightChanged(argument);
                     } else if (message == DISPATCH_AVAILABILITY_CHANGED) {
                         l.onFlashlightAvailabilityChanged(argument);
                     }
@@ -279,106 +177,57 @@
         }
     }
 
-    private final CameraDevice.StateListener mCameraListener = new CameraDevice.StateListener() {
-        @Override
-        public void onOpened(CameraDevice camera) {
-            mCameraDevice = camera;
-            postUpdateFlashlight();
-        }
+    private final CameraManager.TorchCallback mTorchCallback =
+            new CameraManager.TorchCallback() {
 
         @Override
-        public void onDisconnected(CameraDevice camera) {
-            if (mCameraDevice == camera) {
-                dispatchOff();
-                teardown();
-            }
-        }
-
-        @Override
-        public void onError(CameraDevice camera, int error) {
-            Log.e(TAG, "Camera error: camera=" + camera + " error=" + error);
-            if (camera == mCameraDevice || mCameraDevice == null) {
-                handleError();
-            }
-        }
-    };
-
-    private final CameraCaptureSession.StateListener mSessionListener =
-            new CameraCaptureSession.StateListener() {
-        @Override
-        public void onConfigured(CameraCaptureSession session) {
-            if (session.getDevice() == mCameraDevice) {
-                mSession = session;
-            } else {
-                session.close();
-            }
-            postUpdateFlashlight();
-        }
-
-        @Override
-        public void onConfigureFailed(CameraCaptureSession session) {
-            Log.e(TAG, "Configure failed.");
-            if (mSession == null || mSession == session) {
-                handleError();
-            }
-        }
-    };
-
-    private final Runnable mUpdateFlashlightRunnable = new Runnable() {
-        @Override
-        public void run() {
-            updateFlashlight(false /* forceDisable */);
-        }
-    };
-
-    private final Runnable mKillFlashlightRunnable = new Runnable() {
-        @Override
-        public void run() {
-            synchronized (this) {
-                mFlashlightEnabled = false;
-            }
-            updateFlashlight(true /* forceDisable */);
-            dispatchOff();
-        }
-    };
-
-    private final CameraManager.AvailabilityCallback mAvailabilityCallback =
-            new CameraManager.AvailabilityCallback() {
-        @Override
-        public void onCameraAvailable(String cameraId) {
-            if (DEBUG) Log.d(TAG, "onCameraAvailable(" + cameraId + ")");
-            if (cameraId.equals(mCameraId)) {
-                setCameraAvailable(true);
-            }
-        }
-
-        @Override
-        public void onCameraUnavailable(String cameraId) {
-            if (DEBUG) Log.d(TAG, "onCameraUnavailable(" + cameraId + ")");
-            if (cameraId.equals(mCameraId)) {
+        public void onTorchModeUnavailable(String cameraId) {
+            if (TextUtils.equals(cameraId, mCameraId)) {
                 setCameraAvailable(false);
             }
         }
 
+        @Override
+        public void onTorchModeChanged(String cameraId, boolean enabled) {
+            if (TextUtils.equals(cameraId, mCameraId)) {
+                setCameraAvailable(true);
+                setTorchMode(enabled);
+            }
+        }
+
         private void setCameraAvailable(boolean available) {
             boolean changed;
             synchronized (FlashlightController.this) {
-                changed = mCameraAvailable != available;
-                mCameraAvailable = available;
+                changed = mTorchAvailable != available;
+                mTorchAvailable = available;
             }
             if (changed) {
                 if (DEBUG) Log.d(TAG, "dispatchAvailabilityChanged(" + available + ")");
                 dispatchAvailabilityChanged(available);
             }
         }
+
+        private void setTorchMode(boolean enabled) {
+            boolean changed;
+            synchronized (FlashlightController.this) {
+                changed = mFlashlightEnabled != enabled;
+                mFlashlightEnabled = enabled;
+            }
+            if (changed) {
+                if (DEBUG) Log.d(TAG, "dispatchModeChanged(" + enabled + ")");
+                dispatchModeChanged(enabled);
+            }
+        }
     };
 
     public interface FlashlightListener {
 
         /**
-         * Called when the flashlight turns off unexpectedly.
+         * Called when the flashlight was turned off or on.
+         * @param enabled true if the flashlight is currently turned on.
          */
-        void onFlashlightOff();
+        void onFlashlightChanged(boolean enabled);
+
 
         /**
          * Called when there is an error that turns the flashlight off.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 2fbb812..f0dd943 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -190,7 +190,8 @@
             NetworkCapabilities networkCapabilities =
                     mConnectivityManager.getNetworkCapabilities(network);
             if (DEBUG) Log.d(TAG, "onAvailable " + network.netId + " : " + networkCapabilities);
-            if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
+            if (networkCapabilities != null &&
+                    networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
                 setCurrentNetid(network.netId);
             }
         };
diff --git a/packages/SystemUI/src/com/android/systemui/volume/D.java b/packages/SystemUI/src/com/android/systemui/volume/D.java
new file mode 100644
index 0000000..db7c853
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/D.java
@@ -0,0 +1,23 @@
+/*
+ * 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.volume;
+
+import android.util.Log;
+
+class D {
+    public static boolean BUG = Log.isLoggable("volume", Log.DEBUG);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java
new file mode 100644
index 0000000..b20e39c7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java
@@ -0,0 +1,189 @@
+/*
+ * 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.volume;
+
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.provider.Settings.Global;
+import android.util.Log;
+
+import com.android.systemui.volume.VolumeDialogController.State;
+
+import java.util.Arrays;
+
+/**
+ *  Interesting events related to the volume.
+ */
+public class Events {
+    private static final String TAG = Util.logTag(Events.class);
+
+    public static final int EVENT_SHOW_DIALOG = 0;  // (reason|int) (keyguard|bool)
+    public static final int EVENT_DISMISS_DIALOG = 1; // (reason|int)
+    public static final int EVENT_ACTIVE_STREAM_CHANGED = 2; // (stream|int)
+    public static final int EVENT_EXPAND = 3; // (expand|bool)
+    public static final int EVENT_KEY = 4;
+    public static final int EVENT_COLLECTION_STARTED = 5;
+    public static final int EVENT_COLLECTION_STOPPED = 6;
+    public static final int EVENT_ICON_CLICK = 7; // (stream|int) (icon_state|int)
+    public static final int EVENT_SETTINGS_CLICK = 8;
+    public static final int EVENT_TOUCH_LEVEL_CHANGED = 9; // (stream|int) (level|int)
+    public static final int EVENT_LEVEL_CHANGED = 10; // (stream|int) (level|int)
+    public static final int EVENT_INTERNAL_RINGER_MODE_CHANGED = 11; // (mode|int)
+    public static final int EVENT_EXTERNAL_RINGER_MODE_CHANGED = 12; // (mode|int)
+    public static final int EVENT_ZEN_MODE_CHANGED = 13; // (mode|int)
+    public static final int EVENT_SUPPRESSOR_CHANGED = 14;  // (component|string) (name|string)
+    public static final int EVENT_MUTE_CHANGED = 15;  // (stream|int) (muted|bool)
+
+    private static final String[] EVENT_TAGS = {
+        "show_dialog",
+        "dismiss_dialog",
+        "active_stream_changed",
+        "expand",
+        "key",
+        "collection_started",
+        "collection_stopped",
+        "icon_click",
+        "settings_click",
+        "touch_level_changed",
+        "level_changed",
+        "internal_ringer_mode_changed",
+        "external_ringer_mode_changed",
+        "zen_mode_changed",
+        "suppressor_changed",
+        "mute_changed",
+    };
+
+    public static final int DISMISS_REASON_UNKNOWN = 0;
+    public static final int DISMISS_REASON_TOUCH_OUTSIDE = 1;
+    public static final int DISMISS_REASON_VOLUME_CONTROLLER = 2;
+    public static final int DISMISS_REASON_TIMEOUT = 3;
+    public static final int DISMISS_REASON_SCREEN_OFF = 4;
+    public static final int DISMISS_REASON_SETTINGS_CLICKED = 5;
+    public static final int DISMISS_REASON_DONE_CLICKED = 6;
+    public static final String[] DISMISS_REASONS = {
+        "unknown",
+        "touch_outside",
+        "volume_controller",
+        "timeout",
+        "screen_off",
+        "settings_clicked",
+        "done_clicked",
+    };
+
+    public static final int SHOW_REASON_UNKNOWN = 0;
+    public static final int SHOW_REASON_VOLUME_CHANGED = 1;
+    public static final int SHOW_REASON_REMOTE_VOLUME_CHANGED = 2;
+    public static final String[] SHOW_REASONS = {
+        "unknown",
+        "volume_changed",
+        "remote_volume_changed"
+    };
+
+    public static final int ICON_STATE_UNKNOWN = 0;
+    public static final int ICON_STATE_UNMUTE = 1;
+    public static final int ICON_STATE_MUTE = 2;
+    public static final int ICON_STATE_VIBRATE = 3;
+
+    public static Callback sCallback;
+
+    public static void writeEvent(int tag, Object... list) {
+        final long time = System.currentTimeMillis();
+        final StringBuilder sb = new StringBuilder("writeEvent ").append(EVENT_TAGS[tag]);
+        if (list != null && list.length > 0) {
+            sb.append(" ");
+            switch (tag) {
+                case EVENT_SHOW_DIALOG:
+                    sb.append(SHOW_REASONS[(Integer) list[0]]).append(" keyguard=").append(list[1]);
+                    break;
+                case EVENT_EXPAND:
+                    sb.append(list[0]);
+                    break;
+                case EVENT_DISMISS_DIALOG:
+                    sb.append(DISMISS_REASONS[(Integer) list[0]]);
+                    break;
+                case EVENT_ACTIVE_STREAM_CHANGED:
+                    sb.append(AudioSystem.streamToString((Integer) list[0]));
+                    break;
+                case EVENT_ICON_CLICK:
+                    sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
+                            .append(iconStateToString((Integer) list[1]));
+                    break;
+                case EVENT_TOUCH_LEVEL_CHANGED:
+                case EVENT_LEVEL_CHANGED:
+                case EVENT_MUTE_CHANGED:
+                    sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
+                            .append(list[1]);
+                    break;
+                case EVENT_INTERNAL_RINGER_MODE_CHANGED:
+                case EVENT_EXTERNAL_RINGER_MODE_CHANGED:
+                    sb.append(ringerModeToString((Integer) list[0]));
+                    break;
+                case EVENT_ZEN_MODE_CHANGED:
+                    sb.append(zenModeToString((Integer) list[0]));
+                    break;
+                case EVENT_SUPPRESSOR_CHANGED:
+                    sb.append(list[0]).append(' ').append(list[1]);
+                    break;
+                default:
+                    sb.append(Arrays.asList(list));
+                    break;
+            }
+        }
+        Log.i(TAG, sb.toString());
+        if (sCallback != null) {
+            sCallback.writeEvent(time, tag, list);
+        }
+    }
+
+    public static void writeState(long time, State state) {
+        if (sCallback != null) {
+            sCallback.writeState(time, state);
+        }
+    }
+
+    private static String iconStateToString(int iconState) {
+        switch (iconState) {
+            case ICON_STATE_UNMUTE: return "unmute";
+            case ICON_STATE_MUTE: return "mute";
+            case ICON_STATE_VIBRATE: return "vibrate";
+            default: return "unknown_state_" + iconState;
+        }
+    }
+
+    private static String ringerModeToString(int ringerMode) {
+        switch (ringerMode) {
+            case AudioManager.RINGER_MODE_SILENT: return "silent";
+            case AudioManager.RINGER_MODE_VIBRATE: return "vibrate";
+            case AudioManager.RINGER_MODE_NORMAL: return "normal";
+            default: return "unknown";
+        }
+    }
+
+    private static String zenModeToString(int zenMode) {
+        switch (zenMode) {
+            case Global.ZEN_MODE_OFF: return "off";
+            case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: return "important_interruptions";
+            case Global.ZEN_MODE_NO_INTERRUPTIONS: return "no_interruptions";
+            default: return "unknown";
+        }
+    }
+
+    public interface Callback {
+        void writeEvent(long time, int tag, Object[] list);
+        void writeState(long time, State state);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/MediaSessions.java b/packages/SystemUI/src/com/android/systemui/volume/MediaSessions.java
new file mode 100644
index 0000000..712ea27
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/MediaSessions.java
@@ -0,0 +1,378 @@
+/*
+ * 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.volume;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.media.IRemoteVolumeController;
+import android.media.MediaMetadata;
+import android.media.session.ISessionController;
+import android.media.session.MediaController;
+import android.media.session.MediaController.PlaybackInfo;
+import android.media.session.MediaSession.QueueItem;
+import android.media.session.MediaSession.Token;
+import android.media.session.MediaSessionManager;
+import android.media.session.MediaSessionManager.OnActiveSessionsChangedListener;
+import android.media.session.PlaybackState;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Convenience client for all media session updates.  Provides a callback interface for events
+ * related to remote media sessions.
+ */
+public class MediaSessions {
+    private static final String TAG = Util.logTag(MediaSessions.class);
+
+    private static final boolean USE_SERVICE_LABEL = false;
+
+    private final Context mContext;
+    private final H mHandler;
+    private final MediaSessionManager mMgr;
+    private final Map<Token, MediaControllerRecord> mRecords = new HashMap<>();
+    private final Callbacks mCallbacks;
+
+    private boolean mInit;
+
+    public MediaSessions(Context context, Looper looper, Callbacks callbacks) {
+        mContext = context;
+        mHandler = new H(looper);
+        mMgr = (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
+        mCallbacks = callbacks;
+    }
+
+    public void dump(PrintWriter writer) {
+        writer.println(getClass().getSimpleName() + " state:");
+        writer.print("  mInit: "); writer.println(mInit);
+        writer.print("  mRecords.size: "); writer.println(mRecords.size());
+        int i = 0;
+        for (MediaControllerRecord r : mRecords.values()) {
+            dump(++i, writer, r.controller);
+        }
+    }
+
+    public void init() {
+        if (D.BUG) Log.d(TAG, "init");
+        // will throw if no permission
+        mMgr.addOnActiveSessionsChangedListener(mSessionsListener, null, mHandler);
+        mInit = true;
+        postUpdateSessions();
+        mMgr.setRemoteVolumeController(mRvc);
+    }
+
+    protected void postUpdateSessions() {
+        if (!mInit) return;
+        mHandler.sendEmptyMessage(H.UPDATE_SESSIONS);
+    }
+
+    public void destroy() {
+        if (D.BUG) Log.d(TAG, "destroy");
+        mInit = false;
+        mMgr.removeOnActiveSessionsChangedListener(mSessionsListener);
+    }
+
+    public void setVolume(Token token, int level) {
+        final MediaControllerRecord r = mRecords.get(token);
+        if (r == null) {
+            Log.w(TAG, "setVolume: No record found for token " + token);
+            return;
+        }
+        if (D.BUG) Log.d(TAG, "Setting level to " + level);
+        r.controller.setVolumeTo(level, 0);
+    }
+
+    private void onRemoteVolumeChangedH(ISessionController session, int flags) {
+        final MediaController controller = new MediaController(mContext, session);
+        if (D.BUG) Log.d(TAG, "remoteVolumeChangedH " + controller.getPackageName() + " "
+                + Util.audioManagerFlagsToString(flags));
+        final Token token = controller.getSessionToken();
+        mCallbacks.onRemoteVolumeChanged(token, flags);
+    }
+
+    private void onUpdateRemoteControllerH(ISessionController session) {
+        final MediaController controller = session != null ? new MediaController(mContext, session)
+                : null;
+        final String pkg = controller != null ? controller.getPackageName() : null;
+        if (D.BUG) Log.d(TAG, "updateRemoteControllerH " + pkg);
+        // this may be our only indication that a remote session is changed, refresh
+        postUpdateSessions();
+    }
+
+    protected void onActiveSessionsUpdatedH(List<MediaController> controllers) {
+        if (D.BUG) Log.d(TAG, "onActiveSessionsUpdatedH n=" + controllers.size());
+        final Set<Token> toRemove = new HashSet<Token>(mRecords.keySet());
+        for (MediaController controller : controllers) {
+            final Token token = controller.getSessionToken();
+            final PlaybackInfo pi = controller.getPlaybackInfo();
+            toRemove.remove(token);
+            if (!mRecords.containsKey(token)) {
+                final MediaControllerRecord r = new MediaControllerRecord(controller);
+                r.name = getControllerName(controller);
+                mRecords.put(token, r);
+                controller.registerCallback(r, mHandler);
+            }
+            final MediaControllerRecord r = mRecords.get(token);
+            final boolean remote = isRemote(pi);
+            if (remote) {
+                updateRemoteH(token, r.name, pi);
+                r.sentRemote = true;
+            }
+        }
+        for (Token t : toRemove) {
+            final MediaControllerRecord r = mRecords.get(t);
+            r.controller.unregisterCallback(r);
+            mRecords.remove(t);
+            if (D.BUG) Log.d(TAG, "Removing " + r.name + " sentRemote=" + r.sentRemote);
+            if (r.sentRemote) {
+                mCallbacks.onRemoteRemoved(t);
+                r.sentRemote = false;
+            }
+        }
+    }
+
+    private static boolean isRemote(PlaybackInfo pi) {
+        return pi != null && pi.getPlaybackType() == PlaybackInfo.PLAYBACK_TYPE_REMOTE;
+    }
+
+    protected String getControllerName(MediaController controller) {
+        final PackageManager pm = mContext.getPackageManager();
+        final String pkg = controller.getPackageName();
+        try {
+            if (USE_SERVICE_LABEL) {
+                final List<ResolveInfo> ris = pm.queryIntentServices(
+                        new Intent("android.media.MediaRouteProviderService").setPackage(pkg), 0);
+                if (ris != null) {
+                    for (ResolveInfo ri : ris) {
+                        if (ri.serviceInfo == null) continue;
+                        if (pkg.equals(ri.serviceInfo.packageName)) {
+                            final String serviceLabel =
+                                    Objects.toString(ri.serviceInfo.loadLabel(pm), "").trim();
+                            if (serviceLabel.length() > 0) {
+                                return serviceLabel;
+                            }
+                        }
+                    }
+                }
+            }
+            final ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
+            final String appLabel = Objects.toString(ai.loadLabel(pm), "").trim();
+            if (appLabel.length() > 0) {
+                return appLabel;
+            }
+        } catch (NameNotFoundException e) { }
+        return pkg;
+    }
+
+    private void updateRemoteH(Token token, String name, PlaybackInfo pi) {
+        if (mCallbacks != null) {
+            mCallbacks.onRemoteUpdate(token, name, pi);
+        }
+    }
+
+    private static void dump(int n, PrintWriter writer, MediaController c) {
+        writer.println("  Controller " + n + ": " + c.getPackageName());
+        final Bundle extras = c.getExtras();
+        final long flags = c.getFlags();
+        final MediaMetadata mm = c.getMetadata();
+        final PlaybackInfo pi = c.getPlaybackInfo();
+        final PlaybackState playbackState = c.getPlaybackState();
+        final List<QueueItem> queue = c.getQueue();
+        final CharSequence queueTitle = c.getQueueTitle();
+        final int ratingType = c.getRatingType();
+        final PendingIntent sessionActivity = c.getSessionActivity();
+
+        writer.println("    PlaybackState: " + Util.playbackStateToString(playbackState));
+        writer.println("    PlaybackInfo: " + Util.playbackInfoToString(pi));
+        if (mm != null) {
+            writer.println("  MediaMetadata.desc=" + mm.getDescription());
+        }
+        writer.println("    RatingType: " + ratingType);
+        writer.println("    Flags: " + flags);
+        if (extras != null) {
+            writer.println("    Extras:");
+            for (String key : extras.keySet()) {
+                writer.println("      " + key + "=" + extras.get(key));
+            }
+        }
+        if (queueTitle != null) {
+            writer.println("    QueueTitle: " + queueTitle);
+        }
+        if (queue != null && !queue.isEmpty()) {
+            writer.println("    Queue:");
+            for (QueueItem qi : queue) {
+                writer.println("      " + qi);
+            }
+        }
+        if (pi != null) {
+            writer.println("    sessionActivity: " + sessionActivity);
+        }
+    }
+
+    public static void dumpMediaSessions(Context context) {
+        final MediaSessionManager mgr = (MediaSessionManager) context
+                .getSystemService(Context.MEDIA_SESSION_SERVICE);
+        try {
+            final List<MediaController> controllers = mgr.getActiveSessions(null);
+            final int N = controllers.size();
+            if (D.BUG) Log.d(TAG, N + " controllers");
+            for (int i = 0; i < N; i++) {
+                final StringWriter sw = new StringWriter();
+                final PrintWriter pw = new PrintWriter(sw, true);
+                dump(i + 1, pw, controllers.get(i));
+                if (D.BUG) Log.d(TAG, sw.toString());
+            }
+        } catch (SecurityException e) {
+            Log.w(TAG, "Not allowed to get sessions", e);
+        }
+    }
+
+    private final class MediaControllerRecord extends MediaController.Callback {
+        private final MediaController controller;
+
+        private boolean sentRemote;
+        private String name;
+
+        private MediaControllerRecord(MediaController controller) {
+            this.controller = controller;
+        }
+
+        private String cb(String method) {
+            return method + " " + controller.getPackageName() + " ";
+        }
+
+        @Override
+        public void onAudioInfoChanged(PlaybackInfo info) {
+            if (D.BUG) Log.d(TAG, cb("onAudioInfoChanged") + Util.playbackInfoToString(info)
+                    + " sentRemote=" + sentRemote);
+            final boolean remote = isRemote(info);
+            if (!remote && sentRemote) {
+                mCallbacks.onRemoteRemoved(controller.getSessionToken());
+                sentRemote = false;
+            } else if (remote) {
+                updateRemoteH(controller.getSessionToken(), name, info);
+                sentRemote = true;
+            }
+        }
+
+        @Override
+        public void onExtrasChanged(Bundle extras) {
+            if (D.BUG) Log.d(TAG, cb("onExtrasChanged") + extras);
+        }
+
+        @Override
+        public void onMetadataChanged(MediaMetadata metadata) {
+            if (D.BUG) Log.d(TAG, cb("onMetadataChanged") + Util.mediaMetadataToString(metadata));
+        }
+
+        @Override
+        public void onPlaybackStateChanged(PlaybackState state) {
+            if (D.BUG) Log.d(TAG, cb("onPlaybackStateChanged") + Util.playbackStateToString(state));
+        }
+
+        @Override
+        public void onQueueChanged(List<QueueItem> queue) {
+            if (D.BUG) Log.d(TAG, cb("onQueueChanged") + queue);
+        }
+
+        @Override
+        public void onQueueTitleChanged(CharSequence title) {
+            if (D.BUG) Log.d(TAG, cb("onQueueTitleChanged") + title);
+        }
+
+        @Override
+        public void onSessionDestroyed() {
+            if (D.BUG) Log.d(TAG, cb("onSessionDestroyed"));
+        }
+
+        @Override
+        public void onSessionEvent(String event, Bundle extras) {
+            if (D.BUG) Log.d(TAG, cb("onSessionEvent") + "event=" + event + " extras=" + extras);
+        }
+    }
+
+    private final OnActiveSessionsChangedListener mSessionsListener =
+            new OnActiveSessionsChangedListener() {
+        @Override
+        public void onActiveSessionsChanged(List<MediaController> controllers) {
+            onActiveSessionsUpdatedH(controllers);
+        }
+    };
+
+    private final IRemoteVolumeController mRvc = new IRemoteVolumeController.Stub() {
+        @Override
+        public void remoteVolumeChanged(ISessionController session, int flags)
+                throws RemoteException {
+            mHandler.obtainMessage(H.REMOTE_VOLUME_CHANGED, flags, 0, session).sendToTarget();
+        }
+
+        @Override
+        public void updateRemoteController(final ISessionController session)
+                throws RemoteException {
+            mHandler.obtainMessage(H.UPDATE_REMOTE_CONTROLLER, session).sendToTarget();
+        }
+    };
+
+    private final class H extends Handler {
+        private static final int UPDATE_SESSIONS = 1;
+        private static final int REMOTE_VOLUME_CHANGED = 2;
+        private static final int UPDATE_REMOTE_CONTROLLER = 3;
+
+        private H(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case UPDATE_SESSIONS:
+                    onActiveSessionsUpdatedH(mMgr.getActiveSessions(null));
+                    break;
+                case REMOTE_VOLUME_CHANGED:
+                    onRemoteVolumeChangedH((ISessionController) msg.obj, msg.arg1);
+                    break;
+                case UPDATE_REMOTE_CONTROLLER:
+                    onUpdateRemoteControllerH((ISessionController) msg.obj);
+                    break;
+            }
+        }
+    }
+
+    public interface Callbacks {
+        void onRemoteUpdate(Token token, String name, PlaybackInfo pi);
+        void onRemoteRemoved(Token t);
+        void onRemoteVolumeChanged(Token token, int flags);
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Prefs.java b/packages/SystemUI/src/com/android/systemui/volume/Prefs.java
new file mode 100644
index 0000000..58bc9f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/Prefs.java
@@ -0,0 +1,69 @@
+/*
+ * 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.volume;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.preference.PreferenceManager;
+
+/**
+ *  Configuration for the volume dialog + related policy.
+ */
+public class Prefs {
+
+    public static final String PREF_ENABLE_PROTOTYPE = "pref_enable_prototype";  // not persistent
+    public static final String PREF_SHOW_ALARMS = "pref_show_alarms";
+    public static final String PREF_SHOW_SYSTEM = "pref_show_system";
+    public static final String PREF_SHOW_HEADERS = "pref_show_headers";
+    public static final String PREF_SHOW_FAKE_REMOTE_1 = "pref_show_fake_remote_1";
+    public static final String PREF_SHOW_FAKE_REMOTE_2 = "pref_show_fake_remote_2";
+    public static final String PREF_SHOW_FOOTER = "pref_show_footer";
+    public static final String PREF_ZEN_FOOTER = "pref_zen_footer";
+    public static final String PREF_ENABLE_AUTOMUTE = "pref_enable_automute";
+    public static final String PREF_ENABLE_SILENT_MODE = "pref_enable_silent_mode";
+    public static final String PREF_DEBUG_LOGGING = "pref_debug_logging";
+    public static final String PREF_SEND_LOGS = "pref_send_logs";
+    public static final String PREF_ADJUST_SYSTEM = "pref_adjust_system";
+    public static final String PREF_ADJUST_VOICE_CALLS = "pref_adjust_voice_calls";
+    public static final String PREF_ADJUST_BLUETOOTH_SCO = "pref_adjust_bluetooth_sco";
+    public static final String PREF_ADJUST_MEDIA = "pref_adjust_media";
+    public static final String PREF_ADJUST_ALARMS = "pref_adjust_alarms";
+    public static final String PREF_ADJUST_NOTIFICATION = "pref_adjust_notification";
+
+    public static final boolean DEFAULT_SHOW_HEADERS = true;
+    public static final boolean DEFAULT_SHOW_FOOTER = true;
+    public static final boolean DEFAULT_ENABLE_AUTOMUTE = true;
+    public static final boolean DEFAULT_ENABLE_SILENT_MODE = true;
+    public static final boolean DEFAULT_ZEN_FOOTER = true;
+
+    public static void unregisterCallbacks(Context c, OnSharedPreferenceChangeListener listener) {
+        prefs(c).unregisterOnSharedPreferenceChangeListener(listener);
+    }
+
+    public static void registerCallbacks(Context c, OnSharedPreferenceChangeListener listener) {
+        prefs(c).registerOnSharedPreferenceChangeListener(listener);
+    }
+
+    private static SharedPreferences prefs(Context context) {
+        return PreferenceManager.getDefaultSharedPreferences(context);
+    }
+
+    public static boolean get(Context context, String key, boolean def) {
+        return prefs(context).getBoolean(key, def);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
index 5f5b881..4f20ac7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/SegmentedButtons.java
@@ -60,17 +60,14 @@
             final Object tag = c.getTag();
             final boolean selected = Objects.equals(mSelectedValue, tag);
             c.setSelected(selected);
-            c.getCompoundDrawables()[1].setTint(mContext.getColor(selected
-                    ? R.color.segmented_button_selected : R.color.segmented_button_unselected));
         }
         fireOnSelected();
     }
 
-    public void addButton(int labelResId, int iconResId, Object value) {
+    public void addButton(int labelResId, Object value) {
         final Button b = (Button) mInflater.inflate(R.layout.segmented_button, this, false);
         b.setTag(LABEL_RES_KEY, labelResId);
         b.setText(labelResId);
-        b.setCompoundDrawablesWithIntrinsicBounds(0, iconResId, 0, 0);
         final LayoutParams lp = (LayoutParams) b.getLayoutParams();
         if (getChildCount() == 0) {
             lp.leftMargin = lp.rightMargin = 0; // first button has no margin
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SpTexts.java b/packages/SystemUI/src/com/android/systemui/volume/SpTexts.java
new file mode 100644
index 0000000..d8e53db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/SpTexts.java
@@ -0,0 +1,78 @@
+/*
+ * 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.volume;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.ArrayMap;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
+import android.widget.TextView;
+
+/**
+ * Capture initial sp values for registered textviews, and update properly when configuration
+ * changes.
+ */
+public class SpTexts {
+
+    private final Context mContext;
+    private final ArrayMap<TextView, Integer> mTexts = new ArrayMap<>();
+
+    public SpTexts(Context context) {
+        mContext = context;
+    }
+
+    public int add(final TextView text) {
+        if (text == null) return 0;
+        final Resources res = mContext.getResources();
+        final float fontScale = res.getConfiguration().fontScale;
+        final float density = res.getDisplayMetrics().density;
+        final float px = text.getTextSize();
+        final int sp = (int)(px / fontScale / density);
+        mTexts.put(text, sp);
+        text.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
+            @Override
+            public void onViewDetachedFromWindow(View v) {
+            }
+
+            @Override
+            public void onViewAttachedToWindow(View v) {
+               setTextSizeH(text, sp);
+            }
+        });
+        return sp;
+    }
+
+    public void update() {
+        if (mTexts.isEmpty()) return;
+        mTexts.keyAt(0).post(mUpdateAll);
+    }
+
+    private void setTextSizeH(TextView text, int sp) {
+        text.setTextSize(TypedValue.COMPLEX_UNIT_SP, sp);
+    }
+
+    private final Runnable mUpdateAll = new Runnable() {
+        @Override
+        public void run() {
+            for (int i = 0; i < mTexts.size(); i++) {
+                setTextSizeH(mTexts.keyAt(i), mTexts.valueAt(i));
+            }
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Util.java b/packages/SystemUI/src/com/android/systemui/volume/Util.java
new file mode 100644
index 0000000..fbdc1ca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/Util.java
@@ -0,0 +1,165 @@
+/*
+ * 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.volume;
+
+import android.media.AudioManager;
+import android.media.MediaMetadata;
+import android.media.VolumeProvider;
+import android.media.session.MediaController.PlaybackInfo;
+import android.media.session.PlaybackState;
+import android.service.notification.ZenModeConfig.DowntimeInfo;
+import android.view.View;
+import android.widget.TextView;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * Static helpers for the volume dialog.
+ */
+class Util {
+
+    // Note: currently not shown (only used in the text footer)
+    private static final SimpleDateFormat HMMAA = new SimpleDateFormat("h:mm aa", Locale.US);
+
+    private static int[] AUDIO_MANAGER_FLAGS = new int[] {
+        AudioManager.FLAG_SHOW_UI,
+        AudioManager.FLAG_VIBRATE,
+        AudioManager.FLAG_PLAY_SOUND,
+        AudioManager.FLAG_ALLOW_RINGER_MODES,
+        AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE,
+        AudioManager.FLAG_SHOW_VIBRATE_HINT,
+        AudioManager.FLAG_SHOW_SILENT_HINT,
+        AudioManager.FLAG_FROM_KEY,
+    };
+
+    private static String[] AUDIO_MANAGER_FLAG_NAMES = new String[] {
+        "SHOW_UI",
+        "VIBRATE",
+        "PLAY_SOUND",
+        "ALLOW_RINGER_MODES",
+        "REMOVE_SOUND_AND_VIBRATE",
+        "SHOW_VIBRATE_HINT",
+        "SHOW_SILENT_HINT",
+        "FROM_KEY",
+    };
+
+    public static String logTag(Class<?> c) {
+        final String tag = "vol." + c.getSimpleName();
+        return tag.length() < 23 ? tag : tag.substring(0, 23);
+    }
+
+    public static String ringerModeToString(int ringerMode) {
+        switch (ringerMode) {
+            case AudioManager.RINGER_MODE_SILENT: return "RINGER_MODE_SILENT";
+            case AudioManager.RINGER_MODE_VIBRATE: return "RINGER_MODE_VIBRATE";
+            case AudioManager.RINGER_MODE_NORMAL: return "RINGER_MODE_NORMAL";
+            default: return "RINGER_MODE_UNKNOWN_" + ringerMode;
+        }
+    }
+
+    public static String mediaMetadataToString(MediaMetadata metadata) {
+        return metadata.getDescription().toString();
+    }
+
+    public static String playbackInfoToString(PlaybackInfo info) {
+        if (info == null) return null;
+        final String type = playbackInfoTypeToString(info.getPlaybackType());
+        final String vc = volumeProviderControlToString(info.getVolumeControl());
+        return String.format("PlaybackInfo[vol=%s,max=%s,type=%s,vc=%s],atts=%s",
+                info.getCurrentVolume(), info.getMaxVolume(), type, vc, info.getAudioAttributes());
+    }
+
+    public static String playbackInfoTypeToString(int type) {
+        switch (type) {
+            case PlaybackInfo.PLAYBACK_TYPE_LOCAL: return "LOCAL";
+            case PlaybackInfo.PLAYBACK_TYPE_REMOTE: return "REMOTE";
+            default: return "UNKNOWN_" + type;
+        }
+    }
+
+    public static String playbackStateStateToString(int state) {
+        switch (state) {
+            case PlaybackState.STATE_NONE: return "STATE_NONE";
+            case PlaybackState.STATE_STOPPED: return "STATE_STOPPED";
+            case PlaybackState.STATE_PAUSED: return "STATE_PAUSED";
+            case PlaybackState.STATE_PLAYING: return "STATE_PLAYING";
+            default: return "UNKNOWN_" + state;
+        }
+    }
+
+    public static String volumeProviderControlToString(int control) {
+        switch (control) {
+            case VolumeProvider.VOLUME_CONTROL_ABSOLUTE: return "VOLUME_CONTROL_ABSOLUTE";
+            case VolumeProvider.VOLUME_CONTROL_FIXED: return "VOLUME_CONTROL_FIXED";
+            case VolumeProvider.VOLUME_CONTROL_RELATIVE: return "VOLUME_CONTROL_RELATIVE";
+            default: return "VOLUME_CONTROL_UNKNOWN_" + control;
+        }
+    }
+
+    public static String playbackStateToString(PlaybackState playbackState) {
+        if (playbackState == null) return null;
+        return playbackStateStateToString(playbackState.getState()) + " " + playbackState;
+    }
+
+    public static String audioManagerFlagsToString(int value) {
+        return bitFieldToString(value, AUDIO_MANAGER_FLAGS, AUDIO_MANAGER_FLAG_NAMES);
+    }
+
+    private static String bitFieldToString(int value, int[] values, String[] names) {
+        if (value == 0) return "";
+        final StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < values.length; i++) {
+            if ((value & values[i]) != 0) {
+                if (sb.length() > 0) sb.append(',');
+                sb.append(names[i]);
+            }
+            value &= ~values[i];
+        }
+        if (value != 0) {
+            if (sb.length() > 0) sb.append(',');
+            sb.append("UNKNOWN_").append(value);
+        }
+        return sb.toString();
+    }
+
+    public static String getShortTime(long millis) {
+        return HMMAA.format(new Date(millis));
+    }
+
+    public static String getShortTime(DowntimeInfo info) {
+        return ((info.endHour + 1) % 12) + ":" + (info.endMinute < 10 ? " " : "") + info.endMinute;
+    }
+
+    public static void setText(TextView tv, CharSequence text) {
+        if (Objects.equals(tv.getText(), text)) return;
+        tv.setText(text);
+    }
+
+    public static final void setVisOrGone(View v, boolean vis) {
+        if (v == null || (v.getVisibility() == View.VISIBLE) == vis) return;
+        v.setVisibility(vis ? View.VISIBLE : View.GONE);
+    }
+
+    public static final void setVisOrInvis(View v, boolean vis) {
+        if (v == null || (v.getVisibility() == View.VISIBLE) == vis) return;
+        v.setVisibility(vis ? View.VISIBLE : View.INVISIBLE);
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java
index e3f8f3d..1f0ee57 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeComponent.java
@@ -16,10 +16,18 @@
 
 package com.android.systemui.volume;
 
+import android.content.res.Configuration;
+
 import com.android.systemui.DemoMode;
 import com.android.systemui.statusbar.policy.ZenModeController;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 public interface VolumeComponent extends DemoMode {
     ZenModeController getZenController();
     void dismissNow();
+    void onConfigurationChanged(Configuration newConfig);
+    void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+    void register();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
new file mode 100644
index 0000000..5fbb18d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -0,0 +1,1039 @@
+/*
+ * 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.volume;
+
+import android.animation.LayoutTransition;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
+import android.app.Dialog;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.provider.Settings.Global;
+import android.service.notification.ZenModeConfig;
+import android.service.notification.ZenModeConfig.DowntimeInfo;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLayoutChangeListener;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.volume.VolumeDialogController.State;
+import com.android.systemui.volume.VolumeDialogController.StreamState;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Visual presentation of the volume dialog.
+ *
+ * A client of VolumeDialogController and its state model.
+ *
+ * Methods ending in "H" must be called on the (ui) handler.
+ */
+public class VolumeDialog {
+    private static final String TAG = Util.logTag(VolumeDialog.class);
+
+    private static final long USER_ATTEMPT_GRACE_PERIOD = 1000;
+    private static final int WAIT_FOR_RIPPLE = 200;
+    private static final int UPDATE_ANIMATION_DURATION = 80;
+
+    private final Context mContext;
+    private final H mHandler = new H();
+    private final VolumeDialogController mController;
+
+    private final CustomDialog mDialog;
+    private final ViewGroup mDialogView;
+    private final ViewGroup mDialogContentView;
+    private final ImageButton mExpandButton;
+    private final TextView mFootlineText;
+    private final Button mFootlineAction;
+    private final View mSettingsButton;
+    private final View mFooter;
+    private final List<VolumeRow> mRows = new ArrayList<VolumeRow>();
+    private final SpTexts mSpTexts;
+    private final SparseBooleanArray mDynamic = new SparseBooleanArray();
+    private final KeyguardManager mKeyguard;
+    private final int mExpandButtonAnimationDuration;
+    private final View mTextFooter;
+    private final ZenFooter mZenFooter;
+    private final LayoutTransition mLayoutTransition;
+
+    private boolean mShowing;
+    private boolean mExpanded;
+    private int mActiveStream;
+    private boolean mShowHeaders = Prefs.DEFAULT_SHOW_HEADERS;
+    private boolean mShowFooter = Prefs.DEFAULT_SHOW_FOOTER;
+    private boolean mShowZenFooter = Prefs.DEFAULT_ZEN_FOOTER;
+    private boolean mAutomute = Prefs.DEFAULT_ENABLE_AUTOMUTE;
+    private boolean mSilentMode = Prefs.DEFAULT_ENABLE_SILENT_MODE;
+    private State mState;
+    private int mExpandAnimRes;
+    private boolean mExpanding;
+
+    public VolumeDialog(Context context, VolumeDialogController controller,
+            ZenModeController zenModeController) {
+        mContext = context;
+        mController = controller;
+        mSpTexts = new SpTexts(mContext);
+        mKeyguard = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
+
+        mDialog = new CustomDialog(mContext);
+
+        final Window window = mDialog.getWindow();
+        window.requestFeature(Window.FEATURE_NO_TITLE);
+        window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+        window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+        mDialog.setCanceledOnTouchOutside(true);
+        final Resources res = mContext.getResources();
+        final WindowManager.LayoutParams lp = window.getAttributes();
+        lp.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
+        lp.format = PixelFormat.TRANSLUCENT;
+        lp.setTitle(VolumeDialog.class.getSimpleName());
+        lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
+        lp.windowAnimations = R.style.VolumeDialogAnimations;
+        lp.y = res.getDimensionPixelSize(R.dimen.volume_offset_top);
+        lp.gravity = Gravity.TOP;
+        window.setAttributes(lp);
+
+        mDialog.setContentView(R.layout.volume_dialog);
+        mDialogView = (ViewGroup) mDialog.findViewById(R.id.volume_dialog);
+        mDialogContentView = (ViewGroup) mDialog.findViewById(R.id.volume_dialog_content);
+        mExpandButton = (ImageButton) mDialogView.findViewById(R.id.volume_expand_button);
+        mExpandButton.setOnClickListener(mClickExpand);
+        updateWindowWidthH();
+        updateExpandButtonH();
+        mLayoutTransition = new LayoutTransition();
+        mLayoutTransition.setDuration(new ValueAnimator().getDuration() / 2);
+        mLayoutTransition.disableTransitionType(LayoutTransition.DISAPPEARING);
+        mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
+        mDialogContentView.setLayoutTransition(mLayoutTransition);
+
+        addRow(AudioManager.STREAM_RING,
+                R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true);
+        addRow(AudioManager.STREAM_MUSIC,
+                R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true);
+        addRow(AudioManager.STREAM_ALARM,
+                R.drawable.ic_volume_alarm, R.drawable.ic_volume_alarm_mute, false);
+        addRow(AudioManager.STREAM_VOICE_CALL,
+                R.drawable.ic_volume_voice, R.drawable.ic_volume_voice, false);
+        addRow(AudioManager.STREAM_BLUETOOTH_SCO,
+                R.drawable.ic_volume_bt_sco, R.drawable.ic_volume_bt_sco, false);
+        addRow(AudioManager.STREAM_SYSTEM,
+                R.drawable.ic_volume_system, R.drawable.ic_volume_system_mute, false);
+
+        mTextFooter = mDialog.findViewById(R.id.volume_text_footer);
+        mFootlineText = (TextView) mDialog.findViewById(R.id.volume_footline_text);
+        mSpTexts.add(mFootlineText);
+        mFootlineAction = (Button) mDialog.findViewById(R.id.volume_footline_action_button);
+        mSpTexts.add(mFootlineAction);
+        mFooter = mDialog.findViewById(R.id.volume_footer);
+        mSettingsButton = mDialog.findViewById(R.id.volume_settings_button);
+        mSettingsButton.setOnClickListener(mClickSettings);
+        mExpandButtonAnimationDuration = res.getInteger(R.integer.volume_expand_animation_duration);
+        mZenFooter = (ZenFooter) mDialog.findViewById(R.id.volume_zen_footer);
+        mZenFooter.init(zenModeController, mZenFooterCallback);
+
+        controller.addCallback(mControllerCallbackH, mHandler);
+        controller.getState();
+    }
+
+    private void updateWindowWidthH() {
+        final ViewGroup.LayoutParams lp = mDialogView.getLayoutParams();
+        final DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
+        if (D.BUG) Log.d(TAG, "updateWindowWidth dm.w=" + dm.widthPixels);
+        int w = dm.widthPixels;
+        final int max = mContext.getResources()
+                .getDimensionPixelSize(R.dimen.standard_notification_panel_width);
+        if (w > max) {
+            w = max;
+        }
+        w -= mContext.getResources().getDimensionPixelSize(R.dimen.notification_side_padding) * 2;
+        lp.width = w;
+        mDialogView.setLayoutParams(lp);
+    }
+
+    public void setStreamImportant(int stream, boolean important) {
+        mHandler.obtainMessage(H.SET_STREAM_IMPORTANT, stream, important ? 1 : 0).sendToTarget();
+    }
+
+    public void setShowHeaders(boolean showHeaders) {
+        if (showHeaders == mShowHeaders) return;
+        mShowHeaders = showHeaders;
+        mHandler.sendEmptyMessage(H.RECHECK_ALL);
+    }
+
+    public void setShowFooter(boolean show) {
+        if (mShowFooter == show) return;
+        mShowFooter = show;
+        mHandler.sendEmptyMessage(H.RECHECK_ALL);
+    }
+
+    public void setZenFooter(boolean zen) {
+        if (mShowZenFooter == zen) return;
+        mShowZenFooter = zen;
+        mHandler.sendEmptyMessage(H.RECHECK_ALL);
+    }
+
+    public void setAutomute(boolean automute) {
+        if (mAutomute == automute) return;
+        mAutomute = automute;
+        mHandler.sendEmptyMessage(H.RECHECK_ALL);
+    }
+
+    public void setSilentMode(boolean silentMode) {
+        if (mSilentMode == silentMode) return;
+        mSilentMode = silentMode;
+        mHandler.sendEmptyMessage(H.RECHECK_ALL);
+    }
+
+    private void addRow(int stream, int iconRes, int iconMuteRes, boolean important) {
+        final VolumeRow row = initRow(stream, iconRes, iconMuteRes, important);
+        if (!mRows.isEmpty()) {
+            final View v = new View(mContext);
+            final int h = mContext.getResources()
+                    .getDimensionPixelSize(R.dimen.volume_slider_interspacing);
+            final LinearLayout.LayoutParams lp =
+                    new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, h);
+            mDialogContentView.addView(v, mDialogContentView.getChildCount() - 1, lp);
+            row.space = v;
+        }
+        row.settingsButton.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+            @Override
+            public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                if (D.BUG) Log.d(TAG, "onLayoutChange"
+                        + " old=" + new Rect(oldLeft, oldTop, oldRight, oldBottom).toShortString()
+                        + " new=" + new Rect(left,top,right,bottom).toShortString());
+                if (oldLeft != left || oldTop != top) {
+                    for (int i = 0; i < mDialogContentView.getChildCount(); i++) {
+                        final View c = mDialogContentView.getChildAt(i);
+                        if (!c.isShown()) continue;
+                        if (c == row.view) {
+                            repositionExpandAnim(row);
+                        }
+                        return;
+                    }
+                }
+            }
+        });
+        // add new row just before the footer
+        mDialogContentView.addView(row.view, mDialogContentView.getChildCount() - 1);
+        mRows.add(row);
+    }
+
+    private boolean isAttached() {
+        return mDialogContentView != null && mDialogContentView.isAttachedToWindow();
+    }
+
+    private VolumeRow getActiveRow() {
+        for (VolumeRow row : mRows) {
+            if (row.stream == mActiveStream) {
+                return row;
+            }
+        }
+        return mRows.get(0);
+    }
+
+    private VolumeRow findRow(int stream) {
+        for (VolumeRow row : mRows) {
+            if (row.stream == stream) return row;
+        }
+        return null;
+    }
+
+    private void repositionExpandAnim(VolumeRow row) {
+        final int[] loc = new int[2];
+        row.settingsButton.getLocationInWindow(loc);
+        final MarginLayoutParams mlp = (MarginLayoutParams) mDialogView.getLayoutParams();
+        final int x = loc[0] - mlp.leftMargin;
+        final int y = loc[1] - mlp.topMargin;
+        if (D.BUG) Log.d(TAG, "repositionExpandAnim x=" + x + " y=" + y);
+        mExpandButton.setTranslationX(x);
+        mExpandButton.setTranslationY(y);
+    }
+
+    public void dump(PrintWriter writer) {
+        writer.println(VolumeDialog.class.getSimpleName() + " state:");
+        writer.print("  mShowing: "); writer.println(mShowing);
+        writer.print("  mExpanded: "); writer.println(mExpanded);
+        writer.print("  mExpanding: "); writer.println(mExpanding);
+        writer.print("  mActiveStream: "); writer.println(mActiveStream);
+        writer.print("  mDynamic: "); writer.println(mDynamic);
+        writer.print("  mShowHeaders: "); writer.println(mShowHeaders);
+        writer.print("  mShowFooter: "); writer.println(mShowFooter);
+        writer.print("  mAutomute: "); writer.println(mAutomute);
+        writer.print("  mSilentMode: "); writer.println(mSilentMode);
+    }
+
+    private static int getImpliedLevel(SeekBar seekBar, int progress) {
+        final int m = seekBar.getMax();
+        final int n = m / 100 - 1;
+        final int level = progress == 0 ? 0
+                : progress == m ? (m / 100) : (1 + (int)((progress / (float) m) * n));
+        return level;
+    }
+
+    @SuppressLint("InflateParams")
+    private VolumeRow initRow(final int stream, int iconRes, int iconMuteRes, boolean important) {
+        final VolumeRow row = new VolumeRow();
+        row.stream = stream;
+        row.iconRes = iconRes;
+        row.iconMuteRes = iconMuteRes;
+        row.important = important;
+        row.view = mDialog.getLayoutInflater().inflate(R.layout.volume_dialog_row, null);
+        row.view.setTag(row);
+        row.header = (TextView) row.view.findViewById(R.id.volume_row_header);
+        mSpTexts.add(row.header);
+        row.slider = (SeekBar) row.view.findViewById(R.id.volume_row_slider);
+        row.slider.setOnSeekBarChangeListener(new VolumeSeekBarChangeListener(row));
+
+        // forward events above the slider into the slider
+        row.view.setOnTouchListener(new OnTouchListener() {
+            private final Rect mSliderHitRect = new Rect();
+            private boolean mDragging;
+
+            @SuppressLint("ClickableViewAccessibility")
+            @Override
+            public boolean onTouch(View v, MotionEvent event) {
+                row.slider.getHitRect(mSliderHitRect);
+                if (!mDragging && event.getActionMasked() == MotionEvent.ACTION_DOWN
+                        && event.getY() < mSliderHitRect.top) {
+                    mDragging = true;
+                }
+                if (mDragging) {
+                    event.offsetLocation(-mSliderHitRect.left, -mSliderHitRect.top);
+                    row.slider.dispatchTouchEvent(event);
+                    if (event.getActionMasked() == MotionEvent.ACTION_UP
+                            || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
+                        mDragging = false;
+                    }
+                    return true;
+                }
+                return false;
+            }
+        });
+        row.icon = (ImageButton) row.view.findViewById(R.id.volume_row_icon);
+        row.icon.setImageResource(iconRes);
+        row.icon.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                Events.writeEvent(Events.EVENT_ICON_CLICK, row.stream, row.iconState);
+                mController.setActiveStream(row.stream);
+                if (row.stream == AudioManager.STREAM_RING) {
+                    final boolean hasVibrator = mController.hasVibrator();
+                    if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
+                        if (hasVibrator) {
+                            mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false);
+                        } else {
+                            final boolean wasZero = row.ss.level == 0;
+                            mController.setStreamVolume(stream, wasZero ? row.lastAudibleLevel : 0);
+                        }
+                    } else {
+                        mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
+                        if (row.ss.level == 0) {
+                            mController.setStreamVolume(stream, 1);
+                        }
+                    }
+                } else {
+                    if (mAutomute && !row.ss.muteSupported) {
+                        final boolean vmute = row.ss.level == 0;
+                        mController.setStreamVolume(stream, vmute ? row.lastAudibleLevel : 0);
+                    } else {
+                        final boolean mute = !row.ss.muted;
+                        mController.setStreamMute(stream, mute);
+                        if (mAutomute) {
+                            if (!mute && row.ss.level == 0) {
+                                mController.setStreamVolume(stream, 1);
+                            }
+                        }
+                    }
+                }
+                row.userAttempt = 0;  // reset the grace period, slider should update immediately
+            }
+        });
+        row.settingsButton = (ImageButton) row.view.findViewById(R.id.volume_settings_button);
+        row.settingsButton.setOnClickListener(mClickSettings);
+        return row;
+    }
+
+    public void destroy() {
+        mController.removeCallback(mControllerCallbackH);
+    }
+
+    public void show(int reason) {
+        mHandler.obtainMessage(H.SHOW, reason, 0).sendToTarget();
+    }
+
+    public void dismiss(int reason) {
+        mHandler.obtainMessage(H.DISMISS, reason, 0).sendToTarget();
+    }
+
+    protected void onSettingsClickedH() {
+        // hook for subclasses
+    }
+
+    protected void onZenSettingsClickedH() {
+        // hook for subclasses
+    }
+
+    private void showH(int reason) {
+        mHandler.removeMessages(H.SHOW);
+        mHandler.removeMessages(H.DISMISS);
+        rescheduleTimeoutH();
+        if (mShowing) return;
+        mShowing = true;
+        mDialog.show();
+        Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
+        mController.notifyVisible(true);
+    }
+
+    protected void rescheduleTimeoutH() {
+        mHandler.removeMessages(H.DISMISS);
+        final int timeout = computeTimeoutH();
+        if (D.BUG) Log.d(TAG, "rescheduleTimeout " + timeout);
+        mHandler.sendMessageDelayed(mHandler
+                .obtainMessage(H.DISMISS, Events.DISMISS_REASON_TIMEOUT, 0), timeout);
+        mController.userActivity();
+    }
+
+    private int computeTimeoutH() {
+        if (mZenFooter != null && mZenFooter.isFooterExpanded()) return 10000;
+        if (mExpanded || mExpanding) return 5000;
+        if (mActiveStream == AudioManager.STREAM_MUSIC) return 1500;
+        return 3000;
+    }
+
+    protected void dismissH(int reason) {
+        mHandler.removeMessages(H.DISMISS);
+        mHandler.removeMessages(H.SHOW);
+        if (!mShowing) return;
+        mShowing = false;
+        mDialog.dismiss();
+        Events.writeEvent(Events.EVENT_DISMISS_DIALOG, reason);
+        setExpandedH(false);
+        mController.notifyVisible(false);
+    }
+
+    private void setExpandedH(boolean expanded) {
+        if (mExpanded == expanded) return;
+        mExpanded = expanded;
+        mExpanding = isAttached();
+        if (D.BUG) Log.d(TAG, "setExpandedH " + expanded);
+        updateRowsH();
+        if (mExpanding) {
+            final Drawable d = mExpandButton.getDrawable();
+            if (d instanceof AnimatedVectorDrawable) {
+                // workaround to reset drawable
+                final AnimatedVectorDrawable avd = (AnimatedVectorDrawable) d.getConstantState()
+                        .newDrawable();
+                mExpandButton.setImageDrawable(avd);
+                avd.start();
+                mHandler.postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        mExpanding = false;
+                        updateExpandButtonH();
+                        rescheduleTimeoutH();
+                    }
+                }, mExpandButtonAnimationDuration);
+            }
+        }
+        rescheduleTimeoutH();
+    }
+
+    private void updateExpandButtonH() {
+        mExpandButton.setClickable(!mExpanding);
+        if (mExpanding && isAttached()) return;
+        final int res = mExpanded ? R.drawable.ic_volume_collapse_animation
+                : R.drawable.ic_volume_expand_animation;
+        if (res == mExpandAnimRes) return;
+        mExpandAnimRes = res;
+        mExpandButton.setImageResource(res);
+    }
+
+    private boolean isVisibleH(VolumeRow row, boolean isActive) {
+        return mExpanded && row.view.getVisibility() == View.VISIBLE
+                || (mExpanded && (row.important || isActive))
+                || !mExpanded && isActive;
+    }
+
+    private void updateRowsH() {
+        final VolumeRow activeRow = getActiveRow();
+        updateFooterH();
+        updateExpandButtonH();
+        final boolean footerVisible = mFooter.getVisibility() == View.VISIBLE;
+        if (!mShowing) {
+            trimObsoleteH();
+        }
+        // first, find the last visible row
+        VolumeRow lastVisible = null;
+        for (VolumeRow row : mRows) {
+            final boolean isActive = row == activeRow;
+            if (isVisibleH(row, isActive)) {
+                lastVisible = row;
+            }
+        }
+        // apply changes to all rows
+        for (VolumeRow row : mRows) {
+            final boolean isActive = row == activeRow;
+            final boolean visible = isVisibleH(row, isActive);
+            Util.setVisOrGone(row.view, visible);
+            Util.setVisOrGone(row.space, visible && mExpanded);
+            final int expandButtonRes = mExpanded ? R.drawable.ic_volume_settings : 0;
+            if (expandButtonRes != row.cachedExpandButtonRes) {
+                row.cachedExpandButtonRes = expandButtonRes;
+                if (expandButtonRes == 0) {
+                    row.settingsButton.setImageDrawable(null);
+                } else {
+                    row.settingsButton.setImageResource(expandButtonRes);
+                }
+            }
+            Util.setVisOrInvis(row.settingsButton,
+                     mExpanded && (!footerVisible && row == lastVisible));
+            row.header.setAlpha(mExpanded && isActive ? 1 : 0.5f);
+        }
+    }
+
+    private void trimObsoleteH() {
+        for (int i = mRows.size() -1; i >= 0; i--) {
+            final VolumeRow row = mRows.get(i);
+            if (row.ss == null || !row.ss.dynamic) continue;
+            if (!mDynamic.get(row.stream)) {
+                mRows.remove(i);
+                mDialogContentView.removeView(row.view);
+                mDialogContentView.removeView(row.space);
+            }
+        }
+    }
+
+    private void onStateChangedH(State state) {
+        mState = state;
+        mDynamic.clear();
+        // add any new dynamic rows
+        for (int i = 0; i < state.states.size(); i++) {
+            final int stream = state.states.keyAt(i);
+            final StreamState ss = state.states.valueAt(i);
+            if (!ss.dynamic) continue;
+            mDynamic.put(stream, true);
+            if (findRow(stream) == null) {
+                addRow(stream, R.drawable.ic_volume_remote, R.drawable.ic_volume_remote_mute, true);
+            }
+        }
+
+        if (mActiveStream != state.activeStream) {
+            mActiveStream = state.activeStream;
+            updateRowsH();
+            rescheduleTimeoutH();
+        }
+        for (VolumeRow row : mRows) {
+            updateVolumeRowH(row);
+        }
+        updateFooterH();
+    }
+
+    private void updateTextFooterH() {
+        final boolean zen = mState.zenMode != Global.ZEN_MODE_OFF;
+        final boolean wasVisible = mFooter.getVisibility() == View.VISIBLE;
+        Util.setVisOrGone(mTextFooter, mExpanded && mShowFooter && (zen || mShowing && wasVisible));
+        if (mTextFooter.getVisibility() == View.VISIBLE) {
+            String text = null;
+            String action = null;
+            if (mState.exitCondition != null) {
+                final long countdown = ZenModeConfig.tryParseCountdownConditionId(mState
+                        .exitCondition.id);
+                if (countdown != 0) {
+                    text = mContext.getString(R.string.volume_dnd_ends_at,
+                            Util.getShortTime(countdown));
+                    action = mContext.getString(R.string.volume_end_now);
+                } else {
+                    final DowntimeInfo info = ZenModeConfig.tryParseDowntimeConditionId(mState.
+                            exitCondition.id);
+                    if (info != null) {
+                        text = mContext.getString(R.string.volume_dnd_ends_at,
+                                Util.getShortTime(info));
+                        action = mContext.getString(R.string.volume_end_now);
+                    }
+                }
+            }
+            if (text == null) {
+                text = mContext.getString(R.string.volume_dnd_is_on);
+            }
+            if (action == null) {
+                action = mContext.getString(R.string.volume_turn_off);
+            }
+            Util.setText(mFootlineText, text);
+            Util.setText(mFootlineAction, action);
+            mFootlineAction.setOnClickListener(mTurnOffDnd);
+        }
+        Util.setVisOrGone(mFootlineText, zen);
+        Util.setVisOrGone(mFootlineAction, zen);
+    }
+
+    private void updateFooterH() {
+        if (!mShowFooter) {
+            Util.setVisOrGone(mFooter, false);
+            return;
+        }
+        if (mShowZenFooter) {
+            Util.setVisOrGone(mTextFooter, false);
+            final boolean ringActive = mActiveStream == AudioManager.STREAM_RING;
+            Util.setVisOrGone(mZenFooter, mZenFooter.isZen() && ringActive
+                    || mShowing && (mExpanded || mZenFooter.getVisibility() == View.VISIBLE));
+            mZenFooter.update();
+        } else {
+            Util.setVisOrGone(mZenFooter, false);
+            updateTextFooterH();
+        }
+    }
+
+    private void updateVolumeRowH(VolumeRow row) {
+        if (mState == null) return;
+        final StreamState ss = mState.states.get(row.stream);
+        if (ss == null) return;
+        row.ss = ss;
+        if (ss.level > 0) {
+            row.lastAudibleLevel = ss.level;
+        }
+        final boolean isRingStream = row.stream == AudioManager.STREAM_RING;
+        final boolean isSystemStream = row.stream == AudioManager.STREAM_SYSTEM;
+        final boolean isRingVibrate = isRingStream
+                && mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE;
+        final boolean isNoned = (isRingStream || isSystemStream)
+                && mState.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
+        final boolean isLimited = isRingStream
+                && mState.zenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+        final boolean isRingAndSuppressed = isRingStream && mState.effectsSuppressor != null;
+
+        // update slider max
+        final int max = ss.levelMax * 100;
+        if (max != row.slider.getMax()) {
+            row.slider.setMax(max);
+        }
+
+        // update header visible
+        if (row.cachedShowHeaders != mShowHeaders) {
+            row.cachedShowHeaders = mShowHeaders;
+            Util.setVisOrGone(row.header, mShowHeaders);
+        }
+
+        // update header text
+        final String text;
+        if (isRingAndSuppressed) {
+            text = mContext.getString(R.string.volume_stream_suppressed, ss.name,
+                    mState.effectsSuppressorName);
+        } else if (isNoned) {
+            text = mContext.getString(R.string.volume_stream_muted_dnd, ss.name);
+        } else if (isRingVibrate && isLimited) {
+            text = mContext.getString(R.string.volume_stream_vibrate_dnd, ss.name);
+        } else if (isRingVibrate) {
+            text = mContext.getString(R.string.volume_stream_vibrate, ss.name);
+        } else if (ss.muted || mAutomute && ss.level == 0) {
+            text = mContext.getString(R.string.volume_stream_muted, ss.name);
+        } else if (isLimited) {
+            text = mContext.getString(R.string.volume_stream_limited_dnd, ss.name);
+        } else {
+            text = ss.name;
+        }
+        Util.setText(row.header, text);
+
+        // update icon
+        final boolean iconEnabled = !isRingAndSuppressed && (mAutomute || ss.muteSupported);
+        row.icon.setEnabled(iconEnabled);
+        row.icon.setAlpha(iconEnabled ? 1 : 0.5f);
+        final int iconRes =
+                !isRingAndSuppressed && isRingVibrate ? R.drawable.ic_volume_ringer_vibrate
+                : ss.routedToBluetooth ?
+                        (ss.muted ? R.drawable.ic_volume_bt_mute : R.drawable.ic_volume_bt)
+                : isRingAndSuppressed || (mAutomute && ss.level == 0) ? row.iconMuteRes
+                : (ss.muted ? row.iconMuteRes : row.iconRes);
+        if (iconRes != row.cachedIconRes) {
+            if (row.cachedIconRes != 0 && isRingVibrate) {
+                mController.vibrate();
+            }
+            row.cachedIconRes = iconRes;
+            row.icon.setImageResource(iconRes);
+        }
+        row.iconState =
+                iconRes == R.drawable.ic_volume_ringer_vibrate ? Events.ICON_STATE_VIBRATE
+                : (iconRes == R.drawable.ic_volume_bt_mute || iconRes == row.iconMuteRes)
+                        ? Events.ICON_STATE_MUTE
+                : (iconRes == R.drawable.ic_volume_bt || iconRes == row.iconRes)
+                        ? Events.ICON_STATE_UNMUTE
+                : Events.ICON_STATE_UNKNOWN;
+
+        // update slider
+        updateVolumeRowSliderH(row, isRingAndSuppressed);
+    }
+
+    private void updateVolumeRowSliderH(VolumeRow row, boolean isRingAndSuppressed) {
+        row.slider.setEnabled(!isRingAndSuppressed);
+        if (row.tracking) {
+            return;  // don't update if user is sliding
+        }
+        if (isRingAndSuppressed) {
+            row.slider.setProgress(0);
+            return;
+        }
+        final int progress = row.slider.getProgress();
+        final int level = getImpliedLevel(row.slider, progress);
+        final boolean rowVisible = row.view.getVisibility() == View.VISIBLE;
+        final boolean inGracePeriod = (SystemClock.uptimeMillis() - row.userAttempt)
+                < USER_ATTEMPT_GRACE_PERIOD;
+        mHandler.removeMessages(H.RECHECK, row);
+        if (mShowing && rowVisible && inGracePeriod) {
+            if (D.BUG) Log.d(TAG, "inGracePeriod");
+            mHandler.sendMessageAtTime(mHandler.obtainMessage(H.RECHECK, row),
+                    row.userAttempt + USER_ATTEMPT_GRACE_PERIOD);
+            return;  // don't update if visible and in grace period
+        }
+        final int vlevel = row.ss.muted ? 0 : row.ss.level;
+        if (vlevel == level) {
+            if (mShowing && rowVisible) {
+                return;  // don't clamp if visible
+            }
+        }
+        final int newProgress = vlevel * 100;
+        if (progress != newProgress) {
+            if (mShowing && rowVisible) {
+                // animate!
+                if (row.anim != null && row.anim.isRunning()
+                        && row.animTargetProgress == newProgress) {
+                    return;  // already animating to the target progress
+                }
+                // start/update animation
+                if (row.anim == null) {
+                    row.anim = ObjectAnimator.ofInt(row.slider, "progress", progress, newProgress);
+                    row.anim.setInterpolator(new DecelerateInterpolator());
+                } else {
+                    row.anim.cancel();
+                    row.anim.setIntValues(progress, newProgress);
+                }
+                row.animTargetProgress = newProgress;
+                row.anim.setDuration(UPDATE_ANIMATION_DURATION);
+                row.anim.start();
+            } else {
+                // update slider directly to clamped value
+                if (row.anim != null) {
+                    row.anim.cancel();
+                }
+                row.slider.setProgress(newProgress);
+            }
+            if (mAutomute) {
+                if (vlevel == 0 && !row.ss.muted && row.stream == AudioManager.STREAM_MUSIC) {
+                    mController.setStreamMute(row.stream, true);
+                }
+            }
+        }
+    }
+
+    private void recheckH(VolumeRow row) {
+        if (row == null) {
+            if (D.BUG) Log.d(TAG, "recheckH ALL");
+            trimObsoleteH();
+            for (VolumeRow r : mRows) {
+                updateVolumeRowH(r);
+            }
+        } else {
+            if (D.BUG) Log.d(TAG, "recheckH " + row.stream);
+            updateVolumeRowH(row);
+        }
+    }
+
+    private void setStreamImportantH(int stream, boolean important) {
+        for (VolumeRow row : mRows) {
+            if (row.stream == stream) {
+                row.important = important;
+                return;
+            }
+        }
+    }
+
+    private final VolumeDialogController.Callbacks mControllerCallbackH
+            = new VolumeDialogController.Callbacks() {
+        @Override
+        public void onShowRequested(int reason) {
+            showH(reason);
+        }
+
+        @Override
+        public void onDismissRequested(int reason) {
+            dismissH(reason);
+        }
+
+        public void onScreenOff() {
+            dismissH(Events.DISMISS_REASON_SCREEN_OFF);
+        }
+
+        @Override
+        public void onStateChanged(State state) {
+            onStateChangedH(state);
+        }
+
+        @Override
+        public void onLayoutDirectionChanged(int layoutDirection) {
+            mDialogView.setLayoutDirection(layoutDirection);
+        }
+
+        @Override
+        public void onConfigurationChanged() {
+            updateWindowWidthH();
+            mSpTexts.update();
+        }
+
+        @Override
+        public void onShowVibrateHint() {
+            if (mSilentMode) {
+                mController.setRingerMode(AudioManager.RINGER_MODE_SILENT, false);
+            }
+        }
+
+        public void onShowSilentHint() {
+            if (mSilentMode) {
+                mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
+            }
+        }
+    };
+
+    private final OnClickListener mClickExpand = new OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            if (mExpanding) return;
+            final boolean newExpand = !mExpanded;
+            Events.writeEvent(Events.EVENT_EXPAND, v);
+            setExpandedH(newExpand);
+        }
+    };
+
+    private final OnClickListener mClickSettings = new OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            mSettingsButton.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    Events.writeEvent(Events.EVENT_SETTINGS_CLICK);
+                    onSettingsClickedH();
+                }
+            }, WAIT_FOR_RIPPLE);
+        }
+    };
+
+    private final View.OnClickListener mTurnOffDnd = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            mSettingsButton.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    mController.setZenMode(Global.ZEN_MODE_OFF);
+                }
+            }, WAIT_FOR_RIPPLE);
+        }
+    };
+
+    private final ZenFooter.Callback mZenFooterCallback = new ZenFooter.Callback() {
+        @Override
+        public void onFooterExpanded() {
+            mHandler.sendEmptyMessage(H.RESCHEDULE_TIMEOUT);
+        }
+
+        @Override
+        public void onSettingsClicked() {
+            dismiss(Events.DISMISS_REASON_SETTINGS_CLICKED);
+            onZenSettingsClickedH();
+        }
+
+        @Override
+        public void onDoneClicked() {
+            dismiss(Events.DISMISS_REASON_DONE_CLICKED);
+        }
+    };
+
+    private final class H extends Handler {
+        private static final int SHOW = 1;
+        private static final int DISMISS = 2;
+        private static final int RECHECK = 3;
+        private static final int RECHECK_ALL = 4;
+        private static final int SET_STREAM_IMPORTANT = 5;
+        private static final int RESCHEDULE_TIMEOUT = 6;
+
+        public H() {
+            super(Looper.getMainLooper());
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case SHOW: showH(msg.arg1); break;
+                case DISMISS: dismissH(msg.arg1); break;
+                case RECHECK: recheckH((VolumeRow) msg.obj); break;
+                case RECHECK_ALL: recheckH(null); break;
+                case SET_STREAM_IMPORTANT: setStreamImportantH(msg.arg1, msg.arg2 != 0); break;
+                case RESCHEDULE_TIMEOUT: rescheduleTimeoutH(); break;
+            }
+        }
+    }
+
+    private final class CustomDialog extends Dialog {
+        public CustomDialog(Context context) {
+            super(context);
+        }
+
+        @Override
+        public boolean dispatchTouchEvent(MotionEvent ev) {
+            rescheduleTimeoutH();
+            return super.dispatchTouchEvent(ev);
+        }
+
+        @Override
+        protected void onStop() {
+            super.onStop();
+            mHandler.sendEmptyMessage(H.RECHECK_ALL);
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent event) {
+            if (isShowing()) {
+                if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
+                    dismissH(Events.DISMISS_REASON_TOUCH_OUTSIDE);
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    private final class VolumeSeekBarChangeListener implements OnSeekBarChangeListener {
+        private final VolumeRow mRow;
+
+        private VolumeSeekBarChangeListener(VolumeRow row) {
+            mRow = row;
+        }
+
+        @Override
+        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+            if (mRow.ss == null) return;
+            if (D.BUG) Log.d(TAG, AudioSystem.streamToString(mRow.stream)
+                    + " onProgressChanged " + progress + " fromUser=" + fromUser);
+            if (!fromUser) return;
+            if (mRow.ss.levelMin > 0) {
+                final int minProgress = mRow.ss.levelMin * 100;
+                if (progress < minProgress) {
+                    seekBar.setProgress(minProgress);
+                }
+            }
+            final int userLevel = getImpliedLevel(seekBar, progress);
+            if (mRow.ss.level != userLevel || mRow.ss.muted && userLevel > 0) {
+                mRow.userAttempt = SystemClock.uptimeMillis();
+                if (mAutomute) {
+                    if (mRow.stream != AudioManager.STREAM_RING) {
+                        if (userLevel > 0 && mRow.ss.muted) {
+                            mController.setStreamMute(mRow.stream, false);
+                        }
+                        if (userLevel == 0 && mRow.ss.muteSupported && !mRow.ss.muted) {
+                            mController.setStreamMute(mRow.stream, true);
+                        }
+                    }
+                }
+                if (mRow.requestedLevel != userLevel) {
+                    mController.setStreamVolume(mRow.stream, userLevel);
+                    mRow.requestedLevel = userLevel;
+                    Events.writeEvent(Events.EVENT_TOUCH_LEVEL_CHANGED, mRow.stream, userLevel);
+                }
+            }
+        }
+
+        @Override
+        public void onStartTrackingTouch(SeekBar seekBar) {
+            if (D.BUG) Log.d(TAG, "onStartTrackingTouch"+ " " + mRow.stream);
+            mController.setActiveStream(mRow.stream);
+            mRow.tracking = true;
+        }
+
+        @Override
+        public void onStopTrackingTouch(SeekBar seekBar) {
+            if (D.BUG) Log.d(TAG, "onStopTrackingTouch"+ " " + mRow.stream);
+            mRow.tracking = false;
+            mRow.userAttempt = SystemClock.uptimeMillis();
+            int userLevel = getImpliedLevel(seekBar, seekBar.getProgress());
+            if (mRow.ss.level != userLevel) {
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(H.RECHECK, mRow),
+                        USER_ATTEMPT_GRACE_PERIOD);
+            }
+        }
+    }
+
+    private static class VolumeRow {
+        private View view;
+        private View space;
+        private TextView header;
+        private ImageButton icon;
+        private SeekBar slider;
+        private ImageButton settingsButton;
+        private int stream;
+        private StreamState ss;
+        private long userAttempt;  // last user-driven slider change
+        private boolean tracking;  // tracking slider touch
+        private int requestedLevel;
+        private int iconRes;
+        private int iconMuteRes;
+        private boolean important;
+        private int cachedIconRes;
+        private int iconState;  // from Events
+        private boolean cachedShowHeaders = Prefs.DEFAULT_SHOW_HEADERS;
+        private int cachedExpandButtonRes;
+        private ObjectAnimator anim;  // slider progress animation for non-touch-related updates
+        private int animTargetProgress;
+        private int lastAudibleLevel = 1;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
new file mode 100644
index 0000000..741e498
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java
@@ -0,0 +1,120 @@
+/*
+ * 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.volume;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.media.AudioManager;
+import android.media.VolumePolicy;
+import android.os.Bundle;
+import android.os.Handler;
+
+import com.android.systemui.SystemUI;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.qs.tiles.DndTile;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Implementation of VolumeComponent backed by the new volume dialog.
+ */
+public class VolumeDialogComponent implements VolumeComponent {
+    private final SystemUI mSysui;
+    private final Context mContext;
+    private final VolumeDialogController mController;
+    private final ZenModeController mZenModeController;
+    private final VolumeDialog mDialog;
+
+    public VolumeDialogComponent(SystemUI sysui, Context context, Handler handler,
+            ZenModeController zen) {
+        mSysui = sysui;
+        mContext = context;
+        mController = new VolumeDialogController(context, null) {
+            @Override
+            protected void onUserActivityW() {
+                sendUserActivity();
+            }
+        };
+        mZenModeController = zen;
+        mDialog = new VolumeDialog(context, mController, zen) {
+            @Override
+            protected void onZenSettingsClickedH() {
+                startZenSettings();
+            }
+        };
+        applyConfiguration();
+    }
+
+    private void sendUserActivity() {
+        final KeyguardViewMediator kvm = mSysui.getComponent(KeyguardViewMediator.class);
+        if (kvm != null) {
+            kvm.userActivity();
+        }
+    }
+
+    private void applyConfiguration() {
+        mDialog.setStreamImportant(AudioManager.STREAM_ALARM, true);
+        mDialog.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
+        mDialog.setShowHeaders(false);
+        mDialog.setShowFooter(true);
+        mDialog.setZenFooter(true);
+        mDialog.setAutomute(true);
+        mDialog.setSilentMode(false);
+        mController.setVolumePolicy(VolumePolicy.DEFAULT);
+        mController.showDndTile(false);
+    }
+
+    @Override
+    public ZenModeController getZenController() {
+        return mZenModeController;
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        // noop
+    }
+
+    @Override
+    public void dismissNow() {
+        mController.dismiss();
+    }
+
+    @Override
+    public void dispatchDemoCommand(String command, Bundle args) {
+        // noop
+    }
+
+    @Override
+    public void register() {
+        mController.register();
+        DndTile.setCombinedIcon(mContext, true);
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        mController.dump(fd, pw, args);
+        mDialog.dump(pw);
+    }
+
+    private void startZenSettings() {
+        mSysui.getComponent(PhoneStatusBar.class).startActivityDismissingKeyguard(
+                ZenModePanel.ZEN_SETTINGS, true /* onlyProvisioned */, true /* dismissShade */);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
new file mode 100644
index 0000000..ae5312e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java
@@ -0,0 +1,962 @@
+/*
+ * 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.volume;
+
+import android.app.NotificationManager;
+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.database.ContentObserver;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.media.IVolumeController;
+import android.media.VolumePolicy;
+import android.media.session.MediaController.PlaybackInfo;
+import android.media.session.MediaSession.Token;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.service.notification.Condition;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.systemui.R;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ *  Source of truth for all state / events related to the volume dialog.  No presentation.
+ *
+ *  All work done on a dedicated background worker thread & associated worker.
+ *
+ *  Methods ending in "W" must be called on the worker thread.
+ */
+public class VolumeDialogController {
+    private static final String TAG = Util.logTag(VolumeDialogController.class);
+
+    private static final int DYNAMIC_STREAM_START_INDEX = 100;
+    private static final int VIBRATE_HINT_DURATION = 50;
+
+    private static final int[] STREAMS = {
+        AudioSystem.STREAM_ALARM,
+        AudioSystem.STREAM_BLUETOOTH_SCO,
+        AudioSystem.STREAM_DTMF,
+        AudioSystem.STREAM_MUSIC,
+        AudioSystem.STREAM_NOTIFICATION,
+        AudioSystem.STREAM_RING,
+        AudioSystem.STREAM_SYSTEM,
+        AudioSystem.STREAM_SYSTEM_ENFORCED,
+        AudioSystem.STREAM_TTS,
+        AudioSystem.STREAM_VOICE_CALL,
+    };
+
+    private final HandlerThread mWorkerThread;
+    private final W mWorker;
+    private final Context mContext;
+    private final AudioManager mAudio;
+    private final NotificationManager mNoMan;
+    private final ComponentName mComponent;
+    private final SettingObserver mObserver;
+    private final Receiver mReceiver = new Receiver();
+    private final MediaSessions mMediaSessions;
+    private final VC mVolumeController = new VC();
+    private final C mCallbacks = new C();
+    private final State mState = new State();
+    private final String[] mStreamTitles;
+    private final MediaSessionsCallbacks mMediaSessionsCallbacksW = new MediaSessionsCallbacks();
+    private final Vibrator mVibrator;
+    private final boolean mHasVibrator;
+
+    private boolean mEnabled;
+    private boolean mDestroyed;
+    private VolumePolicy mVolumePolicy = new VolumePolicy(true, true, false, 400);
+    private boolean mShowDndTile = false;
+
+    public VolumeDialogController(Context context, ComponentName component) {
+        mContext = context.getApplicationContext();
+        Events.writeEvent(Events.EVENT_COLLECTION_STARTED);
+        mComponent = component;
+        mWorkerThread = new HandlerThread(VolumeDialogController.class.getSimpleName());
+        mWorkerThread.start();
+        mWorker = new W(mWorkerThread.getLooper());
+        mMediaSessions = createMediaSessions(mContext, mWorkerThread.getLooper(),
+                mMediaSessionsCallbacksW);
+        mAudio = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        mNoMan = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        mObserver = new SettingObserver(mWorker);
+        mObserver.init();
+        mReceiver.init();
+        mStreamTitles = mContext.getResources().getStringArray(R.array.volume_stream_titles);
+        mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
+        mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
+    }
+
+    public void dismiss() {
+        mCallbacks.onDismissRequested(Events.DISMISS_REASON_VOLUME_CONTROLLER);
+    }
+
+    public void register() {
+        try {
+            mAudio.setVolumeController(mVolumeController);
+        } catch (SecurityException e) {
+            Log.w(TAG, "Unable to set the volume controller", e);
+            return;
+        }
+        setVolumePolicy(mVolumePolicy);
+        showDndTile(mShowDndTile);
+        try {
+            mMediaSessions.init();
+        } catch (SecurityException e) {
+            Log.w(TAG, "No access to media sessions", e);
+        }
+    }
+
+    public void setVolumePolicy(VolumePolicy policy) {
+        mVolumePolicy = policy;
+        try {
+            mAudio.setVolumePolicy(mVolumePolicy);
+        } catch (NoSuchMethodError e) {
+            Log.w(TAG, "No volume policy api");
+        }
+    }
+
+    protected MediaSessions createMediaSessions(Context context, Looper looper,
+            MediaSessions.Callbacks callbacks) {
+        return new MediaSessions(context, looper, callbacks);
+    }
+
+    public void destroy() {
+        if (D.BUG) Log.d(TAG, "destroy");
+        if (mDestroyed) return;
+        mDestroyed = true;
+        Events.writeEvent(Events.EVENT_COLLECTION_STOPPED);
+        mMediaSessions.destroy();
+        mObserver.destroy();
+        mReceiver.destroy();
+        mWorkerThread.quitSafely();
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(VolumeDialogController.class.getSimpleName() + " state:");
+        pw.print("  mEnabled: "); pw.println(mEnabled);
+        pw.print("  mDestroyed: "); pw.println(mDestroyed);
+        pw.print("  mVolumePolicy: "); pw.println(mVolumePolicy);
+        pw.print("  mEnabled: "); pw.println(mEnabled);
+        pw.print("  mShowDndTile: "); pw.println(mShowDndTile);
+        pw.print("  mHasVibrator: "); pw.println(mHasVibrator);
+        pw.print("  mRemoteStreams: "); pw.println(mMediaSessionsCallbacksW.mRemoteStreams
+                .values());
+        pw.println();
+        mMediaSessions.dump(pw);
+    }
+
+    public void addCallback(Callbacks callback, Handler handler) {
+        mCallbacks.add(callback, handler);
+    }
+
+    public void removeCallback(Callbacks callback) {
+        mCallbacks.remove(callback);
+    }
+
+    public void getState() {
+        if (mDestroyed) return;
+        mWorker.sendEmptyMessage(W.GET_STATE);
+    }
+
+    public void notifyVisible(boolean visible) {
+        if (mDestroyed) return;
+        mWorker.obtainMessage(W.NOTIFY_VISIBLE, visible ? 1 : 0, 0).sendToTarget();
+    }
+
+    public void userActivity() {
+        if (mDestroyed) return;
+        mWorker.removeMessages(W.USER_ACTIVITY);
+        mWorker.sendEmptyMessage(W.USER_ACTIVITY);
+    }
+
+    public void setRingerMode(int value, boolean external) {
+        if (mDestroyed) return;
+        mWorker.obtainMessage(W.SET_RINGER_MODE, value, external ? 1 : 0).sendToTarget();
+    }
+
+    public void setZenMode(int value) {
+        if (mDestroyed) return;
+        mWorker.obtainMessage(W.SET_ZEN_MODE, value, 0).sendToTarget();
+    }
+
+    public void setExitCondition(Condition condition) {
+        if (mDestroyed) return;
+        mWorker.obtainMessage(W.SET_EXIT_CONDITION, condition).sendToTarget();
+    }
+
+    public void setStreamMute(int stream, boolean mute) {
+        if (mDestroyed) return;
+        mWorker.obtainMessage(W.SET_STREAM_MUTE, stream, mute ? 1 : 0).sendToTarget();
+    }
+
+    public void setStreamVolume(int stream, int level) {
+        if (mDestroyed) return;
+        mWorker.obtainMessage(W.SET_STREAM_VOLUME, stream, level).sendToTarget();
+    }
+
+    public void setActiveStream(int stream) {
+        if (mDestroyed) return;
+        mWorker.obtainMessage(W.SET_ACTIVE_STREAM, stream, 0).sendToTarget();
+    }
+
+    public void vibrate() {
+        if (mHasVibrator) {
+            mVibrator.vibrate(VIBRATE_HINT_DURATION);
+        }
+    }
+
+    public boolean hasVibrator() {
+        return mHasVibrator;
+    }
+
+    private void onNotifyVisibleW(boolean visible) {
+        if (mDestroyed) return;
+        mAudio.notifyVolumeControllerVisible(mVolumeController, visible);
+        if (!visible) {
+            if (updateActiveStreamW(-1)) {
+                mCallbacks.onStateChanged(mState);
+            }
+        }
+    }
+
+    protected void onUserActivityW() {
+        // hook for subclasses
+    }
+
+    private boolean checkRoutedToBluetoothW(int stream) {
+        boolean changed = false;
+        if (stream == AudioManager.STREAM_MUSIC) {
+            final boolean routedToBluetooth =
+                    (mAudio.getDevicesForStream(AudioManager.STREAM_MUSIC) &
+                            (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
+                            AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
+                            AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0;
+            changed |= updateStreamRoutedToBluetoothW(stream, routedToBluetooth);
+        }
+        return changed;
+    }
+
+    private void onVolumeChangedW(int stream, int flags) {
+        final boolean showUI = (flags & AudioManager.FLAG_SHOW_UI) != 0;
+        final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;
+        final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;
+        final boolean showSilentHint = (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0;
+        boolean changed = false;
+        if (showUI) {
+            changed |= updateActiveStreamW(stream);
+        }
+        changed |= updateStreamLevelW(stream, mAudio.getLastAudibleStreamVolume(stream));
+        changed |= checkRoutedToBluetoothW(showUI ? AudioManager.STREAM_MUSIC : stream);
+        if (changed) {
+            mCallbacks.onStateChanged(mState);
+        }
+        if (showUI) {
+            mCallbacks.onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
+        }
+        if (showVibrateHint) {
+            mCallbacks.onShowVibrateHint();
+        }
+        if (showSilentHint) {
+            mCallbacks.onShowSilentHint();
+        }
+        if (changed && fromKey) {
+            Events.writeEvent(Events.EVENT_KEY);
+        }
+    }
+
+    private boolean updateActiveStreamW(int activeStream) {
+        if (activeStream == mState.activeStream) return false;
+        mState.activeStream = activeStream;
+        Events.writeEvent(Events.EVENT_ACTIVE_STREAM_CHANGED, activeStream);
+        if (D.BUG) Log.d(TAG, "updateActiveStreamW " + activeStream);
+        final int s = activeStream < DYNAMIC_STREAM_START_INDEX ? activeStream : -1;
+        if (D.BUG) Log.d(TAG, "forceVolumeControlStream " + s);
+        mAudio.forceVolumeControlStream(s);
+        return true;
+    }
+
+    private StreamState streamStateW(int stream) {
+        StreamState ss = mState.states.get(stream);
+        if (ss == null) {
+            ss = new StreamState();
+            mState.states.put(stream, ss);
+        }
+        return ss;
+    }
+
+    private void onGetStateW() {
+        for (int stream : STREAMS) {
+            updateStreamLevelW(stream, mAudio.getLastAudibleStreamVolume(stream));
+            streamStateW(stream).levelMin = mAudio.getStreamMinVolume(stream);
+            streamStateW(stream).levelMax = mAudio.getStreamMaxVolume(stream);
+            updateStreamMuteW(stream, mAudio.isStreamMute(stream));
+            final StreamState ss = streamStateW(stream);
+            ss.muteSupported = mAudio.isStreamAffectedByMute(stream);
+            ss.name = mStreamTitles[stream];
+            checkRoutedToBluetoothW(stream);
+        }
+        updateRingerModeExternalW(mAudio.getRingerMode());
+        updateZenModeW();
+        updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
+        updateExitConditionW();
+        mCallbacks.onStateChanged(mState);
+    }
+
+    private boolean updateStreamRoutedToBluetoothW(int stream, boolean routedToBluetooth) {
+        final StreamState ss = streamStateW(stream);
+        if (ss.routedToBluetooth == routedToBluetooth) return false;
+        ss.routedToBluetooth = routedToBluetooth;
+        if (D.BUG) Log.d(TAG, "updateStreamRoutedToBluetoothW stream=" + stream
+                + " routedToBluetooth=" + routedToBluetooth);
+        return true;
+    }
+
+    private boolean updateStreamLevelW(int stream, int level) {
+        final StreamState ss = streamStateW(stream);
+        if (ss.level == level) return false;
+        ss.level = level;
+        if (isLogWorthy(stream)) {
+            Events.writeEvent(Events.EVENT_LEVEL_CHANGED, stream, level);
+        }
+        return true;
+    }
+
+    private static boolean isLogWorthy(int stream) {
+        switch (stream) {
+            case AudioSystem.STREAM_ALARM:
+            case AudioSystem.STREAM_BLUETOOTH_SCO:
+            case AudioSystem.STREAM_MUSIC:
+            case AudioSystem.STREAM_RING:
+            case AudioSystem.STREAM_SYSTEM:
+            case AudioSystem.STREAM_VOICE_CALL:
+                return true;
+        }
+        return false;
+    }
+
+    private boolean updateStreamMuteW(int stream, boolean muted) {
+        final StreamState ss = streamStateW(stream);
+        if (ss.muted == muted) return false;
+        ss.muted = muted;
+        if (isLogWorthy(stream)) {
+            Events.writeEvent(Events.EVENT_MUTE_CHANGED, stream, muted);
+        }
+        if (muted && isRinger(stream)) {
+            updateRingerModeInternalW(mAudio.getRingerModeInternal());
+        }
+        return true;
+    }
+
+    private static boolean isRinger(int stream) {
+        return stream == AudioManager.STREAM_RING || stream == AudioManager.STREAM_NOTIFICATION;
+    }
+
+    private boolean updateExitConditionW() {
+        final Condition exitCondition = mNoMan.getZenModeCondition();
+        if (Objects.equals(mState.exitCondition, exitCondition)) return false;
+        mState.exitCondition = exitCondition;
+        return true;
+    }
+
+    private boolean updateEffectsSuppressorW(ComponentName effectsSuppressor) {
+        if (Objects.equals(mState.effectsSuppressor, effectsSuppressor)) return false;
+        mState.effectsSuppressor = effectsSuppressor;
+        mState.effectsSuppressorName = getApplicationName(mContext, mState.effectsSuppressor);
+        Events.writeEvent(Events.EVENT_SUPPRESSOR_CHANGED, mState.effectsSuppressor,
+                mState.effectsSuppressorName);
+        return true;
+    }
+
+    private static String getApplicationName(Context context, ComponentName component) {
+        if (component == null) return null;
+        final PackageManager pm = context.getPackageManager();
+        final String pkg = component.getPackageName();
+        try {
+            final ApplicationInfo ai = pm.getApplicationInfo(pkg, 0);
+            final String rt = Objects.toString(ai.loadLabel(pm), "").trim();
+            if (rt.length() > 0) {
+                return rt;
+            }
+        } catch (NameNotFoundException e) {}
+        return pkg;
+    }
+
+    private boolean updateZenModeW() {
+        final int zen = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
+        if (mState.zenMode == zen) return false;
+        mState.zenMode = zen;
+        Events.writeEvent(Events.EVENT_ZEN_MODE_CHANGED, zen);
+        return true;
+    }
+
+    private boolean updateRingerModeExternalW(int rm) {
+        if (rm == mState.ringerModeExternal) return false;
+        mState.ringerModeExternal = rm;
+        Events.writeEvent(Events.EVENT_EXTERNAL_RINGER_MODE_CHANGED, rm);
+        return true;
+    }
+
+    private boolean updateRingerModeInternalW(int rm) {
+        if (rm == mState.ringerModeInternal) return false;
+        mState.ringerModeInternal = rm;
+        Events.writeEvent(Events.EVENT_INTERNAL_RINGER_MODE_CHANGED, rm);
+        return true;
+    }
+
+    private void onSetRingerModeW(int mode, boolean external) {
+        if (external) {
+            mAudio.setRingerMode(mode);
+        } else {
+            mAudio.setRingerModeInternal(mode);
+        }
+    }
+
+    private void onSetStreamMuteW(int stream, boolean mute) {
+        mAudio.adjustStreamVolume(stream, mute ? AudioManager.ADJUST_MUTE
+                : AudioManager.ADJUST_UNMUTE, 0);
+    }
+
+    private void onSetStreamVolumeW(int stream, int level) {
+        if (D.BUG) Log.d(TAG, "onSetStreamVolume " + stream + " level=" + level);
+        if (stream >= DYNAMIC_STREAM_START_INDEX) {
+            mMediaSessionsCallbacksW.setStreamVolume(stream, level);
+            return;
+        }
+        mAudio.setStreamVolume(stream, level, 0);
+    }
+
+    private void onSetActiveStreamW(int stream) {
+        boolean changed = updateActiveStreamW(stream);
+        if (changed) {
+            mCallbacks.onStateChanged(mState);
+        }
+    }
+
+    private void onSetExitConditionW(Condition condition) {
+        mNoMan.setZenModeCondition(condition);
+    }
+
+    private void onSetZenModeW(int mode) {
+        if (D.BUG) Log.d(TAG, "onSetZenModeW " + mode);
+        mNoMan.setZenMode(mode);
+    }
+
+    private void onDismissRequestedW(int reason) {
+        mCallbacks.onDismissRequested(reason);
+    }
+
+    public void showDndTile(boolean visible) {
+        if (D.BUG) Log.d(TAG, "showDndTile");
+        mContext.sendBroadcast(new Intent("com.android.systemui.dndtile.SET_VISIBLE")
+                .putExtra("visible", visible));
+    }
+
+    private final class VC extends IVolumeController.Stub {
+        private final String TAG = VolumeDialogController.TAG + ".VC";
+
+        @Override
+        public void displaySafeVolumeWarning(int flags) throws RemoteException {
+            // noop
+        }
+
+        @Override
+        public void volumeChanged(int streamType, int flags) throws RemoteException {
+            if (D.BUG) Log.d(TAG, "volumeChanged " + AudioSystem.streamToString(streamType)
+                    + " " + Util.audioManagerFlagsToString(flags));
+            if (mDestroyed) return;
+            mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget();
+        }
+
+        @Override
+        public void masterMuteChanged(int flags) throws RemoteException {
+            if (D.BUG) Log.d(TAG, "masterMuteChanged");
+        }
+
+        @Override
+        public void setLayoutDirection(int layoutDirection) throws RemoteException {
+            if (D.BUG) Log.d(TAG, "setLayoutDirection");
+            if (mDestroyed) return;
+            mWorker.obtainMessage(W.LAYOUT_DIRECTION_CHANGED, layoutDirection, 0).sendToTarget();
+        }
+
+        @Override
+        public void dismiss() throws RemoteException {
+            if (D.BUG) Log.d(TAG, "dismiss requested");
+            if (mDestroyed) return;
+            mWorker.obtainMessage(W.DISMISS_REQUESTED, Events.DISMISS_REASON_VOLUME_CONTROLLER, 0)
+                    .sendToTarget();
+            mWorker.sendEmptyMessage(W.DISMISS_REQUESTED);
+        }
+    }
+
+    private final class W extends Handler {
+        private static final int VOLUME_CHANGED = 1;
+        private static final int DISMISS_REQUESTED = 2;
+        private static final int GET_STATE = 3;
+        private static final int SET_RINGER_MODE = 4;
+        private static final int SET_ZEN_MODE = 5;
+        private static final int SET_EXIT_CONDITION = 6;
+        private static final int SET_STREAM_MUTE = 7;
+        private static final int LAYOUT_DIRECTION_CHANGED = 8;
+        private static final int CONFIGURATION_CHANGED = 9;
+        private static final int SET_STREAM_VOLUME = 10;
+        private static final int SET_ACTIVE_STREAM = 11;
+        private static final int NOTIFY_VISIBLE = 12;
+        private static final int USER_ACTIVITY = 13;
+
+        W(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case VOLUME_CHANGED: onVolumeChangedW(msg.arg1, msg.arg2); break;
+                case DISMISS_REQUESTED: onDismissRequestedW(msg.arg1); break;
+                case GET_STATE: onGetStateW(); break;
+                case SET_RINGER_MODE: onSetRingerModeW(msg.arg1, msg.arg2 != 0); break;
+                case SET_ZEN_MODE: onSetZenModeW(msg.arg1); break;
+                case SET_EXIT_CONDITION: onSetExitConditionW((Condition) msg.obj); break;
+                case SET_STREAM_MUTE: onSetStreamMuteW(msg.arg1, msg.arg2 != 0); break;
+                case LAYOUT_DIRECTION_CHANGED: mCallbacks.onLayoutDirectionChanged(msg.arg1); break;
+                case CONFIGURATION_CHANGED: mCallbacks.onConfigurationChanged(); break;
+                case SET_STREAM_VOLUME: onSetStreamVolumeW(msg.arg1, msg.arg2); break;
+                case SET_ACTIVE_STREAM: onSetActiveStreamW(msg.arg1); break;
+                case NOTIFY_VISIBLE: onNotifyVisibleW(msg.arg1 != 0);
+                case USER_ACTIVITY: onUserActivityW();
+            }
+        }
+    }
+
+    private final class C implements Callbacks {
+        private final HashMap<Callbacks, Handler> mCallbackMap = new HashMap<>();
+
+        public void add(Callbacks callback, Handler handler) {
+            if (callback == null || handler == null) throw new IllegalArgumentException();
+            mCallbackMap.put(callback, handler);
+        }
+
+        public void remove(Callbacks callback) {
+            mCallbackMap.remove(callback);
+        }
+
+        @Override
+        public void onShowRequested(final int reason) {
+            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
+                entry.getValue().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        entry.getKey().onShowRequested(reason);
+                    }
+                });
+            }
+        }
+
+        @Override
+        public void onDismissRequested(final int reason) {
+            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
+                entry.getValue().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        entry.getKey().onDismissRequested(reason);
+                    }
+                });
+            }
+        }
+
+        @Override
+        public void onStateChanged(final State state) {
+            final long time = System.currentTimeMillis();
+            final State copy = state.copy();
+            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
+                entry.getValue().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        entry.getKey().onStateChanged(copy);
+                    }
+                });
+            }
+            Events.writeState(time, copy);
+        }
+
+        @Override
+        public void onLayoutDirectionChanged(final int layoutDirection) {
+            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
+                entry.getValue().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        entry.getKey().onLayoutDirectionChanged(layoutDirection);
+                    }
+                });
+            }
+        }
+
+        @Override
+        public void onConfigurationChanged() {
+            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
+                entry.getValue().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        entry.getKey().onConfigurationChanged();
+                    }
+                });
+            }
+        }
+
+        @Override
+        public void onShowVibrateHint() {
+            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
+                entry.getValue().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        entry.getKey().onShowVibrateHint();
+                    }
+                });
+            }
+        }
+
+        @Override
+        public void onShowSilentHint() {
+            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
+                entry.getValue().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        entry.getKey().onShowSilentHint();
+                    }
+                });
+            }
+        }
+
+        @Override
+        public void onScreenOff() {
+            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
+                entry.getValue().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        entry.getKey().onScreenOff();
+                    }
+                });
+            }
+        }
+    }
+
+
+    private final class SettingObserver extends ContentObserver {
+        private final Uri SERVICE_URI = Settings.Secure.getUriFor(
+                Settings.Secure.VOLUME_CONTROLLER_SERVICE_COMPONENT);
+        private final Uri ZEN_MODE_URI =
+                Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
+        private final Uri ZEN_MODE_CONFIG_URI =
+                Settings.Global.getUriFor(Settings.Global.ZEN_MODE_CONFIG_ETAG);
+
+        public SettingObserver(Handler handler) {
+            super(handler);
+        }
+
+        public void init() {
+            mContext.getContentResolver().registerContentObserver(SERVICE_URI, false, this);
+            mContext.getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this);
+            mContext.getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_URI, false, this);
+            onChange(true, SERVICE_URI);
+        }
+
+        public void destroy() {
+            mContext.getContentResolver().unregisterContentObserver(this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            boolean changed = false;
+            if (SERVICE_URI.equals(uri)) {
+                final String setting = Settings.Secure.getString(mContext.getContentResolver(),
+                        Settings.Secure.VOLUME_CONTROLLER_SERVICE_COMPONENT);
+                final boolean enabled = setting != null && mComponent != null
+                        && mComponent.equals(ComponentName.unflattenFromString(setting));
+                if (enabled == mEnabled) return;
+                if (enabled) {
+                    register();
+                }
+                mEnabled = enabled;
+            }
+            if (ZEN_MODE_URI.equals(uri)) {
+                changed = updateZenModeW();
+            }
+            if (ZEN_MODE_CONFIG_URI.equals(uri)) {
+                changed = updateExitConditionW();
+            }
+            if (changed) {
+                mCallbacks.onStateChanged(mState);
+            }
+        }
+    }
+
+    private final class Receiver extends BroadcastReceiver {
+
+        public void init() {
+            final IntentFilter filter = new IntentFilter();
+            filter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
+            filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
+            filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
+            filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
+            filter.addAction(AudioManager.STREAM_MUTE_CHANGED_ACTION);
+            filter.addAction(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
+            filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+            filter.addAction(Intent.ACTION_SCREEN_OFF);
+            mContext.registerReceiver(this, filter, null, mWorker);
+        }
+
+        public void destroy() {
+            mContext.unregisterReceiver(this);
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            boolean changed = false;
+            if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
+                final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+                final int level = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
+                final int oldLevel = intent
+                        .getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, -1);
+                if (D.BUG) Log.d(TAG, "onReceive VOLUME_CHANGED_ACTION stream=" + stream
+                        + " level=" + level + " oldLevel=" + oldLevel);
+                changed = updateStreamLevelW(stream, level);
+            } else if (action.equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION)) {
+                final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+                final int devices = intent
+                        .getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, -1);
+                final int oldDevices = intent
+                        .getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, -1);
+                if (D.BUG) Log.d(TAG, "onReceive STREAM_DEVICES_CHANGED_ACTION stream="
+                        + stream + " devices=" + devices + " oldDevices=" + oldDevices);
+                changed = checkRoutedToBluetoothW(stream);
+            } else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
+                final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
+                if (D.BUG) Log.d(TAG, "onReceive RINGER_MODE_CHANGED_ACTION rm="
+                        + Util.ringerModeToString(rm));
+                changed = updateRingerModeExternalW(rm);
+            } else if (action.equals(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)) {
+                final int rm = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
+                if (D.BUG) Log.d(TAG, "onReceive INTERNAL_RINGER_MODE_CHANGED_ACTION rm="
+                        + Util.ringerModeToString(rm));
+                changed = updateRingerModeInternalW(rm);
+            } else if (action.equals(AudioManager.STREAM_MUTE_CHANGED_ACTION)) {
+                final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+                final boolean muted = intent
+                        .getBooleanExtra(AudioManager.EXTRA_STREAM_VOLUME_MUTED, false);
+                if (D.BUG) Log.d(TAG, "onReceive STREAM_MUTE_CHANGED_ACTION stream=" + stream
+                        + " muted=" + muted);
+                changed = updateStreamMuteW(stream, muted);
+            } else if (action.equals(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED)) {
+                if (D.BUG) Log.d(TAG, "onReceive ACTION_EFFECTS_SUPPRESSOR_CHANGED");
+                changed = updateEffectsSuppressorW(mNoMan.getEffectsSuppressor());
+            } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
+                if (D.BUG) Log.d(TAG, "onReceive ACTION_CONFIGURATION_CHANGED");
+                mCallbacks.onConfigurationChanged();
+            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
+                if (D.BUG) Log.d(TAG, "onReceive ACTION_SCREEN_OFF");
+                mCallbacks.onScreenOff();
+            }
+            if (changed) {
+                mCallbacks.onStateChanged(mState);
+            }
+        }
+    }
+
+    private final class MediaSessionsCallbacks implements MediaSessions.Callbacks {
+        private final HashMap<Token, Integer> mRemoteStreams = new HashMap<>();
+
+        private int mNextStream = DYNAMIC_STREAM_START_INDEX;
+
+        @Override
+        public void onRemoteUpdate(Token token, String name, PlaybackInfo pi) {
+            if (!mRemoteStreams.containsKey(token)) {
+                mRemoteStreams.put(token, mNextStream);
+                if (D.BUG) Log.d(TAG, "onRemoteUpdate: " + name + " is stream " + mNextStream);
+                mNextStream++;
+            }
+            final int stream = mRemoteStreams.get(token);
+            boolean changed = mState.states.indexOfKey(stream) < 0;
+            final StreamState ss = streamStateW(stream);
+            ss.dynamic = true;
+            ss.levelMin = 0;
+            ss.levelMax = pi.getMaxVolume();
+            if (ss.level != pi.getCurrentVolume()) {
+                ss.level = pi.getCurrentVolume();
+                changed = true;
+            }
+            if (!Objects.equals(ss.name, name)) {
+                ss.name = name;
+                changed = true;
+            }
+            if (changed) {
+                if (D.BUG) Log.d(TAG, "onRemoteUpdate: " + name + ": " + ss.level
+                        + " of " + ss.levelMax);
+                mCallbacks.onStateChanged(mState);
+            }
+        }
+
+        @Override
+        public void onRemoteVolumeChanged(Token token, int flags) {
+            final int stream = mRemoteStreams.get(token);
+            final boolean showUI = (flags & AudioManager.FLAG_SHOW_UI) != 0;
+            boolean changed = updateActiveStreamW(stream);
+            if (showUI) {
+                changed |= checkRoutedToBluetoothW(AudioManager.STREAM_MUSIC);
+            }
+            if (changed) {
+                mCallbacks.onStateChanged(mState);
+            }
+            if (showUI) {
+                mCallbacks.onShowRequested(Events.SHOW_REASON_REMOTE_VOLUME_CHANGED);
+            }
+        }
+
+        @Override
+        public void onRemoteRemoved(Token token) {
+            final int stream = mRemoteStreams.get(token);
+            mState.states.remove(stream);
+            if (mState.activeStream == stream) {
+                updateActiveStreamW(-1);
+            }
+            mCallbacks.onStateChanged(mState);
+        }
+
+        public void setStreamVolume(int stream, int level) {
+            final Token t = findToken(stream);
+            if (t == null) {
+                Log.w(TAG, "setStreamVolume: No token found for stream: " + stream);
+                return;
+            }
+            mMediaSessions.setVolume(t, level);
+        }
+
+        private Token findToken(int stream) {
+            for (Map.Entry<Token, Integer> entry : mRemoteStreams.entrySet()) {
+                if (entry.getValue().equals(stream)) {
+                    return entry.getKey();
+                }
+            }
+            return null;
+        }
+    }
+
+    public static final class StreamState {
+        public boolean dynamic;
+        public int level;
+        public int levelMin;
+        public int levelMax;
+        public boolean muted;
+        public boolean muteSupported;
+        public String name;
+        public boolean routedToBluetooth;
+
+        public StreamState copy() {
+            final StreamState rt = new StreamState();
+            rt.dynamic = dynamic;
+            rt.level = level;
+            rt.levelMin = levelMin;
+            rt.levelMax = levelMax;
+            rt.muted = muted;
+            rt.muteSupported = muteSupported;
+            rt.name = name;
+            rt.routedToBluetooth = routedToBluetooth;
+            return rt;
+        }
+    }
+
+    public static final class State {
+        public static int NO_ACTIVE_STREAM = -1;
+
+        public final SparseArray<StreamState> states = new SparseArray<StreamState>();
+
+        public int ringerModeInternal;
+        public int ringerModeExternal;
+        public int zenMode;
+        public ComponentName effectsSuppressor;
+        public String effectsSuppressorName;
+        public Condition exitCondition;
+        public int activeStream = NO_ACTIVE_STREAM;
+
+        public State copy() {
+            final State rt = new State();
+            for (int i = 0; i < states.size(); i++) {
+                rt.states.put(states.keyAt(i), states.valueAt(i).copy());
+            }
+            rt.ringerModeExternal = ringerModeExternal;
+            rt.ringerModeInternal = ringerModeInternal;
+            rt.zenMode = zenMode;
+            if (effectsSuppressor != null) rt.effectsSuppressor = effectsSuppressor.clone();
+            rt.effectsSuppressorName = effectsSuppressorName;
+            if (exitCondition != null) rt.exitCondition = exitCondition.copy();
+            rt.activeStream = activeStream;
+            return rt;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder sb = new StringBuilder("{");
+            for (int i = 0; i < states.size(); i++) {
+                if (i > 0) sb.append(',');
+                final int stream = states.keyAt(i);
+                final StreamState ss = states.valueAt(i);
+                sb.append(AudioSystem.streamToString(stream)).append(":").append(ss.level)
+                        .append("[").append(ss.levelMin).append("..").append(ss.levelMax);
+                if (ss.muted) sb.append(" [MUTED]");
+            }
+            sb.append(",ringerModeExternal:").append(ringerModeExternal);
+            sb.append(",ringerModeInternal:").append(ringerModeInternal);
+            sb.append(",zenMode:").append(zenMode);
+            sb.append(",effectsSuppressor:").append(effectsSuppressor);
+            sb.append(",effectsSuppressorName:").append(effectsSuppressorName);
+            sb.append(",exitCondition:").append(exitCondition);
+            sb.append(",activeStream:").append(activeStream);
+            return sb.append('}').toString();
+        }
+    }
+
+    public interface Callbacks {
+        void onShowRequested(int reason);
+        void onDismissRequested(int reason);
+        void onStateChanged(State state);
+        void onLayoutDirectionChanged(int layoutDirection);
+        void onConfigurationChanged();
+        void onShowVibrateHint();
+        void onShowSilentHint();
+        void onScreenOff();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
index d16b818..50d5f7a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanel.java
@@ -384,7 +384,7 @@
         final Window window = mDialog.getWindow();
         window.requestFeature(Window.FEATURE_NO_TITLE);
         mDialog.setCanceledOnTouchOutside(true);
-        mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
+        mDialog.setContentView(com.android.systemui.R.layout.volume_panel_dialog);
         mDialog.setOnDismissListener(new OnDismissListener() {
             @Override
             public void onDismiss(DialogInterface dialog) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelComponent.java
new file mode 100644
index 0000000..fa6ea9e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelComponent.java
@@ -0,0 +1,184 @@
+/*
+ * 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.volume;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.media.AudioManager;
+import android.media.IRemoteVolumeController;
+import android.media.IVolumeController;
+import android.media.VolumePolicy;
+import android.media.session.ISessionController;
+import android.media.session.MediaController;
+import android.media.session.MediaSessionManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.qs.tiles.DndTile;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Implementation of VolumeComponent backed by the old volume panel.
+ */
+public class VolumePanelComponent implements VolumeComponent {
+
+    private final SystemUI mSysui;
+    private final Context mContext;
+    private final Handler mHandler;
+    private final VolumeController mVolumeController;
+    private final RemoteVolumeController mRemoteVolumeController;
+    private final AudioManager mAudioManager;
+    private final MediaSessionManager mMediaSessionManager;
+
+    private VolumePanel mPanel;
+    private int mDismissDelay;
+
+    public VolumePanelComponent(SystemUI sysui, Context context, Handler handler,
+            ZenModeController controller) {
+        mSysui = sysui;
+        mContext = context;
+        mHandler = handler;
+        mAudioManager = context.getSystemService(AudioManager.class);
+        mMediaSessionManager = context.getSystemService(MediaSessionManager.class);
+        mVolumeController = new VolumeController();
+        mRemoteVolumeController = new RemoteVolumeController();
+        mDismissDelay = mContext.getResources().getInteger(R.integer.volume_panel_dismiss_delay);
+        mPanel = new VolumePanel(mContext, controller);
+        mPanel.setCallback(new VolumePanel.Callback() {
+            @Override
+            public void onZenSettings() {
+                mHandler.removeCallbacks(mStartZenSettings);
+                mHandler.post(mStartZenSettings);
+            }
+
+            @Override
+            public void onInteraction() {
+                final KeyguardViewMediator kvm = mSysui.getComponent(KeyguardViewMediator.class);
+                if (kvm != null) {
+                    kvm.userActivity();
+                }
+            }
+
+            @Override
+            public void onVisible(boolean visible) {
+                if (mAudioManager != null && mVolumeController != null) {
+                    mAudioManager.notifyVolumeControllerVisible(mVolumeController, visible);
+                }
+            }
+        });
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mPanel != null) {
+            mPanel.dump(fd, pw, args);
+        }
+    }
+
+    public void register() {
+        mAudioManager.setVolumeController(mVolumeController);
+        mAudioManager.setVolumePolicy(VolumePolicy.DEFAULT);
+        mMediaSessionManager.setRemoteVolumeController(mRemoteVolumeController);
+        DndTile.setVisible(mContext, false);
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        if (mPanel != null) {
+            mPanel.onConfigurationChanged(newConfig);
+        }
+    }
+
+    @Override
+    public ZenModeController getZenController() {
+        return mPanel.getZenController();
+    }
+
+    @Override
+    public void dispatchDemoCommand(String command, Bundle args) {
+        mPanel.dispatchDemoCommand(command, args);
+    }
+
+    @Override
+    public void dismissNow() {
+        mPanel.postDismiss(0);
+    }
+
+    private final Runnable mStartZenSettings = new Runnable() {
+        @Override
+        public void run() {
+            mSysui.getComponent(PhoneStatusBar.class).startActivityDismissingKeyguard(
+                    ZenModePanel.ZEN_SETTINGS, true /* onlyProvisioned */, true /* dismissShade */);
+            mPanel.postDismiss(mDismissDelay);
+        }
+    };
+
+    private final class RemoteVolumeController extends IRemoteVolumeController.Stub {
+        @Override
+        public void remoteVolumeChanged(ISessionController binder, int flags)
+                throws RemoteException {
+            MediaController controller = new MediaController(mContext, binder);
+            mPanel.postRemoteVolumeChanged(controller, flags);
+        }
+
+        @Override
+        public void updateRemoteController(ISessionController session) throws RemoteException {
+            mPanel.postRemoteSliderVisibility(session != null);
+            // TODO stash default session in case the slider can be opened other
+            // than by remoteVolumeChanged.
+        }
+    }
+
+    /** For now, simply host an unmodified base volume panel in this process. */
+    private final class VolumeController extends IVolumeController.Stub {
+
+        @Override
+        public void displaySafeVolumeWarning(int flags) throws RemoteException {
+            mPanel.postDisplaySafeVolumeWarning(flags);
+        }
+
+        @Override
+        public void volumeChanged(int streamType, int flags)
+                throws RemoteException {
+            mPanel.postVolumeChanged(streamType, flags);
+        }
+
+        @Override
+        public void masterMuteChanged(int flags) throws RemoteException {
+            // no-op
+        }
+
+        @Override
+        public void setLayoutDirection(int layoutDirection)
+                throws RemoteException {
+            mPanel.postLayoutDirection(layoutDirection);
+        }
+
+        @Override
+        public void dismiss() throws RemoteException {
+            dismissNow();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
index ac08904..387aed0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeUI.java
@@ -29,25 +29,17 @@
 import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.media.AudioManager;
-import android.media.IRemoteVolumeController;
-import android.media.IVolumeController;
-import android.media.VolumePolicy;
-import android.media.session.ISessionController;
-import android.media.session.MediaController;
 import android.media.session.MediaSessionManager;
-import android.os.Bundle;
 import android.os.Handler;
-import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
-import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.qs.tiles.DndTile;
 import com.android.systemui.statusbar.ServiceMonitor;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
@@ -59,6 +51,8 @@
     private static final String TAG = "VolumeUI";
     private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
 
+    private static final boolean USE_OLD_VOLUME = SystemProperties.getBoolean("volume.old", false);
+
     private final Handler mHandler = new Handler();
     private final Receiver mReceiver = new Receiver();
     private final RestorationNotification mRestorationNotification = new RestorationNotification();
@@ -67,12 +61,10 @@
     private AudioManager mAudioManager;
     private NotificationManager mNotificationManager;
     private MediaSessionManager mMediaSessionManager;
-    private VolumeController mVolumeController;
-    private RemoteVolumeController mRemoteVolumeController;
     private ServiceMonitor mVolumeControllerService;
 
-    private VolumePanel mPanel;
-    private int mDismissDelay;
+    private VolumePanelComponent mOldVolume;
+    private VolumeDialogComponent mNewVolume;
 
     @Override
     public void start() {
@@ -83,10 +75,10 @@
                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
         mMediaSessionManager = (MediaSessionManager) mContext
                 .getSystemService(Context.MEDIA_SESSION_SERVICE);
-        initPanel();
-        mVolumeController = new VolumeController();
-        mRemoteVolumeController = new RemoteVolumeController();
-        putComponent(VolumeComponent.class, mVolumeController);
+        final ZenModeController zenController = new ZenModeControllerImpl(mContext, mHandler);
+        mOldVolume = new VolumePanelComponent(this, mContext, mHandler, zenController);
+        mNewVolume = new VolumeDialogComponent(this, mContext, null, zenController);
+        putComponent(VolumeComponent.class, getVolumeComponent());
         mReceiver.start();
         mVolumeControllerService = new ServiceMonitor(TAG, LOGD,
                 mContext, Settings.Secure.VOLUME_CONTROLLER_SERVICE_COMPONENT,
@@ -94,30 +86,30 @@
         mVolumeControllerService.start();
     }
 
+    private VolumeComponent getVolumeComponent() {
+        return USE_OLD_VOLUME ? mOldVolume : mNewVolume;
+    }
+
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        if (mPanel != null) {
-            mPanel.onConfigurationChanged(newConfig);
-        }
+        if (!mEnabled) return;
+        getVolumeComponent().onConfigurationChanged(newConfig);
     }
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.print("mEnabled="); pw.println(mEnabled);
+        if (!mEnabled) return;
         pw.print("mVolumeControllerService="); pw.println(mVolumeControllerService.getComponent());
-        if (mPanel != null) {
-            mPanel.dump(fd, pw, args);
-        }
+        getVolumeComponent().dump(fd, pw, args);
     }
 
-    private void setVolumeController(boolean register) {
+    private void setDefaultVolumeController(boolean register) {
         if (register) {
-            if (LOGD) Log.d(TAG, "Registering default volume controller");
-            mAudioManager.setVolumeController(mVolumeController);
-            mAudioManager.setVolumePolicy(VolumePolicy.DEFAULT);
-            mMediaSessionManager.setRemoteVolumeController(mRemoteVolumeController);
             DndTile.setVisible(mContext, false);
+            if (LOGD) Log.d(TAG, "Registering default volume controller");
+            getVolumeComponent().register();
         } else {
             if (LOGD) Log.d(TAG, "Unregistering default volume controller");
             mAudioManager.setVolumeController(null);
@@ -125,33 +117,6 @@
         }
     }
 
-    private void initPanel() {
-        mDismissDelay = mContext.getResources().getInteger(R.integer.volume_panel_dismiss_delay);
-        mPanel = new VolumePanel(mContext, new ZenModeControllerImpl(mContext, mHandler));
-        mPanel.setCallback(new VolumePanel.Callback() {
-            @Override
-            public void onZenSettings() {
-                mHandler.removeCallbacks(mStartZenSettings);
-                mHandler.post(mStartZenSettings);
-            }
-
-            @Override
-            public void onInteraction() {
-                final KeyguardViewMediator kvm = getComponent(KeyguardViewMediator.class);
-                if (kvm != null) {
-                    kvm.userActivity();
-                }
-            }
-
-            @Override
-            public void onVisible(boolean visible) {
-                if (mAudioManager != null && mVolumeController != null) {
-                    mAudioManager.notifyVolumeControllerVisible(mVolumeController, visible);
-                }
-            }
-        });
-    }
-
     private String getAppLabel(ComponentName component) {
         final String pkg = component.getPackageName();
         try {
@@ -179,83 +144,11 @@
         d.show();
     }
 
-    private final Runnable mStartZenSettings = new Runnable() {
-        @Override
-        public void run() {
-            getComponent(PhoneStatusBar.class).startActivityDismissingKeyguard(
-                    ZenModePanel.ZEN_SETTINGS, true /* onlyProvisioned */, true /* dismissShade */);
-            mPanel.postDismiss(mDismissDelay);
-        }
-    };
-
-    /** For now, simply host an unmodified base volume panel in this process. */
-    private final class VolumeController extends IVolumeController.Stub implements VolumeComponent {
-
-        @Override
-        public void displaySafeVolumeWarning(int flags) throws RemoteException {
-            mPanel.postDisplaySafeVolumeWarning(flags);
-        }
-
-        @Override
-        public void volumeChanged(int streamType, int flags)
-                throws RemoteException {
-            mPanel.postVolumeChanged(streamType, flags);
-        }
-
-        @Override
-        public void masterMuteChanged(int flags) throws RemoteException {
-            // no-op
-        }
-
-        @Override
-        public void setLayoutDirection(int layoutDirection)
-                throws RemoteException {
-            mPanel.postLayoutDirection(layoutDirection);
-        }
-
-        @Override
-        public void dismiss() throws RemoteException {
-            dismissNow();
-        }
-
-        @Override
-        public ZenModeController getZenController() {
-            return mPanel.getZenController();
-        }
-
-        @Override
-        public void dispatchDemoCommand(String command, Bundle args) {
-            mPanel.dispatchDemoCommand(command, args);
-        }
-
-        @Override
-        public void dismissNow() {
-            mPanel.postDismiss(0);
-        }
-    }
-
-    private final class RemoteVolumeController extends IRemoteVolumeController.Stub {
-
-        @Override
-        public void remoteVolumeChanged(ISessionController binder, int flags)
-                throws RemoteException {
-            MediaController controller = new MediaController(mContext, binder);
-            mPanel.postRemoteVolumeChanged(controller, flags);
-        }
-
-        @Override
-        public void updateRemoteController(ISessionController session) throws RemoteException {
-            mPanel.postRemoteSliderVisibility(session != null);
-            // TODO stash default session in case the slider can be opened other
-            // than by remoteVolumeChanged.
-        }
-    }
-
     private final class ServiceMonitorCallbacks implements ServiceMonitor.Callbacks {
         @Override
         public void onNoService() {
             if (LOGD) Log.d(TAG, "onNoService");
-            setVolumeController(true);
+            setDefaultVolumeController(true);
             mRestorationNotification.hide();
             if (!mVolumeControllerService.isPackageAvailable()) {
                 mVolumeControllerService.setComponent(null);
@@ -267,8 +160,8 @@
             if (LOGD) Log.d(TAG, "onServiceStartAttempt");
             // poke the setting to update the uid
             mVolumeControllerService.setComponent(mVolumeControllerService.getComponent());
-            setVolumeController(false);
-            mVolumeController.dismissNow();
+            setDefaultVolumeController(false);
+            getVolumeComponent().dismissNow();
             mRestorationNotification.show();
             return 0;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
new file mode 100644
index 0000000..ba5b8d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenFooter.java
@@ -0,0 +1,216 @@
+/*
+ * 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.volume;
+
+import android.animation.LayoutTransition;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.Settings.Global;
+import android.service.notification.Condition;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.LinearLayout;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+/**
+ * Switch bar + zen mode panel (conditions) attached to the bottom of the volume dialog.
+ */
+public class ZenFooter extends LinearLayout {
+    private static final String TAG = Util.logTag(ZenFooter.class);
+
+    private final Context mContext;
+    private final float mSecondaryAlpha;
+    private final LayoutTransition mLayoutTransition;
+
+    private ZenModeController mController;
+    private Switch mSwitch;
+    private ZenModePanel mZenModePanel;
+    private View mZenModePanelButtons;
+    private View mZenModePanelMoreButton;
+    private View mZenModePanelDoneButton;
+    private View mSwitchBar;
+    private View mSwitchBarIcon;
+    private View mSummary;
+    private TextView mSummaryLine1;
+    private TextView mSummaryLine2;
+    private boolean mFooterExpanded;
+    private int mZen = -1;
+    private Callback mCallback;
+
+    public ZenFooter(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+        mSecondaryAlpha = getFloat(context.getResources(), R.dimen.volume_secondary_alpha);
+        mLayoutTransition = new LayoutTransition();
+        mLayoutTransition.setDuration(new ValueAnimator().getDuration() / 2);
+        mLayoutTransition.disableTransitionType(LayoutTransition.DISAPPEARING);
+        mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
+    }
+
+    private static float getFloat(Resources r, int resId) {
+        final TypedValue tv = new TypedValue();
+        r.getValue(resId, tv, true);
+        return tv.getFloat();
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mSwitchBar = findViewById(R.id.volume_zen_switch_bar);
+        mSwitchBarIcon = findViewById(R.id.volume_zen_switch_bar_icon);
+        mSwitch = (Switch) findViewById(R.id.volume_zen_switch);
+        mZenModePanel = (ZenModePanel) findViewById(R.id.zen_mode_panel);
+        mZenModePanelButtons = findViewById(R.id.volume_zen_mode_panel_buttons);
+        mZenModePanelMoreButton = findViewById(R.id.volume_zen_mode_panel_more);
+        mZenModePanelDoneButton = findViewById(R.id.volume_zen_mode_panel_done);
+        mSummary = findViewById(R.id.volume_zen_panel_summary);
+        mSummaryLine1 = (TextView) findViewById(R.id.volume_zen_panel_summary_line_1);
+        mSummaryLine2 = (TextView) findViewById(R.id.volume_zen_panel_summary_line_2);
+    }
+
+    public void init(ZenModeController controller, Callback callback) {
+        mCallback = callback;
+        mController = controller;
+        mZenModePanel.init(controller);
+        mZenModePanel.setEmbedded(true);
+        mSwitch.setOnCheckedChangeListener(mCheckedListener);
+        mController.addCallback(new ZenModeController.Callback() {
+            @Override
+            public void onZenChanged(int zen) {
+                setZen(zen);
+            }
+            @Override
+            public void onExitConditionChanged(Condition exitCondition) {
+                update();
+            }
+        });
+        mSwitchBar.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mSwitch.setChecked(!mSwitch.isChecked());
+            }
+        });
+        mZenModePanelMoreButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mCallback != null) {
+                    mCallback.onSettingsClicked();
+                }
+            }
+        });
+        mZenModePanelDoneButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (mCallback != null) {
+                    mCallback.onDoneClicked();
+                }
+            }
+        });
+        mZen = mController.getZen();
+        update();
+    }
+
+    private void setZen(int zen) {
+        if (mZen == zen) return;
+        mZen = zen;
+        update();
+    }
+
+    public boolean isZen() {
+        return isZenPriority() || isZenNone();
+    }
+
+    private boolean isZenPriority() {
+        return mZen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+    }
+
+    private boolean isZenNone() {
+        return mZen == Global.ZEN_MODE_NO_INTERRUPTIONS;
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        setLayoutTransition(null);
+        setFooterExpanded(false);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        setLayoutTransition(mLayoutTransition);
+    }
+
+    private boolean setFooterExpanded(boolean expanded) {
+        if (mFooterExpanded == expanded) return false;
+        mFooterExpanded = expanded;
+        update();
+        if (mCallback != null) {
+            mCallback.onFooterExpanded();
+        }
+        return true;
+    }
+
+    public boolean isFooterExpanded() {
+        return mFooterExpanded;
+    }
+
+    public void update() {
+        final boolean isZen = isZen();
+        mSwitch.setOnCheckedChangeListener(null);
+        mSwitch.setChecked(isZen);
+        mSwitch.setOnCheckedChangeListener(mCheckedListener);
+        Util.setVisOrGone(mZenModePanel, isZen && mFooterExpanded);
+        Util.setVisOrGone(mZenModePanelButtons, isZen && mFooterExpanded);
+        Util.setVisOrGone(mSummary, isZen && !mFooterExpanded);
+        mSwitchBarIcon.setAlpha(isZen ? 1 : mSecondaryAlpha);
+        final String line1 =
+                isZenPriority() ? mContext.getString(R.string.interruption_level_priority)
+                : isZenNone() ? mContext.getString(R.string.interruption_level_none)
+                : null;
+        Util.setText(mSummaryLine1, line1);
+        Util.setText(mSummaryLine2, mZenModePanel.getExitConditionText());
+    }
+
+    private final OnCheckedChangeListener mCheckedListener = new OnCheckedChangeListener() {
+        @Override
+        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+            if (D.BUG) Log.d(TAG, "onCheckedChanged " + isChecked);
+            if (isChecked != isZen()) {
+                final int newZen = isChecked ? Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+                        : Global.ZEN_MODE_OFF;
+                mZen = newZen;  // this one's optimistic
+                setFooterExpanded(isChecked);
+                mController.setZen(newZen);
+            }
+        }
+    };
+
+    public interface Callback {
+        void onFooterExpanded();
+        void onSettingsClicked();
+        void onDoneClicked();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 878ab712..a7f6175 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -23,7 +23,6 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.content.res.Resources;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Handler;
@@ -150,6 +149,7 @@
         if (mEmbedded == embedded) return;
         mEmbedded = embedded;
         mZenButtonsContainer.setLayoutTransition(mEmbedded ? null : newLayoutTransition(null));
+        setLayoutTransition(mEmbedded ? null : newLayoutTransition(null));
         if (mEmbedded) {
             mZenButtonsContainer.setBackground(null);
         } else {
@@ -166,12 +166,10 @@
         super.onFinishInflate();
 
         mZenButtons = (SegmentedButtons) findViewById(R.id.zen_buttons);
-        mZenButtons.addButton(R.string.interruption_level_none, R.drawable.ic_zen_none,
-                Global.ZEN_MODE_NO_INTERRUPTIONS);
-        mZenButtons.addButton(R.string.interruption_level_priority, R.drawable.ic_zen_important,
+        mZenButtons.addButton(R.string.interruption_level_none, Global.ZEN_MODE_NO_INTERRUPTIONS);
+        mZenButtons.addButton(R.string.interruption_level_priority,
                 Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
-        mZenButtons.addButton(R.string.interruption_level_all, R.drawable.ic_zen_all,
-                Global.ZEN_MODE_OFF);
+        mZenButtons.addButton(R.string.interruption_level_all, Global.ZEN_MODE_OFF);
         mZenButtons.setCallback(mZenButtonsCallback);
 
         mZenButtonsContainer = (ViewGroup) findViewById(R.id.zen_buttons_container);
@@ -275,6 +273,7 @@
 
     private void setExpanded(boolean expanded) {
         if (expanded == mExpanded) return;
+        if (DEBUG) Log.d(mTag, "setExpanded " + expanded);
         mExpanded = expanded;
         if (mExpanded) {
             ensureSelection();
@@ -358,6 +357,10 @@
         return condition == null ? null : condition.copy();
     }
 
+    public String getExitConditionText() {
+        return mExitConditionText;
+    }
+
     private void refreshExitConditionText() {
         if (mExitCondition == null) {
             mExitConditionText = foreverSummary();
@@ -428,7 +431,7 @@
         mZenSubheadExpanded.setVisibility(expanded ? VISIBLE : GONE);
         mZenSubheadCollapsed.setVisibility(!expanded ? VISIBLE : GONE);
         mMoreSettings.setVisibility(zenImportant && expanded ? VISIBLE : GONE);
-        mZenConditions.setVisibility(!zenOff && expanded ? VISIBLE : GONE);
+        mZenConditions.setVisibility(mEmbedded || !zenOff && expanded ? VISIBLE : GONE);
 
         if (zenNone) {
             mZenSubheadExpanded.setText(R.string.zen_no_interruptions_with_warning);
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index 523c8fb..2b2e611 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -273,8 +273,8 @@
     }
 
     /**
-     * @hide
      * Enable/Disable AutoPadding for Vec3 elements.
+     * By default: Diabled.
      *
      * @param useAutoPadding True: enable AutoPadding; flase: disable AutoPadding
      *
@@ -802,7 +802,7 @@
 
     /**
      * This is only intended to be used by auto-generated code reflected from
-     * the RenderScript script files.
+     * the RenderScript script files and should not be used by developers.
      *
      * @param xoff
      * @param component_number
@@ -813,9 +813,8 @@
     }
 
     /**
-     * @hide
      * This is only intended to be used by auto-generated code reflected from
-     * the RenderScript script files.
+     * the RenderScript script files and should not be used by developers.
      *
      * @param xoff
      * @param yoff
@@ -1247,8 +1246,11 @@
     }
 
     /**
-     * @hide
+     * Copy a rectangular region from the array into the allocation.
+     * The array is assumed to be tightly packed.
      *
+     * The data type of the array is not required to be the same as
+     * the element data type.
      */
     private void copy3DRangeFromUnchecked(int xoff, int yoff, int zoff, int w, int h, int d,
                                           Object array, Element.DataType dt, int arrayLen) {
@@ -1277,7 +1279,6 @@
     }
 
     /**
-     * @hide
      * Copy a rectangular region from the array into the allocation.
      * The array is assumed to be tightly packed.
      *
@@ -1298,7 +1299,6 @@
     }
 
     /**
-     * @hide
      * Copy a rectangular region into the allocation from another
      * allocation.
      *
@@ -1415,7 +1415,6 @@
     }
 
     /**
-     * @hide
      * This is only intended to be used by auto-generated code reflected from
      * the RenderScript script files and should not be used by developers.
      *
@@ -1423,7 +1422,7 @@
      * @param yoff
      * @param zoff
      * @param component_number
-     * @param array
+     * @param fp
      */
     public void copyToFieldPacker(int xoff, int yoff, int zoff, int component_number, FieldPacker fp) {
         mRS.validate();
@@ -1501,7 +1500,6 @@
     }
 
     /**
-     * @hide
      * Copy part of this Allocation into an array.  This method does not
      * guarantee that the Allocation is compatible with the input buffer.
      *
@@ -1516,7 +1514,6 @@
     }
 
     /**
-     * @hide
      * Copy part of this Allocation into an array.  This method does not
      * guarantee that the Allocation is compatible with the input buffer.
      *
@@ -1529,7 +1526,6 @@
     }
 
     /**
-     * @hide
      * Copy part of this Allocation into an array.  This method does not
      * guarantee that the Allocation is compatible with the input buffer.
      *
@@ -1542,7 +1538,6 @@
     }
 
     /**
-     * @hide
      * Copy part of this Allocation into an array.  This method does not
      * guarantee that the Allocation is compatible with the input buffer.
      *
@@ -1555,7 +1550,6 @@
     }
 
     /**
-     * @hide
      * Copy part of this Allocation into an array.  This method does not
      * guarantee that the Allocation is compatible with the input buffer.
      *
@@ -1569,7 +1563,6 @@
 
 
     /**
-     * @hide
      * Copy part of this Allocation into an array.  This method does not
      * and will generate exceptions if the Allocation type does not
      * match the component type of the array passed in.
@@ -1585,7 +1578,6 @@
     }
 
     /**
-     * @hide
      * Copy part of this Allocation into an array.  This method does not
      * and will generate exceptions if the Allocation type is not a 32 bit
      * integer type.
@@ -1600,7 +1592,6 @@
     }
 
     /**
-     * @hide
      * Copy part of this Allocation into an array.  This method does not
      * and will generate exceptions if the Allocation type is not a 16 bit
      * integer type.
@@ -1615,7 +1606,6 @@
     }
 
     /**
-     * @hide
      * Copy part of this Allocation into an array.  This method does not
      * and will generate exceptions if the Allocation type is not an 8 bit
      * integer type.
@@ -1630,7 +1620,6 @@
     }
 
     /**
-     * @hide
      * Copy part of this Allocation into an array.  This method does not
      * and will generate exceptions if the Allocation type is not a 32 bit float
      * type.
@@ -1671,7 +1660,6 @@
     }
 
     /**
-     * @hide
      * Copy from a rectangular region in this Allocation into an array.
      *
      * @param xoff X offset of the region to copy in this Allocation
@@ -1687,7 +1675,6 @@
     }
 
     /**
-     * @hide
      * Copy from a rectangular region in this Allocation into an array.
      *
      * @param xoff X offset of the region to copy in this Allocation
@@ -1703,7 +1690,6 @@
     }
 
     /**
-     * @hide
      * Copy from a rectangular region in this Allocation into an array.
      *
      * @param xoff X offset of the region to copy in this Allocation
@@ -1719,7 +1705,6 @@
     }
 
     /**
-     * @hide
      * Copy from a rectangular region in this Allocation into an array.
      *
      * @param xoff X offset of the region to copy in this Allocation
@@ -1735,7 +1720,6 @@
     }
 
     /**
-     * @hide
      * Copy from a rectangular region in this Allocation into an array.
      *
      * @param xoff X offset of the region to copy in this Allocation
@@ -1752,8 +1736,11 @@
 
 
     /**
-     * @hide
+     * Copy from a rectangular region in this Allocation into an array.
+     * The array is assumed to be tightly packed.
      *
+     * The data type of the array is not required to be the same as
+     * the element data type.
      */
     private void copy3DRangeToUnchecked(int xoff, int yoff, int zoff, int w, int h, int d,
                                         Object array, Element.DataType dt, int arrayLen) {
@@ -1780,8 +1767,7 @@
         Trace.traceEnd(RenderScript.TRACE_TAG);
     }
 
-    /**
-     * @hide
+    /*
      * Copy from a rectangular region in this Allocation into an array.
      *
      * @param xoff X offset of the region to copy in this Allocation
diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java
index 287b3f1..4b3e30f 100644
--- a/rs/java/android/renderscript/Element.java
+++ b/rs/java/android/renderscript/Element.java
@@ -114,13 +114,11 @@
      * MATRIX the three matrix types contain FLOAT_32 elements and are treated
      * as 32 bits for alignment purposes.
      *
-     * RS_* objects.  32 bit opaque handles.
+     * RS_* objects:  opaque handles with implementation dependent
+     * sizes.
      */
     public enum DataType {
         NONE (0, 0),
-        /**
-         *     @hide
-         */
         FLOAT_16 (1, 2),
         FLOAT_32 (2, 4),
         FLOAT_64 (3, 8),
@@ -389,9 +387,6 @@
         return rs.mElement_I64;
     }
 
-    /**
-     *     @hide
-     */
     public static Element F16(RenderScript rs) {
         if(rs.mElement_F16 == null) {
             rs.mElement_F16 = createUser(rs, DataType.FLOAT_16);
@@ -533,9 +528,6 @@
         return rs.mElement_RGBA_8888;
     }
 
-    /**
-     *     @hide
-     */
     public static Element F16_2(RenderScript rs) {
         if(rs.mElement_HALF_2 == null) {
             rs.mElement_HALF_2 = createVector(rs, DataType.FLOAT_16, 2);
@@ -543,9 +535,6 @@
         return rs.mElement_HALF_2;
     }
 
-    /**
-     *     @hide
-     */
     public static Element F16_3(RenderScript rs) {
         if(rs.mElement_FLOAT_3 == null) {
             rs.mElement_FLOAT_3 = createVector(rs, DataType.FLOAT_16, 3);
@@ -553,9 +542,6 @@
         return rs.mElement_HALF_3;
     }
 
-    /**
-     *     @hide
-     */
     public static Element F16_4(RenderScript rs) {
         if(rs.mElement_HALF_4 == null) {
             rs.mElement_HALF_4 = createVector(rs, DataType.FLOAT_16, 4);
diff --git a/rs/java/android/renderscript/FileA3D.java b/rs/java/android/renderscript/FileA3D.java
index 4164810..9d8f162 100644
--- a/rs/java/android/renderscript/FileA3D.java
+++ b/rs/java/android/renderscript/FileA3D.java
@@ -145,6 +145,9 @@
             case MESH:
                 entry.mLoadedObj = new Mesh(objectID, rs);
                 break;
+
+            default:
+                throw new RSRuntimeException("Unrecognized object type in file.");
             }
 
             entry.mLoadedObj.updateFromNative();
diff --git a/rs/java/android/renderscript/Mesh.java b/rs/java/android/renderscript/Mesh.java
index 5b4cadb..13c8e1c 100644
--- a/rs/java/android/renderscript/Mesh.java
+++ b/rs/java/android/renderscript/Mesh.java
@@ -363,6 +363,9 @@
                     alloc = Allocation.createTyped(mRS, entry.t, mUsage);
                 } else if(entry.e != null) {
                     alloc = Allocation.createSized(mRS, entry.e, entry.size, mUsage);
+                } else {
+                    // Should never happen because the builder will always set one
+                    throw new IllegalStateException("Builder corrupt, no valid element in entry.");
                 }
                 vertexBuffers[ct] = alloc;
                 vtx[ct] = alloc.getID(mRS);
@@ -375,6 +378,9 @@
                     alloc = Allocation.createTyped(mRS, entry.t, mUsage);
                 } else if(entry.e != null) {
                     alloc = Allocation.createSized(mRS, entry.e, entry.size, mUsage);
+                } else {
+                    // Should never happen because the builder will always set one
+                    throw new IllegalStateException("Builder corrupt, no valid element in entry.");
                 }
                 long allocID = (alloc == null) ? 0 : alloc.getID(mRS);
                 indexBuffers[ct] = alloc;
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index 8562288..7ef17a7 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -139,7 +139,8 @@
      * Returns an identifier that can be used to identify a particular
      * minor version of RS.
      *
-     * @hide
+     * @return The minor RenderScript version number
+     *
      */
     public static long getMinorID() {
         return sMinorID;
@@ -1321,7 +1322,6 @@
     /**
      * Create a RenderScript context.
      *
-     * @hide
      * @param ctx The context.
      * @return RenderScript
      */
@@ -1351,7 +1351,7 @@
     }
 
     /**
-     * calls create(cts, ContextType.NORMAL, CREATE_FLAG_NONE)
+     * calls create(ctx, ContextType.NORMAL, CREATE_FLAG_NONE)
      *
      * See documentation for @create for details
      *
@@ -1363,7 +1363,7 @@
     }
 
     /**
-     * calls create(cts, ct, CREATE_FLAG_NONE)
+     * calls create(ctx, ct, CREATE_FLAG_NONE)
      *
      * See documentation for @create for details
      *
@@ -1375,7 +1375,8 @@
         return create(ctx, ct, CREATE_FLAG_NONE);
     }
 
-     /**
+
+    /**
      * Gets or creates a RenderScript context of the specified type.
      *
      * The returned context will be cached for future reuse within
@@ -1397,21 +1398,48 @@
      */
     public static RenderScript create(Context ctx, ContextType ct, int flags) {
         int v = ctx.getApplicationInfo().targetSdkVersion;
-        if (v < 23) {
-            return internalCreate(ctx, v, ct, flags);
+        return create(ctx, v, ct, flags);
+    }
+
+    /**
+     * calls create(ctx, sdkVersion, ContextType.NORMAL, CREATE_FLAG_NONE)
+     *
+     * Used by the RenderScriptThunker to maintain backward compatibility.
+     *
+     * @hide
+     * @param ctx The context.
+     * @param sdkVersion The target SDK Version.
+     * @return RenderScript
+     */
+    public static RenderScript create(Context ctx, int sdkVersion) {
+        return create(ctx, sdkVersion, ContextType.NORMAL, CREATE_FLAG_NONE);
+    }
+
+     /**
+     * Gets or creates a RenderScript context of the specified type.
+     *
+     * @param ctx The context.
+     * @param ct The type of context to be created.
+     * @param sdkVersion The target SDK Version.
+     * @param flags The OR of the CREATE_FLAG_* options desired
+     * @return RenderScript
+     */
+    private static RenderScript create(Context ctx, int sdkVersion, ContextType ct, int flags) {
+        if (sdkVersion < 23) {
+            return internalCreate(ctx, sdkVersion, ct, flags);
         }
 
         synchronized (mProcessContextList) {
             for (RenderScript prs : mProcessContextList) {
                 if ((prs.mContextType == ct) &&
                     (prs.mContextFlags == flags) &&
-                    (prs.mContextSdkVersion == v)) {
+                    (prs.mContextSdkVersion == sdkVersion)) {
 
                     return prs;
                 }
             }
 
-            RenderScript prs = internalCreate(ctx, v, ct, flags);
+            RenderScript prs = internalCreate(ctx, sdkVersion, ct, flags);
             prs.mIsProcessContext = true;
             mProcessContextList.add(prs);
             return prs;
@@ -1419,8 +1447,6 @@
     }
 
     /**
-     * @hide
-     *
      * Releases all the process contexts.  This is the same as
      * calling .destroy() on each unique context retreived with
      * create(...). If no contexts have been created this
@@ -1457,7 +1483,6 @@
      *
      * If you need a single context please use create()
      *
-     * @hide
      * @param ctx The context.
      * @return RenderScript
      */
diff --git a/rs/java/android/renderscript/Script.java b/rs/java/android/renderscript/Script.java
index 65056ac..6a1efee 100644
--- a/rs/java/android/renderscript/Script.java
+++ b/rs/java/android/renderscript/Script.java
@@ -66,7 +66,6 @@
     }
 
     /**
-     * @hide Pending API review
      * InvokeID is an identifier for an invoke function. It is used
      * as an identifier for ScriptGroup creation.
      *
@@ -86,7 +85,6 @@
 
     private final SparseArray<InvokeID> mIIDs = new SparseArray<InvokeID>();
     /**
-     * @hide Pending API review
      * Only to be used by generated reflected classes.
      */
     protected InvokeID createInvokeID(int slot) {
@@ -222,22 +220,21 @@
 
     /**
      * Only intended for use by generated reflected code.
-     *
-     * @hide
      */
     protected void forEach(int slot, Allocation[] ains, Allocation aout,
                            FieldPacker v) {
+
+        // FieldPacker is kept here to support regular params in the future.
         forEach(slot, ains, aout, v, null);
     }
 
     /**
      * Only intended for use by generated reflected code.
-     *
-     * @hide
      */
     protected void forEach(int slot, Allocation[] ains, Allocation aout,
                            FieldPacker v, LaunchOptions sc) {
         // TODO: Is this necessary if nScriptForEach calls validate as well?
+        // FieldPacker is kept here to support regular params in the future.
         mRS.validate();
         if (ains != null) {
             for (Allocation ain : ains) {
@@ -476,7 +473,23 @@
 
 
     /**
-     * Class used to specify clipping for a kernel launch.
+     * Class for specifying the specifics about how a kernel will be
+     * launched
+     *
+     * This class can specify a potential range of cells on which to
+     * run a kernel.  If no set is called for a dimension then this
+     * class will have no impact on that dimension when the kernel
+     * is executed.
+     *
+     * The forEach launch will operate over the intersection of the
+     * dimensions.
+     *
+     * Example:
+     * LaunchOptions with setX(5, 15)
+     * Allocation with dimension X=10, Y=10
+     * The resulting forEach run would execute over x = 5 to 10 and
+     * y = 0 to 10.
+     *
      *
      */
     public static final class LaunchOptions {
diff --git a/rs/java/android/renderscript/ScriptGroup2.java b/rs/java/android/renderscript/ScriptGroup2.java
index 4a56572..9d73ac4 100644
--- a/rs/java/android/renderscript/ScriptGroup2.java
+++ b/rs/java/android/renderscript/ScriptGroup2.java
@@ -24,26 +24,41 @@
 import java.util.Map;
 
 /**
+ * ScriptGroup2 is a new, enhanced API for script groups.
+ * A script group is a collection of kernels or invocable functions, with
+ * data dependencies defined among them. A script group is launched for
+ * execution as a whole, rather than launching each kernel or invocable function
+ * separately. Once created, a script group can be repeatedly used with
+ * different inputs.
+ * <p>
+ * In the new ScriptGroup2 API, a script group is modeled using closures.
+ * A closure, in this context, is defined as a function call to a kernel or
+ * invocable function. Each function argument or global variable accessed inside
+ * the function is bound to 1) a known value, 2) a script group input, or 3) a
+ * future. A future is the output of a closure, i.e., the return value of the
+ * function or a global variable written by that function.
+ * <p>
+ * A script group is a directed acyclic graph (DAG), in which closures are the
+ * vertices and the dependencies among them are the edges.
+ * The way the ScriptGroup2 API is designed makes cycles impossible in a script
+ * group. For example, it is impossible to make forward references to futures,
+ * i.e., it is impossible to set as input to a closure the future from itself or
+ * a future from another closure that directly or indirectly depends on it.
+ * <p>
+ * Grouping kernels and invocable functions together allows to execute them more
+ * efficiently. Runtime and compiler optimizations are applied to script
+ * groups, to reduce computation or communication overhead, and to make more
+ * efficient use of the CPU and the GPU.
+ */
 
-******************************
-You have tried to change the API from what has been previously approved.
-
-To make these errors go away, you have two choices:
-1) You can add "@hide" javadoc comments to the methods, etc. listed in the
-errors above.
-
-2) You can update current.txt by executing the following command:
-make update-api
-
-To submit the revised current.txt to the main Android repository,
-you will need approval.
-******************************
-
-@hide Pending Android public API approval.
-*/
 public class ScriptGroup2 extends BaseObj {
 
+    /**
+     * An opaque class for closures
+     */
+
     public static class Closure extends BaseObj {
+        private Object[] mArgs;
         private Allocation mReturnValue;
         private Map<Script.FieldID, Object> mBindings;
 
@@ -62,8 +77,9 @@
                        Object[] args, Map<Script.FieldID, Object> globals) {
             super(0, rs);
 
+            mArgs = args;
             mReturnValue = Allocation.createTyped(rs, returnType);
-            mBindings = new HashMap<Script.FieldID, Object>();
+            mBindings = globals;
             mGlobalFuture = new HashMap<Script.FieldID, Future>();
 
             int numValues = args.length + globals.size();
@@ -112,7 +128,8 @@
             super(0, rs);
             mFP = FieldPacker.createFieldPack(args);
 
-            mBindings = new HashMap<Script.FieldID, Object>();
+            mArgs = args;
+            mBindings = globals;
             mGlobalFuture = new HashMap<Script.FieldID, Future>();
 
             int numValues = globals.size();
@@ -132,7 +149,6 @@
                     UnboundValue unbound = (UnboundValue)obj;
                     unbound.addReference(this, fieldID);
                 } else {
-                    // TODO(yangni): Verify obj not a future.
                     retrieveValueAndDependenceInfo(rs, i, obj, values,
                                                    sizes, depClosures, depFieldIDs);
                 }
@@ -174,6 +190,12 @@
             sizes[index] = vs.size;
         }
 
+        /**
+         * Returns the future for the return value
+         *
+         * @return a future
+         */
+
         public Future getReturn() {
             if (mReturnFuture == null) {
                 mReturnFuture = new Future(this, null, mReturnValue);
@@ -182,6 +204,13 @@
             return mReturnFuture;
         }
 
+        /**
+         * Returns the future for a global variable
+         *
+         * @param field the field ID for the global variable
+         * @return a future
+         */
+
         public Future getGlobal(Script.FieldID field) {
             Future f = mGlobalFuture.get(field);
 
@@ -198,11 +227,13 @@
         }
 
         void setArg(int index, Object obj) {
+            mArgs[index] = obj;
             ValueAndSize vs = new ValueAndSize(mRS, obj);
             mRS.nClosureSetArg(getID(mRS), index, vs.value, vs.size);
         }
 
         void setGlobal(Script.FieldID fieldID, Object obj) {
+            mBindings.put(fieldID, obj);
             ValueAndSize vs = new ValueAndSize(mRS, obj);
             mRS.nClosureSetGlobal(getID(mRS), fieldID.getID(mRS), vs.value, vs.size);
         }
@@ -234,6 +265,10 @@
         }
     }
 
+    /**
+     * An opaque class for futures
+     */
+
     public static class Future {
         Closure mClosure;
         Script.FieldID mFieldID;
@@ -250,6 +285,10 @@
         Object getValue() { return mValue; }
     }
 
+    /**
+     * An opaque class for unbound values (a.k.a. script group inputs)
+     */
+
     public static class UnboundValue {
         // Either mFieldID or mArgIndex should be set but not both.
         List<Pair<Closure, Script.FieldID>> mFieldID;
@@ -309,6 +348,13 @@
         setID(id);
     }
 
+    /**
+     * Executes a script group
+     *
+     * @param inputs inputs to the script group
+     * @return outputs of the script group as an array of objects
+     */
+
     public Object[] execute(Object... inputs) {
         if (inputs.length < mInputs.size()) {
             Log.e(TAG, this.toString() + " receives " + inputs.length + " inputs, " +
@@ -343,32 +389,95 @@
     }
 
     /**
-       @hide Pending Android public API approval.
-    */
+     * A class representing a binding of a value to a global variable in a
+     * kernel or invocable function. Such a binding can be used to create a
+     * closure.
+     */
+
     public static final class Binding {
-        public Script.FieldID mField;
-        public Object mValue;
+        private Script.FieldID mField;
+        private Object mValue;
+
+        /**
+         * Returns a Binding object that binds value to field
+         *
+         * @param field the Script.FieldID of the global variable
+         * @param value the value
+         */
+
         public Binding(Script.FieldID field, Object value) {
             mField = field;
             mValue = value;
         }
+
+        /**
+         * Returns the field ID
+         */
+
+        public Script.FieldID getField() { return mField; }
+
+        /**
+         * Returns the value
+         */
+
+        public Object getValue() { return mValue; }
     }
 
     /**
-       @hide Pending Android public API approval.
-    */
+     * The builder class to create a script group.
+     * <p>
+     * Closures are created using the {@link #addKernel} or {@link #addInvoke}
+     * methods.
+     * When a closure is created, futures from previously created closures
+     * can be used as inputs.
+     * Unbound values can be used as inputs to create closures as well.
+     * An unbound value is created using the {@link #addInput} method.
+     * Unbound values become inputs to the script group to be created,
+     * in the order that they are added.
+     * A script group is created by a call to the {@link #create} method, which
+     * accepts an array of futures as the outputs for the script group.
+     * <p>
+     * Closures in a script group can be evaluated in any order as long as the
+     * following conditions are met.
+     * First, a closure must be evaluated before any other closures that take its
+     * futures as inputs.
+     * Second, all closures added before an invoke closure must be evaluated
+     * before it.
+     * Third, all closures added after an invoke closure must be evaluated after
+     * it.
+     * <p>
+     * As a special case, the order that the closures are added is a legal
+     * evaluation order. However, other evaluation orders are allowed, including
+     * concurrently evaluating independent closures.
+     */
+
     public static final class Builder {
         RenderScript mRS;
         List<Closure> mClosures;
         List<UnboundValue> mInputs;
         private static final String TAG = "ScriptGroup2.Builder";
 
+        /**
+         * Returns a Builder object
+         *
+         * @param rs the RenderScript context
+         */
         public Builder(RenderScript rs) {
             mRS = rs;
             mClosures = new ArrayList<Closure>();
             mInputs = new ArrayList<UnboundValue>();
         }
 
+        /**
+         * Adds a closure for a kernel
+         *
+         * @param k Kernel ID for the kernel function
+         * @param returnType Allocation type for the return value
+         * @param args arguments to the kernel function
+         * @param globalBindings bindings for global variables
+         * @return a closure
+         */
+
         public Closure addKernel(Script.KernelID k, Type returnType, Object[] args,
                                  Map<Script.FieldID, Object> globalBindings) {
             Closure c = new Closure(mRS, k, returnType, args, globalBindings);
@@ -376,6 +485,15 @@
             return c;
         }
 
+        /**
+         * Adds a closure for an invocable function
+         *
+         * @param invoke Invoke ID for the invocable function
+         * @param args arguments to the invocable function
+         * @param globalBindings bindings for global variables
+         * @return a closure
+         */
+
         public Closure addInvoke(Script.InvokeID invoke, Object[] args,
                                  Map<Script.FieldID, Object> globalBindings) {
             Closure c = new Closure(mRS, invoke, args, globalBindings);
@@ -383,12 +501,25 @@
             return c;
         }
 
+        /**
+         * Adds a script group input
+         *
+         * @return a unbound value that can be used to create a closure
+         */
         public UnboundValue addInput() {
             UnboundValue unbound = new UnboundValue();
             mInputs.add(unbound);
             return unbound;
         }
 
+        /**
+         * Adds a closure for a kernel
+         *
+         * @param k Kernel ID for the kernel function
+         * @param argsAndBindings arguments followed by bindings for global variables
+         * @return a closure
+         */
+
         public Closure addKernel(Script.KernelID k, Type returnType, Object... argsAndBindings) {
             ArrayList<Object> args = new ArrayList<Object>();
             Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>();
@@ -398,6 +529,14 @@
             return addKernel(k, returnType, args.toArray(), bindingMap);
         }
 
+        /**
+         * Adds a closure for an invocable function
+         *
+         * @param invoke Invoke ID for the invocable function
+         * @param argsAndBindings arguments followed by bindings for global variables
+         * @return a closure
+         */
+
         public Closure addInvoke(Script.InvokeID invoke, Object... argsAndBindings) {
             ArrayList<Object> args = new ArrayList<Object>();
             Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>();
@@ -407,6 +546,13 @@
             return addInvoke(invoke, args.toArray(), bindingMap);
         }
 
+        /**
+         * Creates a script group
+         *
+         * @param outputs futures intended as outputs of the script group
+         * @return a script group
+         */
+
         public ScriptGroup2 create(Future... outputs) {
             ScriptGroup2 ret = new ScriptGroup2(mRS, mClosures, mInputs, outputs);
             return ret;
@@ -428,7 +574,7 @@
                     return false;
                 }
                 Binding b = (Binding)argsAndBindings[i];
-                bindingMap.put(b.mField, b.mValue);
+                bindingMap.put(b.getField(), b.getValue());
             }
 
             return true;
diff --git a/rs/java/android/renderscript/Type.java b/rs/java/android/renderscript/Type.java
index a58e42c..cc9b58b 100644
--- a/rs/java/android/renderscript/Type.java
+++ b/rs/java/android/renderscript/Type.java
@@ -150,24 +150,29 @@
     }
 
     /**
-     * @hide
-     */
-    public int getArray(int dim) {
-        if ((dim < 0) || (dim >= mMaxArrays)) {
+      * Return the dimension of the specified array.
+      *
+      * @param arrayNum  The array dimension to query
+      * @return int
+      */
+    public int getArray(int arrayNum) {
+        if ((arrayNum < 0) || (arrayNum >= mMaxArrays)) {
             throw new RSIllegalArgumentException("Array dimension out of range.");
         }
 
-        if (mArrays == null || dim >= mArrays.length) {
+        if (mArrays == null || arrayNum >= mArrays.length) {
             // Dimension in range but no array for that dimension allocated
             return 0;
         }
 
-        return mArrays[dim];
+        return mArrays[arrayNum];
     }
 
     /**
-     * @hide
-     */
+      * Return the number of array dimensions.
+      *
+      * @return int
+      */
     public int getArrayCount() {
         if (mArrays != null) return mArrays.length;
         return 0;
@@ -377,7 +382,7 @@
         }
 
         /**
-         * @hide
+         * Adds an array dimension to the builder
          *
          * @param dim
          * @param value
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 886845c..40fad38 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -367,7 +367,7 @@
   return (jlong)(uintptr_t)rsClosureCreate(
       (RsContext)con, (RsScriptKernelID)kernelID, (RsAllocation)returnValue,
       fieldIDs, (size_t)fieldIDs_length, values, (size_t)values_length,
-      (size_t*)sizes, (size_t)sizes_length,
+      (int*)sizes, (size_t)sizes_length,
       depClosures, (size_t)depClosures_length,
       depFieldIDs, (size_t)depFieldIDs_length);
 }
@@ -400,7 +400,7 @@
   return (jlong)(uintptr_t)rsInvokeClosureCreate(
       (RsContext)con, (RsScriptInvokeID)invokeID, jParams, jParamLength,
       fieldIDs, (size_t)fieldIDs_length, values, (size_t)values_length,
-      (size_t*)sizes, (size_t)sizes_length);
+      (int*)sizes, (size_t)sizes_length);
 }
 
 static void
diff --git a/services/Android.mk b/services/Android.mk
index 8777085..1918db5 100644
--- a/services/Android.mk
+++ b/services/Android.mk
@@ -24,6 +24,7 @@
     appwidget \
     backup \
     devicepolicy \
+    midi \
     net \
     print \
     restrictions \
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 8fcdd39..5cc59e5 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -22,12 +22,14 @@
 import android.app.IActivityManager;
 import android.app.IApplicationThread;
 import android.app.IBackupAgent;
+import android.app.PackageInstallObserver;
 import android.app.PendingIntent;
 import android.app.backup.BackupAgent;
 import android.app.backup.BackupDataInput;
 import android.app.backup.BackupDataOutput;
 import android.app.backup.BackupTransport;
 import android.app.backup.FullBackup;
+import android.app.backup.FullBackupDataOutput;
 import android.app.backup.RestoreDescription;
 import android.app.backup.RestoreSet;
 import android.app.backup.IBackupManager;
@@ -45,7 +47,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageDeleteObserver;
-import android.content.pm.IPackageInstallObserver;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -134,6 +135,7 @@
 import java.util.Random;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.zip.Deflater;
@@ -200,7 +202,6 @@
 
     private static final String RUN_BACKUP_ACTION = "android.app.backup.intent.RUN";
     private static final String RUN_INITIALIZE_ACTION = "android.app.backup.intent.INIT";
-    private static final String RUN_CLEAR_ACTION = "android.app.backup.intent.CLEAR";
     private static final int MSG_RUN_BACKUP = 1;
     private static final int MSG_RUN_ADB_BACKUP = 2;
     private static final int MSG_RUN_RESTORE = 3;
@@ -370,7 +371,7 @@
                     // we're now good to go, so start the backup alarms
                     if (MORE_DEBUG) Slog.d(TAG, "Now provisioned, so starting backups");
                     KeyValueBackupJob.schedule(mContext);
-                    scheduleNextFullBackupJob();
+                    scheduleNextFullBackupJob(0);
                 }
             }
         }
@@ -727,7 +728,7 @@
             {
                 try {
                     BackupRestoreTask task = (BackupRestoreTask) msg.obj;
-                    task.operationComplete();
+                    task.operationComplete(msg.arg1);
                 } catch (ClassCastException e) {
                     Slog.e(TAG, "Invalid completion in flight, obj=" + msg.obj);
                 }
@@ -1198,11 +1199,13 @@
                 temp = new RandomAccessFile(tempProcessedFile, "rws");
                 in = new RandomAccessFile(mEverStored, "r");
 
+                // Loop until we hit EOF
                 while (true) {
-                    PackageInfo info;
                     String pkg = in.readUTF();
                     try {
-                        info = mPackageManager.getPackageInfo(pkg, 0);
+                        // is this package still present?
+                        mPackageManager.getPackageInfo(pkg, 0);
+                        // if we get here then yes it is; remember it
                         mEverStoredApps.add(pkg);
                         temp.writeUTF(pkg);
                         if (MORE_DEBUG) Slog.v(TAG, "   + " + pkg);
@@ -1783,7 +1786,7 @@
                         PackageInfo app = mPackageManager.getPackageInfo(packageName, 0);
                         if (appGetsFullBackup(app) && appIsEligibleForBackup(app.applicationInfo)) {
                             enqueueFullBackup(packageName, now);
-                            scheduleNextFullBackupJob();
+                            scheduleNextFullBackupJob(0);
                         }
 
                         // Transport maintenance: rebind to known existing transports that have
@@ -2223,7 +2226,7 @@
         void execute();
 
         // An operation that wanted a callback has completed
-        void operationComplete();
+        void operationComplete(int result);
 
         // An operation that wanted a callback has timed out
         void handleTimeout();
@@ -2791,7 +2794,7 @@
         }
 
         @Override
-        public void operationComplete() {
+        public void operationComplete(int unusedResult) {
             // The agent reported back to us!
 
             if (mBackupData == null) {
@@ -3127,8 +3130,23 @@
 
     // Core logic for performing one package's full backup, gathering the tarball from the
     // application and emitting it to the designated OutputStream.
+
+    // Callout from the engine to an interested participant that might need to communicate
+    // with the agent prior to asking it to move data
+    interface FullBackupPreflight {
+        /**
+         * Perform the preflight operation necessary for the given package.
+         * @param pkg The name of the package being proposed for full-data backup
+         * @param agent Live BackupAgent binding to the target app's agent
+         * @return BackupTransport.TRANSPORT_OK to proceed with the backup operation,
+         *         or one of the other BackupTransport.* error codes as appropriate
+         */
+        int preflightFullBackup(PackageInfo pkg, IBackupAgent agent);
+    };
+
     class FullBackupEngine {
         OutputStream mOutput;
+        FullBackupPreflight mPreflightHook;
         IFullBackupRestoreObserver mObserver;
         File mFilesDir;
         File mManifestFile;
@@ -3159,8 +3177,7 @@
             @Override
             public void run() {
                 try {
-                    BackupDataOutput output = new BackupDataOutput(
-                            mPipe.getFileDescriptor());
+                    FullBackupDataOutput output = new FullBackupDataOutput(mPipe);
 
                     if (mWriteManifest) {
                         final boolean writeWidgetData = mWidgetData != null;
@@ -3203,15 +3220,16 @@
             }
         }
 
-        FullBackupEngine(OutputStream output, String packageName, boolean alsoApks) {
+        FullBackupEngine(OutputStream output, String packageName, FullBackupPreflight preflightHook,
+                boolean alsoApks) {
             mOutput = output;
+            mPreflightHook = preflightHook;
             mIncludeApks = alsoApks;
             mFilesDir = new File("/data/system");
             mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
             mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
         }
 
-
         public int backupOnePackage(PackageInfo pkg) throws RemoteException {
             int result = BackupTransport.TRANSPORT_OK;
             Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
@@ -3221,42 +3239,52 @@
             if (agent != null) {
                 ParcelFileDescriptor[] pipes = null;
                 try {
-                    pipes = ParcelFileDescriptor.createPipe();
+                    // Call the preflight hook, if any
+                    if (mPreflightHook != null) {
+                        result = mPreflightHook.preflightFullBackup(pkg, agent);
+                        if (MORE_DEBUG) {
+                            Slog.v(TAG, "preflight returned " + result);
+                        }
+                    }
 
-                    ApplicationInfo app = pkg.applicationInfo;
-                    final boolean isSharedStorage = pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
-                    final boolean sendApk = mIncludeApks
-                            && !isSharedStorage
-                            && ((app.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) == 0)
-                            && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
+                    // If we're still good to go after preflighting, start moving data
+                    if (result == BackupTransport.TRANSPORT_OK) {
+                        pipes = ParcelFileDescriptor.createPipe();
+
+                        ApplicationInfo app = pkg.applicationInfo;
+                        final boolean isSharedStorage = pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
+                        final boolean sendApk = mIncludeApks
+                                && !isSharedStorage
+                                && ((app.privateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) == 0)
+                                && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
                                 (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
 
-                    byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(pkg.packageName,
-                            UserHandle.USER_OWNER);
+                        byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(pkg.packageName,
+                                UserHandle.USER_OWNER);
 
-                    final int token = generateToken();
-                    FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1],
-                            token, sendApk, !isSharedStorage, widgetBlob);
-                    pipes[1].close();   // the runner has dup'd it
-                    pipes[1] = null;
-                    Thread t = new Thread(runner, "app-data-runner");
-                    t.start();
+                        final int token = generateToken();
+                        FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1],
+                                token, sendApk, !isSharedStorage, widgetBlob);
+                        pipes[1].close();   // the runner has dup'd it
+                        pipes[1] = null;
+                        Thread t = new Thread(runner, "app-data-runner");
+                        t.start();
 
-                    // Now pull data from the app and stuff it into the output
-                    try {
-                        routeSocketDataToOutput(pipes[0], mOutput);
-                    } catch (IOException e) {
-                        Slog.i(TAG, "Caught exception reading from agent", e);
-                        result = BackupTransport.AGENT_ERROR;
+                        // Now pull data from the app and stuff it into the output
+                        try {
+                            routeSocketDataToOutput(pipes[0], mOutput);
+                        } catch (IOException e) {
+                            Slog.i(TAG, "Caught exception reading from agent", e);
+                            result = BackupTransport.AGENT_ERROR;
+                        }
+
+                        if (!waitUntilOperationComplete(token)) {
+                            Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
+                            result = BackupTransport.AGENT_ERROR;
+                        } else {
+                            if (DEBUG) Slog.d(TAG, "Full package backup success: " + pkg.packageName);
+                        }
                     }
-
-                    if (!waitUntilOperationComplete(token)) {
-                        Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
-                        result = BackupTransport.AGENT_ERROR;
-                    } else {
-                        if (DEBUG) Slog.d(TAG, "Full package backup success: " + pkg.packageName);
-                    }
-
                 } catch (IOException e) {
                     Slog.e(TAG, "Error backing up " + pkg.packageName, e);
                     result = BackupTransport.AGENT_ERROR;
@@ -3281,7 +3309,7 @@
             return result;
         }
 
-        private void writeApkToBackup(PackageInfo pkg, BackupDataOutput output) {
+        private void writeApkToBackup(PackageInfo pkg, FullBackupDataOutput output) {
             // Forward-locked apps, system-bundled .apks, etc are filtered out before we get here
             // TODO: handle backing up split APKs
             final String appSourceDir = pkg.applicationInfo.getBaseCodePath();
@@ -3780,7 +3808,7 @@
                     final boolean isSharedStorage =
                             pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
 
-                    mBackupEngine = new FullBackupEngine(out, pkg.packageName, mIncludeApks);
+                    mBackupEngine = new FullBackupEngine(out, pkg.packageName, null, mIncludeApks);
                     sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
                     mBackupEngine.backupOnePackage(pkg);
 
@@ -3827,13 +3855,13 @@
         static final String TAG = "PFTBT";
         ArrayList<PackageInfo> mPackages;
         boolean mUpdateSchedule;
-        AtomicBoolean mLatch;
+        CountDownLatch mLatch;
         AtomicBoolean mKeepRunning;     // signal from job scheduler
         FullBackupJob mJob;             // if a scheduled job needs to be finished afterwards
 
         PerformFullTransportBackupTask(IFullBackupRestoreObserver observer, 
                 String[] whichPackages, boolean updateSchedule,
-                FullBackupJob runningJob, AtomicBoolean latch) {
+                FullBackupJob runningJob, CountDownLatch latch) {
             super(observer);
             mUpdateSchedule = updateSchedule;
             mLatch = latch;
@@ -3891,6 +3919,7 @@
             ParcelFileDescriptor[] transportPipes = null;
 
             PackageInfo currentPackage;
+            long backoff = 0;
 
             try {
                 if (!mEnabled || !mProvisioned) {
@@ -3909,16 +3938,6 @@
                     return;
                 }
 
-                // Don't proceed unless we have already established package metadata
-                // for the current dataset via a key/value backup pass.
-                File stateDir = new File(mBaseStateDir, transport.transportDirName());
-                File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
-                if (pmState.length() <= 0) {
-                    Slog.i(TAG, "Full backup requested but dataset not yet initialized "
-                            + "via k/v backup pass; ignoring");
-                    return;
-                }
-
                 // Set up to send data to the transport
                 final int N = mPackages.size();
                 for (int i = 0; i < N; i++) {
@@ -3943,10 +3962,10 @@
 
                         // Now set up the backup engine / data source end of things
                         enginePipes = ParcelFileDescriptor.createPipe();
-                        AtomicBoolean runnerLatch = new AtomicBoolean(false);
+                        CountDownLatch runnerLatch = new CountDownLatch(1);
                         SinglePackageBackupRunner backupRunner =
                                 new SinglePackageBackupRunner(enginePipes[1], currentPackage,
-                                        runnerLatch);
+                                        transport, runnerLatch);
                         // The runner dup'd the pipe half, so we close it here
                         enginePipes[1].close();
                         enginePipes[1] = null;
@@ -3971,6 +3990,9 @@
                                 break;
                             }
                             nRead = in.read(buffer);
+                            if (MORE_DEBUG) {
+                                Slog.v(TAG, "in.read(buffer) from app: " + nRead);
+                            }
                             if (nRead > 0) {
                                 out.write(buffer, 0, nRead);
                                 result = transport.sendBackupData(nRead);
@@ -4002,6 +4024,14 @@
                             Slog.e(TAG, "Error " + result
                                     + " backing up " + currentPackage.packageName);
                         }
+
+                        // Also ask the transport how long it wants us to wait before
+                        // moving on to the next package, if any.
+                        backoff = transport.requestFullBackupTime();
+                        if (DEBUG_SCHEDULING) {
+                            Slog.i(TAG, "Transport suggested backoff=" + backoff);
+                        }
+
                     }
 
                     // Roll this package to the end of the backup queue if we're
@@ -4054,15 +4084,12 @@
                     mRunningFullBackupTask = null;
                 }
 
-                synchronized (mLatch) {
-                    mLatch.set(true);
-                    mLatch.notifyAll();
-                }
+                mLatch.countDown();
 
                 // Now that we're actually done with schedule-driven work, reschedule
                 // the next pass based on the new queue state.
                 if (mUpdateSchedule) {
-                    scheduleNextFullBackupJob();
+                    scheduleNextFullBackupJob(backoff);
                 }
             }
         }
@@ -4093,16 +4120,79 @@
         // Run the backup and pipe it back to the given socket -- expects to run on
         // a standalone thread.  The  runner owns this half of the pipe, and closes
         // it to indicate EOD to the other end.
+        class SinglePackageBackupPreflight implements BackupRestoreTask, FullBackupPreflight {
+            final AtomicInteger mResult = new AtomicInteger();
+            final CountDownLatch mLatch = new CountDownLatch(1);
+            final IBackupTransport mTransport;
+
+            public SinglePackageBackupPreflight(IBackupTransport transport) {
+                mTransport = transport;
+            }
+
+            @Override
+            public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) {
+                int result;
+                try {
+                    final int token = generateToken();
+                    prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL, this);
+                    addBackupTrace("preflighting");
+                    if (MORE_DEBUG) {
+                        Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
+                    }
+                    agent.doMeasureFullBackup(token, mBackupManagerBinder);
+
+                    // now wait to get our result back
+                    mLatch.await();
+                    int totalSize = mResult.get();
+                    if (MORE_DEBUG) {
+                        Slog.v(TAG, "Got preflight response; size=" + totalSize);
+                    }
+
+                    result = mTransport.checkFullBackupSize(totalSize);
+                } catch (Exception e) {
+                    Slog.w(TAG, "Exception preflighting " + pkg.packageName + ": " + e.getMessage());
+                    result = BackupTransport.AGENT_ERROR;
+                }
+                return result;
+            }
+
+            @Override
+            public void execute() {
+                // Unused in this case
+            }
+
+            @Override
+            public void operationComplete(int result) {
+                // got the callback, and our preflightFullBackup() method is waiting for the result
+                if (MORE_DEBUG) {
+                    Slog.i(TAG, "Preflight op complete, result=" + result);
+                }
+                mResult.set(result);
+                mLatch.countDown();
+            }
+
+            @Override
+            public void handleTimeout() {
+                if (MORE_DEBUG) {
+                    Slog.i(TAG, "Preflight timeout; failing");
+                }
+                mResult.set(BackupTransport.AGENT_ERROR);
+                mLatch.countDown();
+            }
+            
+        }
+
         class SinglePackageBackupRunner implements Runnable {
             final ParcelFileDescriptor mOutput;
             final PackageInfo mTarget;
-            final AtomicBoolean mLatch;
+            final FullBackupPreflight mPreflight;
+            final CountDownLatch mLatch;
 
             SinglePackageBackupRunner(ParcelFileDescriptor output, PackageInfo target,
-                    AtomicBoolean latch) throws IOException {
-                int oldfd = output.getFd();
+                    IBackupTransport transport, CountDownLatch latch) throws IOException {
                 mOutput = ParcelFileDescriptor.dup(output.getFileDescriptor());
                 mTarget = target;
+                mPreflight = new SinglePackageBackupPreflight(transport);
                 mLatch = latch;
             }
 
@@ -4110,15 +4200,13 @@
             public void run() {
                 try {
                     FileOutputStream out = new FileOutputStream(mOutput.getFileDescriptor());
-                    FullBackupEngine engine = new FullBackupEngine(out, mTarget.packageName, false);
+                    FullBackupEngine engine = new FullBackupEngine(out, mTarget.packageName,
+                            mPreflight, false);
                     engine.backupOnePackage(mTarget);
                 } catch (Exception e) {
                     Slog.e(TAG, "Exception during full package backup of " + mTarget);
                 } finally {
-                    synchronized (mLatch) {
-                        mLatch.set(true);
-                        mLatch.notifyAll();
-                    }
+                    mLatch.countDown();
                     try {
                         mOutput.close();
                     } catch (IOException e) {
@@ -4126,7 +4214,6 @@
                     }
                 }
             }
-            
         }
     }
 
@@ -4135,16 +4222,17 @@
     /**
      * Schedule a job to tell us when it's a good time to run a full backup
      */
-    void scheduleNextFullBackupJob() {
+    void scheduleNextFullBackupJob(long transportMinLatency) {
         synchronized (mQueueLock) {
             if (mFullBackupQueue.size() > 0) {
                 // schedule the next job at the point in the future when the least-recently
                 // backed up app comes due for backup again; or immediately if it's already
                 // due.
-                long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
-                long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
-                final long latency = (timeSinceLast < MIN_FULL_BACKUP_INTERVAL)
+                final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
+                final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
+                final long appLatency = (timeSinceLast < MIN_FULL_BACKUP_INTERVAL)
                         ? (MIN_FULL_BACKUP_INTERVAL - timeSinceLast) : 0;
+                final long latency = Math.min(transportMinLatency, appLatency);
                 Runnable r = new Runnable() {
                     @Override public void run() {
                         FullBackupJob.schedule(mContext, latency);
@@ -4198,6 +4286,31 @@
         writeFullBackupScheduleAsync();
     }
 
+    private boolean fullBackupAllowable(IBackupTransport transport) {
+        if (transport == null) {
+            Slog.w(TAG, "Transport not present; full data backup not performed");
+            return false;
+        }
+
+        // Don't proceed unless we have already established package metadata
+        // for the current dataset via a key/value backup pass.
+        try {
+            File stateDir = new File(mBaseStateDir, transport.transportDirName());
+            File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
+            if (pmState.length() <= 0) {
+                if (DEBUG) {
+                    Slog.i(TAG, "Full backup requested but dataset not yet initialized");
+                }
+                return false;
+            }
+        } catch (Exception e) {
+            Slog.w(TAG, "Unable to contact transport");
+            return false;
+        }
+
+        return true;
+    }
+
     /**
      * Conditions are right for a full backup operation, so run one.  The model we use is
      * to perform one app backup per scheduled job execution, and to reschedule the job
@@ -4209,6 +4322,7 @@
     boolean beginFullBackup(FullBackupJob scheduledJob) {
         long now = System.currentTimeMillis();
         FullBackupEntry entry = null;
+        long latency = MIN_FULL_BACKUP_INTERVAL;
 
         if (!mEnabled || !mProvisioned) {
             // Backups are globally disabled, so don't proceed.  We also don't reschedule
@@ -4240,17 +4354,41 @@
                 return false;
             }
 
-            entry = mFullBackupQueue.get(0);
-            long timeSinceRun = now - entry.lastBackup;
-            if (timeSinceRun < MIN_FULL_BACKUP_INTERVAL) {
-                // It's too early to back up the next thing in the queue, so bow out
+            // At this point we know that we have work to do, just not right now.  Any
+            // exit without actually running backups will also require that we
+            // reschedule the job.
+            boolean runBackup = true;
+
+            if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
                 if (MORE_DEBUG) {
-                    Slog.i(TAG, "Device ready but too early to back up next app");
+                    Slog.i(TAG, "Preconditions not met; not running full backup");
                 }
-                final long latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
+                runBackup = false;
+                // Typically this means we haven't run a key/value backup yet.  Back off
+                // full-backup operations by the key/value job's run interval so that
+                // next time we run, we are likely to be able to make progress.
+                latency = KeyValueBackupJob.BATCH_INTERVAL;
+            }
+
+            if (runBackup) {
+                entry = mFullBackupQueue.get(0);
+                long timeSinceRun = now - entry.lastBackup;
+                runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL);
+                if (!runBackup) {
+                    // It's too early to back up the next thing in the queue, so bow out
+                    if (MORE_DEBUG) {
+                        Slog.i(TAG, "Device ready but too early to back up next app");
+                    }
+                    // Wait until the next app in the queue falls due for a full data backup
+                    latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
+                }
+            }
+
+            if (!runBackup) {
+                final long deferTime = latency;     // pin for the closure
                 mBackupHandler.post(new Runnable() {
                     @Override public void run() {
-                        FullBackupJob.schedule(mContext, latency);
+                        FullBackupJob.schedule(mContext, deferTime);
                     }
                 });
                 return false;
@@ -4258,7 +4396,7 @@
 
             // Okay, the top thing is runnable now.  Pop it off and get going.
             mFullBackupQueue.remove(0);
-            AtomicBoolean latch = new AtomicBoolean(false);
+            CountDownLatch latch = new CountDownLatch(1);
             String[] pkg = new String[] {entry.packageName};
             mRunningFullBackupTask = new PerformFullTransportBackupTask(null, pkg, true,
                     scheduledJob, latch);
@@ -4812,7 +4950,7 @@
             }
         }
 
-        class RestoreInstallObserver extends IPackageInstallObserver.Stub {
+        class RestoreInstallObserver extends PackageInstallObserver {
             final AtomicBoolean mDone = new AtomicBoolean();
             String mPackageName;
             int mResult;
@@ -4838,8 +4976,8 @@
             }
 
             @Override
-            public void packageInstalled(String packageName, int returnCode)
-                    throws RemoteException {
+            public void onPackageInstalled(String packageName, int returnCode,
+                    String msg, Bundle extras) {
                 synchronized (mDone) {
                     mResult = returnCode;
                     mPackageName = packageName;
@@ -5095,7 +5233,9 @@
                         offset = extractLine(buffer, offset, str);
                         version = Integer.parseInt(str[0]);  // app version
                         offset = extractLine(buffer, offset, str);
-                        int platformVersion = Integer.parseInt(str[0]);
+                        // This is the platform version, which we don't use, but we parse it
+                        // as a safety against corruption in the manifest.
+                        Integer.parseInt(str[0]);
                         offset = extractLine(buffer, offset, str);
                         info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
                         offset = extractLine(buffer, offset, str);
@@ -6156,7 +6296,7 @@
             }
         }
 
-        class RestoreInstallObserver extends IPackageInstallObserver.Stub {
+        class RestoreInstallObserver extends PackageInstallObserver {
             final AtomicBoolean mDone = new AtomicBoolean();
             String mPackageName;
             int mResult;
@@ -6182,8 +6322,8 @@
             }
 
             @Override
-            public void packageInstalled(String packageName, int returnCode)
-                    throws RemoteException {
+            public void onPackageInstalled(String packageName, int returnCode,
+                    String msg, Bundle extras) {
                 synchronized (mDone) {
                     mResult = returnCode;
                     mPackageName = packageName;
@@ -6432,7 +6572,9 @@
                         offset = extractLine(buffer, offset, str);
                         version = Integer.parseInt(str[0]);  // app version
                         offset = extractLine(buffer, offset, str);
-                        int platformVersion = Integer.parseInt(str[0]);
+                        // This is the platform version, which we don't use, but we parse it
+                        // as a safety against corruption in the manifest.
+                        Integer.parseInt(str[0]);
                         offset = extractLine(buffer, offset, str);
                         info.installerPackageName = (str[0].length() > 0) ? str[0] : null;
                         offset = extractLine(buffer, offset, str);
@@ -7891,7 +8033,7 @@
         }
 
         @Override
-        public void operationComplete() {
+        public void operationComplete(int unusedResult) {
             if (MORE_DEBUG) {
                 Slog.i(TAG, "operationComplete() during restore: target="
                         + mCurrentPackage.packageName
@@ -8357,7 +8499,9 @@
             }
 
             // make sure the screen is lit for the user interaction
-            mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
+            mPowerManager.userActivity(SystemClock.uptimeMillis(),
+                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
+                    0);
 
             // start the confirmation countdown
             startConfirmationTimeout(token, params);
@@ -8385,21 +8529,33 @@
             throw new IllegalStateException("Restore supported only for the device owner");
         }
 
-        if (DEBUG) {
-            Slog.d(TAG, "fullTransportBackup()");
+        if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
+            Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
+        } else {
+            if (DEBUG) {
+                Slog.d(TAG, "fullTransportBackup()");
+            }
+
+            CountDownLatch latch = new CountDownLatch(1);
+            PerformFullTransportBackupTask task =
+                    new PerformFullTransportBackupTask(null, pkgNames, false, null, latch);
+            (new Thread(task, "full-transport-master")).start();
+            do {
+                try {
+                    latch.await();
+                    break;
+                } catch (InterruptedException e) {
+                    // Just go back to waiting for the latch to indicate completion
+                }
+            } while (true);
+
+            // We just ran a backup on these packages, so kick them to the end of the queue
+            final long now = System.currentTimeMillis();
+            for (String pkg : pkgNames) {
+                enqueueFullBackup(pkg, now);
+            }
         }
 
-        AtomicBoolean latch = new AtomicBoolean(false);
-        PerformFullTransportBackupTask task =
-                new PerformFullTransportBackupTask(null, pkgNames, false, null, latch);
-        (new Thread(task, "full-transport-master")).start();
-        synchronized (latch) {
-            try {
-                while (latch.get() == false) {
-                    latch.wait();
-                }
-            } catch (InterruptedException e) {}
-        }
         if (DEBUG) {
             Slog.d(TAG, "Done with full transport backup.");
         }
@@ -8440,7 +8596,9 @@
             }
 
             // make sure the screen is lit for the user interaction
-            mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
+            mPowerManager.userActivity(SystemClock.uptimeMillis(),
+                    PowerManager.USER_ACTIVITY_EVENT_OTHER,
+                    0);
 
             // start the confirmation countdown
             startConfirmationTimeout(token, params);
@@ -8567,7 +8725,7 @@
                 if (enable && !wasEnabled && mProvisioned) {
                     // if we've just been enabled, start scheduling backup passes
                     KeyValueBackupJob.schedule(mContext);
-                    scheduleNextFullBackupJob();
+                    scheduleNextFullBackupJob(0);
                 } else if (!enable) {
                     // No longer enabled, so stop running backups
                     if (DEBUG) Slog.i(TAG, "Opting out of backup");
@@ -8940,8 +9098,10 @@
 
     // Note that a currently-active backup agent has notified us that it has
     // completed the given outstanding asynchronous backup/restore operation.
-    public void opComplete(int token) {
-        if (MORE_DEBUG) Slog.v(TAG, "opComplete: " + Integer.toHexString(token));
+    public void opComplete(int token, long result) {
+        if (MORE_DEBUG) {
+            Slog.v(TAG, "opComplete: " + Integer.toHexString(token) + " result=" + result);
+        }
         Operation op = null;
         synchronized (mCurrentOpLock) {
             op = mCurrentOperations.get(token);
@@ -8954,6 +9114,8 @@
         // The completion callback, if any, is invoked on the handler
         if (op != null && op.callback != null) {
             Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, op.callback);
+            // NB: this cannot distinguish between results > 2 gig
+            msg.arg1 = (result > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) result;
             mBackupHandler.sendMessage(msg);
         }
     }
diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
index dc1c9d5..a4489c1 100644
--- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
+++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
@@ -41,7 +41,7 @@
     // Once someone asks for a backup, this is how long we hold off, batching
     // up additional requests, before running the actual backup pass.  Privileged
     // callers can always trigger an immediate pass via BackupManager.backupNow().
-    private static final long BATCH_INTERVAL = 4 * AlarmManager.INTERVAL_HOUR;
+    static final long BATCH_INTERVAL = 4 * AlarmManager.INTERVAL_HOUR;
 
     // Random variation in next-backup scheduling time to avoid server load spikes
     private static final int FUZZ_MILLIS = 10 * 60 * 1000;
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 2e84fbe..99bbdae 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -309,10 +309,10 @@
     }
 
     @Override
-    public void opComplete(int token) throws RemoteException {
+    public void opComplete(int token, long result) throws RemoteException {
         BackupManagerService svc = mService;
         if (svc != null) {
-            svc.opComplete(token);
+            svc.opComplete(token, result);
         }
     }
 
diff --git a/services/core/Android.mk b/services/core/Android.mk
index 1a0fa34..64b6134 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -10,5 +10,6 @@
     java/com/android/server/am/EventLogTags.logtags
 
 LOCAL_JAVA_LIBRARIES := telephony-common
+LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/core/java/com/android/server/AssetAtlasService.java b/services/core/java/com/android/server/AssetAtlasService.java
index e6dc1c7..f106667 100644
--- a/services/core/java/com/android/server/AssetAtlasService.java
+++ b/services/core/java/com/android/server/AssetAtlasService.java
@@ -403,13 +403,13 @@
         if (cpuCount == 1) {
             new ComputeWorker(MIN_SIZE, MAX_SIZE, STEP, bitmaps, pixelCount, results, null).run();
         } else {
-            int start = MIN_SIZE;
-            int end = MAX_SIZE - (cpuCount - 1) * STEP;
+            int start = MIN_SIZE + (cpuCount - 1) * STEP;
+            int end = MAX_SIZE;
             int step = STEP * cpuCount;
 
             final CountDownLatch signal = new CountDownLatch(cpuCount);
 
-            for (int i = 0; i < cpuCount; i++, start += STEP, end += STEP) {
+            for (int i = 0; i < cpuCount; i++, start -= STEP, end -= STEP) {
                 ComputeWorker worker = new ComputeWorker(start, end, step,
                         bitmaps, pixelCount, results, signal);
                 new Thread(worker, "Atlas Worker #" + (i + 1)).start();
@@ -435,7 +435,8 @@
 
         if (DEBUG_ATLAS) {
             float delay = (System.nanoTime() - begin) / 1000.0f / 1000.0f / 1000.0f;
-            Log.d(LOG_TAG, String.format("Found best atlas configuration in %.2fs", delay));
+            Log.d(LOG_TAG, String.format("Found best atlas configuration (out of %d) in %.2fs",
+                    results.size(), delay));
         }
 
         WorkerResult result = results.get(0);
@@ -696,8 +697,8 @@
 
             Atlas.Entry entry = new Atlas.Entry();
             for (Atlas.Type type : Atlas.Type.values()) {
-                for (int width = mStart; width < mEnd; width += mStep) {
-                    for (int height = MIN_SIZE; height < MAX_SIZE; height += STEP) {
+                for (int width = mEnd; width > mStart; width -= mStep) {
+                    for (int height = MAX_SIZE; height > MIN_SIZE; height -= STEP) {
                         // If the atlas is not big enough, skip it
                         if (width * height <= mThreshold) continue;
 
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 983d83a..694e851 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -221,3 +221,4 @@
 # AudioService.java
 # ---------------------------
 40000 volume_changed (stream|1), (prev_level|1), (level|1), (max_level|1), (caller|3)
+40001 stream_devices_changed (stream|1), (prev_devices|1), (devices|1)
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java
new file mode 100644
index 0000000..c79fdfc
--- /dev/null
+++ b/services/core/java/com/android/server/GraphicsStatsService.java
@@ -0,0 +1,256 @@
+/*
+ * 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.server;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.MemoryFile;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IGraphicsStats;
+import android.view.ThreadedRenderer;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * This service's job is to collect aggregate rendering profile data. It
+ * does this by allowing rendering processes to request an ashmem buffer
+ * to place their stats into. This buffer will be pre-initialized with historical
+ * data for that process if it exists (if the userId & packageName match a buffer
+ * in the historical log)
+ *
+ * This service does not itself attempt to understand the data in the buffer,
+ * its primary job is merely to manage distributing these buffers. However,
+ * it is assumed that this buffer is for ThreadedRenderer and delegates
+ * directly to ThreadedRenderer for dumping buffers.
+ *
+ * MEMORY USAGE:
+ *
+ * This class consumes UP TO:
+ * 1) [active rendering processes] * (ASHMEM_SIZE * 2)
+ * 2) ASHMEM_SIZE (for scratch space used during dumping)
+ * 3) ASHMEM_SIZE * HISTORY_SIZE
+ *
+ * Currently ASHMEM_SIZE is 256 bytes and HISTORY_SIZE is 10. Assuming
+ * the system then also has 10 active rendering processes in the worst case
+ * this would end up using under 10KiB (8KiB for the buffers, plus some overhead
+ * for userId, pid, package name, and a couple other objects)
+ *
+ *  @hide */
+public class GraphicsStatsService extends IGraphicsStats.Stub {
+    public static final String GRAPHICS_STATS_SERVICE = "graphicsstats";
+
+    private static final String TAG = "GraphicsStatsService";
+    private static final int ASHMEM_SIZE = 256;
+    private static final int HISTORY_SIZE = 10;
+
+    private final Context mContext;
+    private final Object mLock = new Object();
+    private ArrayList<ActiveBuffer> mActive = new ArrayList<>();
+    private HistoricalData[] mHistoricalLog = new HistoricalData[HISTORY_SIZE];
+    private int mNextHistoricalSlot = 0;
+    private byte[] mTempBuffer = new byte[ASHMEM_SIZE];
+
+    public GraphicsStatsService(Context context) {
+        mContext = context;
+    }
+
+    private boolean isValid(int uid, String packageName) {
+        try {
+            PackageInfo info = mContext.getPackageManager().getPackageInfo(packageName, 0);
+            return info.applicationInfo.uid == uid;
+        } catch (NameNotFoundException e) {
+        }
+        return false;
+    }
+
+    @Override
+    public ParcelFileDescriptor requestBufferForProcess(String packageName, IBinder token)
+            throws RemoteException {
+        int uid = Binder.getCallingUid();
+        int pid = Binder.getCallingPid();
+        ParcelFileDescriptor pfd = null;
+        long callingIdentity = Binder.clearCallingIdentity();
+        try {
+            if (!isValid(uid, packageName)) {
+                throw new RemoteException("Invalid package name");
+            }
+            synchronized (mLock) {
+                pfd = requestBufferForProcessLocked(token, uid, pid, packageName);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentity);
+        }
+        return pfd;
+    }
+
+    private ParcelFileDescriptor getPfd(MemoryFile file) {
+        try {
+            return new ParcelFileDescriptor(file.getFileDescriptor());
+        } catch (IOException ex) {
+            throw new IllegalStateException("Failed to get PFD from memory file", ex);
+        }
+    }
+
+    private ParcelFileDescriptor requestBufferForProcessLocked(IBinder token,
+            int uid, int pid, String packageName) throws RemoteException {
+        ActiveBuffer buffer = fetchActiveBuffersLocked(token, uid, pid, packageName);
+        return getPfd(buffer.mProcessBuffer);
+    }
+
+    private void processDied(ActiveBuffer buffer) {
+        synchronized (mLock) {
+            mActive.remove(buffer);
+            Log.d("GraphicsStats", "Buffer count: " + mActive.size());
+        }
+        HistoricalData data = buffer.mPreviousData;
+        buffer.mPreviousData = null;
+        if (data == null) {
+            data = mHistoricalLog[mNextHistoricalSlot];
+            if (data == null) {
+                data = new HistoricalData();
+            }
+        }
+        data.update(buffer.mPackageName, buffer.mUid, buffer.mProcessBuffer);
+        buffer.closeAllBuffers();
+
+        mHistoricalLog[mNextHistoricalSlot] = data;
+        mNextHistoricalSlot = (mNextHistoricalSlot + 1) % mHistoricalLog.length;
+    }
+
+    private ActiveBuffer fetchActiveBuffersLocked(IBinder token, int uid, int pid,
+            String packageName) throws RemoteException {
+        int size = mActive.size();
+        for (int i = 0; i < size; i++) {
+            ActiveBuffer buffers = mActive.get(i);
+            if (buffers.mPid == pid
+                    && buffers.mUid == uid) {
+                return buffers;
+            }
+        }
+        // Didn't find one, need to create it
+        try {
+            ActiveBuffer buffers = new ActiveBuffer(token, uid, pid, packageName);
+            mActive.add(buffers);
+            return buffers;
+        } catch (IOException ex) {
+            throw new RemoteException("Failed to allocate space");
+        }
+    }
+
+    private HistoricalData removeHistoricalDataLocked(int uid, String packageName) {
+        for (int i = 0; i < mHistoricalLog.length; i++) {
+            final HistoricalData data = mHistoricalLog[i];
+            if (data != null && data.mUid == uid
+                    && data.mPackageName.equals(packageName)) {
+                if (i == mNextHistoricalSlot) {
+                    mHistoricalLog[i] = null;
+                } else {
+                    mHistoricalLog[i] = mHistoricalLog[mNextHistoricalSlot];
+                    mHistoricalLog[mNextHistoricalSlot] = null;
+                }
+                return data;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
+        synchronized (mLock) {
+            for (int i = 0; i < mActive.size(); i++) {
+                final ActiveBuffer buffer = mActive.get(i);
+                fout.print("Package: ");
+                fout.print(buffer.mPackageName);
+                fout.flush();
+                try {
+                    buffer.mProcessBuffer.readBytes(mTempBuffer, 0, 0, ASHMEM_SIZE);
+                    ThreadedRenderer.dumpProfileData(mTempBuffer, fd);
+                } catch (IOException e) {
+                    fout.println("Failed to dump");
+                }
+                fout.println();
+            }
+            for (HistoricalData buffer : mHistoricalLog) {
+                if (buffer == null) continue;
+                fout.print("Package: ");
+                fout.print(buffer.mPackageName);
+                fout.flush();
+                ThreadedRenderer.dumpProfileData(buffer.mBuffer, fd);
+                fout.println();
+            }
+        }
+    }
+
+    private final class ActiveBuffer implements DeathRecipient {
+        final int mUid;
+        final int mPid;
+        final String mPackageName;
+        final IBinder mToken;
+        MemoryFile mProcessBuffer;
+        HistoricalData mPreviousData;
+
+        ActiveBuffer(IBinder token, int uid, int pid, String packageName)
+                throws RemoteException, IOException {
+            mUid = uid;
+            mPid = pid;
+            mPackageName = packageName;
+            mToken = token;
+            mToken.linkToDeath(this, 0);
+            mProcessBuffer = new MemoryFile("GFXStats-" + uid, ASHMEM_SIZE);
+            mPreviousData = removeHistoricalDataLocked(mUid, mPackageName);
+            if (mPreviousData != null) {
+                mProcessBuffer.writeBytes(mPreviousData.mBuffer, 0, 0, ASHMEM_SIZE);
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            mToken.unlinkToDeath(this, 0);
+            processDied(this);
+        }
+
+        void closeAllBuffers() {
+            if (mProcessBuffer != null) {
+                mProcessBuffer.close();
+                mProcessBuffer = null;
+            }
+        }
+    }
+
+    private final static class HistoricalData {
+        final byte[] mBuffer = new byte[ASHMEM_SIZE];
+        int mUid;
+        String mPackageName;
+
+        void update(String packageName, int uid, MemoryFile file) {
+            mUid = uid;
+            mPackageName = packageName;
+            try {
+                file.readBytes(mBuffer, 0, 0, ASHMEM_SIZE);
+            } catch (IOException e) {}
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index fd35b5e..4677f65 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -86,7 +86,10 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.text.TextUtils.SimpleStringSplitter;
 import android.text.style.SuggestionSpan;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.EventLog;
 import android.util.LruCache;
@@ -125,6 +128,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 
@@ -134,8 +138,12 @@
 public class InputMethodManagerService extends IInputMethodManager.Stub
         implements ServiceConnection, Handler.Callback {
     static final boolean DEBUG = false;
+    static final boolean DEBUG_RESTORE = DEBUG || false;
     static final String TAG = "InputMethodManagerService";
 
+    private static final char INPUT_METHOD_SEPARATOR = ':';
+    private static final char INPUT_METHOD_SUBTYPE_SEPARATOR = ';';
+
     static final int MSG_SHOW_IM_PICKER = 1;
     static final int MSG_SHOW_IM_SUBTYPE_PICKER = 2;
     static final int MSG_SHOW_IM_SUBTYPE_ENABLER = 3;
@@ -466,12 +474,101 @@
                     || Intent.ACTION_USER_REMOVED.equals(action)) {
                 updateCurrentProfileIds();
                 return;
+            } else if (Intent.ACTION_SETTING_RESTORED.equals(action)) {
+                final String name = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
+                if (Settings.Secure.ENABLED_INPUT_METHODS.equals(name)) {
+                    final String prevValue = intent.getStringExtra(
+                            Intent.EXTRA_SETTING_PREVIOUS_VALUE);
+                    final String newValue = intent.getStringExtra(
+                            Intent.EXTRA_SETTING_NEW_VALUE);
+                    restoreEnabledInputMethods(mContext, prevValue, newValue);
+                }
             } else {
                 Slog.w(TAG, "Unexpected intent " + intent);
             }
         }
     }
 
+    // Apply the results of a restore operation to the set of enabled IMEs.  Note that this
+    // does not attempt to validate on the fly with any installed device policy, so must only
+    // be run in the context of initial device setup.
+    //
+    // TODO: Move this method to InputMethodUtils with adding unit tests.
+    static void restoreEnabledInputMethods(Context context, String prevValue, String newValue) {
+        if (DEBUG_RESTORE) {
+            Slog.i(TAG, "Restoring enabled input methods:");
+            Slog.i(TAG, "prev=" + prevValue);
+            Slog.i(TAG, " new=" + newValue);
+        }
+        // 'new' is the just-restored state, 'prev' is what was in settings prior to the restore
+        ArrayMap<String, ArraySet<String>> prevMap = parseInputMethodsAndSubtypesString(prevValue);
+        ArrayMap<String, ArraySet<String>> newMap = parseInputMethodsAndSubtypesString(newValue);
+
+        // Merge the restored ime+subtype enabled states into the live state
+        for (ArrayMap.Entry<String, ArraySet<String>> entry : newMap.entrySet()) {
+            final String imeId = entry.getKey();
+            ArraySet<String> prevSubtypes = prevMap.get(imeId);
+            if (prevSubtypes == null) {
+                prevSubtypes = new ArraySet<String>(2);
+                prevMap.put(imeId, prevSubtypes);
+            }
+            prevSubtypes.addAll(entry.getValue());
+        }
+
+        final String mergedImesAndSubtypesString = buildInputMethodsAndSubtypesString(prevMap);
+        if (DEBUG_RESTORE) {
+            Slog.i(TAG, "Merged IME string:");
+            Slog.i(TAG, "     " + mergedImesAndSubtypesString);
+        }
+        Settings.Secure.putString(context.getContentResolver(),
+                Settings.Secure.ENABLED_INPUT_METHODS, mergedImesAndSubtypesString);
+    }
+
+    // TODO: Move this method to InputMethodUtils with adding unit tests.
+    static String buildInputMethodsAndSubtypesString(ArrayMap<String, ArraySet<String>> map) {
+        // we want to use the canonical InputMethodSettings implementation,
+        // so we convert data structures first.
+        List<Pair<String, ArrayList<String>>> imeMap =
+                new ArrayList<Pair<String, ArrayList<String>>>(4);
+        for (ArrayMap.Entry<String, ArraySet<String>> entry : map.entrySet()) {
+            final String imeName = entry.getKey();
+            final ArraySet<String> subtypeSet = entry.getValue();
+            final ArrayList<String> subtypes = new ArrayList<String>(2);
+            if (subtypeSet != null) {
+                subtypes.addAll(subtypeSet);
+            }
+            imeMap.add(new Pair<String, ArrayList<String>>(imeName, subtypes));
+        }
+        return InputMethodSettings.buildInputMethodsSettingString(imeMap);
+    }
+
+    // TODO: Move this method to InputMethodUtils with adding unit tests.
+    static ArrayMap<String, ArraySet<String>> parseInputMethodsAndSubtypesString(
+            final String inputMethodsAndSubtypesString) {
+        final ArrayMap<String, ArraySet<String>> imeMap =
+                new ArrayMap<String, ArraySet<String>>();
+        if (TextUtils.isEmpty(inputMethodsAndSubtypesString)) {
+            return imeMap;
+        }
+
+        final SimpleStringSplitter typeSplitter =
+                new SimpleStringSplitter(INPUT_METHOD_SEPARATOR);
+        final SimpleStringSplitter subtypeSplitter =
+                new SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR);
+        List<Pair<String, ArrayList<String>>> allImeSettings =
+                InputMethodSettings.buildInputMethodsAndSubtypeList(inputMethodsAndSubtypesString,
+                        typeSplitter,
+                        subtypeSplitter);
+        for (Pair<String, ArrayList<String>> ime : allImeSettings) {
+            ArraySet<String> subtypes = new ArraySet<String>();
+            if (ime.second != null) {
+                subtypes.addAll(ime.second);
+            }
+            imeMap.put(ime.first, subtypes);
+        }
+        return imeMap;
+    }
+
     class MyPackageMonitor extends PackageMonitor {
         private boolean isChangingPackagesOfCurrentUser() {
             final int userId = getChangingUserId();
@@ -675,6 +772,7 @@
         broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
         broadcastFilter.addAction(Intent.ACTION_USER_ADDED);
         broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
+        broadcastFilter.addAction(Intent.ACTION_SETTING_RESTORED);
         mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter);
 
         mNotificationShown = false;
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index cea1ebe..744156b 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -47,6 +47,7 @@
     final private static String TAG = "IntentResolver";
     final private static boolean DEBUG = false;
     final private static boolean localLOGV = DEBUG || false;
+    final private static boolean localVerificationLOGV = DEBUG || false;
 
     public void addFilter(F f) {
         if (localLOGV) {
@@ -478,7 +479,7 @@
 
     /**
      * Returns whether the object associated with the given filter is
-     * "stopped," that is whether it should not be included in the result
+     * "stopped", that is whether it should not be included in the result
      * if the intent requests to excluded stopped objects.
      */
     protected boolean isFilterStopped(F filter, int userId) {
@@ -486,6 +487,22 @@
     }
 
     /**
+     * Returns whether the given filter is "verified" that is whether it has been verified against
+     * its data URIs.
+     *
+     * The verification would happen only and only if the Intent action is
+     * {@link android.content.Intent#ACTION_VIEW} and the Intent category is
+     * {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent data scheme
+     * is "http" or "https".
+     *
+     * @see android.content.IntentFilter#setAutoVerify(boolean)
+     * @see android.content.IntentFilter#getAutoVerify()
+     */
+    protected boolean isFilterVerified(F filter) {
+        return filter.isVerified();
+    }
+
+    /**
      * Returns whether this filter is owned by this package. This must be
      * implemented to provide correct filtering of Intents that have
      * specified a package name they are to be delivered to.
@@ -710,6 +727,13 @@
                 continue;
             }
 
+            // Are we verified ?
+            if (filter.getAutoVerify()) {
+                if (localVerificationLOGV || debug) {
+                    Slog.v(TAG, "  Filter verified: " + isFilterVerified(filter));
+                }
+            }
+
             // Do we already have this one?
             if (!allowFilterResult(filter, dest)) {
                 if (debug) {
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index b8d9ec5..61286e8 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -16,29 +16,21 @@
 
 package com.android.server;
 
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
 import android.Manifest;
 import android.app.ActivityManagerNative;
 import android.app.AppOpsManager;
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.content.res.ObbInfo;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.hardware.usb.UsbManager;
+import android.mtp.MtpStorage;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Environment.UserEnvironment;
+import android.os.FileUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -59,26 +51,24 @@
 import android.os.storage.StorageResultCode;
 import android.os.storage.StorageVolume;
 import android.text.TextUtils;
-import android.util.AttributeSet;
+import android.util.ArrayMap;
+import android.util.DebugUtils;
 import android.util.Slog;
-import android.util.Xml;
+import android.util.SparseArray;
+
+import libcore.util.EmptyArray;
+import libcore.util.HexEncoding;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IMediaContainerService;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
-import com.android.internal.util.XmlUtils;
 import com.android.server.NativeDaemonConnector.Command;
 import com.android.server.NativeDaemonConnector.SensitiveArg;
-import com.android.server.am.ActivityManagerService;
 import com.android.server.pm.PackageManagerService;
-import com.android.server.pm.UserManagerService;
 import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-import libcore.util.HexEncoding;
-
-import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -101,7 +91,6 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -110,19 +99,18 @@
 import javax.crypto.spec.PBEKeySpec;
 
 /**
- * MountService implements back-end services for platform storage
- * management.
- * @hide - Applications should use android.os.storage.StorageManager
- * to access the MountService.
+ * Service responsible for various storage media. Connects to {@code vold} to
+ * watch for and manage dynamically added storage, such as SD cards and USB mass
+ * storage. Also decides how storage should be presented to users on the device.
  */
 class MountService extends IMountService.Stub
         implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
 
+    // TODO: finish enforcing UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA
+
     // Static direct instance pointer for the tightly-coupled idle service to use
     static MountService sSelf = null;
 
-    // TODO: listen for user creation/deletion
-
     public static class Lifecycle extends SystemService {
         private MountService mMountService;
 
@@ -142,6 +130,16 @@
                 mMountService.systemReady();
             }
         }
+
+        @Override
+        public void onStartUser(int userHandle) {
+            mMountService.onStartUser(userHandle);
+        }
+
+        @Override
+        public void onCleanupUser(int userHandle) {
+            mMountService.onCleanupUser(userHandle);
+        }
     }
 
     private static final boolean LOCAL_LOGD = false;
@@ -159,21 +157,7 @@
     /** Maximum number of ASEC containers allowed to be mounted. */
     private static final int MAX_CONTAINERS = 250;
 
-    /*
-     * Internal vold volume state constants
-     */
-    class VolumeState {
-        public static final int Init       = -1;
-        public static final int NoMedia    = 0;
-        public static final int Idle       = 1;
-        public static final int Pending    = 2;
-        public static final int Checking   = 3;
-        public static final int Mounted    = 4;
-        public static final int Unmounting = 5;
-        public static final int Formatting = 6;
-        public static final int Shared     = 7;
-        public static final int SharedMnt  = 8;
-    }
+    private static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical";
 
     /*
      * Internal vold response code constants
@@ -209,12 +193,19 @@
         /*
          * 600 series - Unsolicited broadcasts.
          */
-        public static final int VolumeStateChange              = 605;
-        public static final int VolumeUuidChange               = 613;
-        public static final int VolumeUserLabelChange          = 614;
-        public static final int VolumeDiskInserted             = 630;
-        public static final int VolumeDiskRemoved              = 631;
-        public static final int VolumeBadRemoval               = 632;
+        public static final int DISK_CREATED = 640;
+        public static final int DISK_SIZE_CHANGED = 641;
+        public static final int DISK_LABEL_CHANGED = 642;
+        public static final int DISK_VOLUME_CREATED = 643;
+        public static final int DISK_DESTROYED = 649;
+
+        public static final int VOLUME_CREATED = 650;
+        public static final int VOLUME_STATE_CHANGED = 651;
+        public static final int VOLUME_FS_TYPE_CHANGED = 652;
+        public static final int VOLUME_FS_UUID_CHANGED = 653;
+        public static final int VOLUME_FS_LABEL_CHANGED = 654;
+        public static final int VOLUME_PATH_CHANGED = 655;
+        public static final int VOLUME_DESTROYED = 659;
 
         /*
          * 700 series - fstrim
@@ -222,6 +213,243 @@
         public static final int FstrimCompleted                = 700;
     }
 
+    private static SparseArray<String> sStateToEnvironment = new SparseArray<>();
+    private static ArrayMap<String, String> sEnvironmentToBroadcast = new ArrayMap<>();
+
+    static {
+        sStateToEnvironment.put(Volume.STATE_UNMOUNTED, Environment.MEDIA_UNMOUNTED);
+        sStateToEnvironment.put(Volume.STATE_MOUNTING, Environment.MEDIA_CHECKING);
+        sStateToEnvironment.put(Volume.STATE_MOUNTED, Environment.MEDIA_MOUNTED);
+        sStateToEnvironment.put(Volume.STATE_FORMATTING, Environment.MEDIA_UNMOUNTED);
+        sStateToEnvironment.put(Volume.STATE_UNMOUNTING, Environment.MEDIA_EJECTING);
+
+        sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTED, Intent.ACTION_MEDIA_UNMOUNTED);
+        sEnvironmentToBroadcast.put(Environment.MEDIA_CHECKING, Intent.ACTION_MEDIA_CHECKING);
+        sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED, Intent.ACTION_MEDIA_MOUNTED);
+        sEnvironmentToBroadcast.put(Environment.MEDIA_EJECTING, Intent.ACTION_MEDIA_EJECT);
+    }
+
+    /**
+     * <em>Never</em> hold the lock while performing downcalls into vold, since
+     * unsolicited events can suddenly appear to update data structures.
+     */
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private int[] mStartedUsers = EmptyArray.INT;
+    @GuardedBy("mLock")
+    private ArrayMap<String, Disk> mDisks = new ArrayMap<>();
+    @GuardedBy("mLock")
+    private ArrayMap<String, Volume> mVolumes = new ArrayMap<>();
+
+    @Deprecated
+    private Volume findVolumeByLegacyPath(String legacyPath) {
+        synchronized (mLock) {
+            for (Volume vol : mVolumes.values()) {
+                if (vol.path != null && legacyPath.startsWith(vol.path)) {
+                    return vol;
+                }
+            }
+        }
+        Slog.w(TAG, "Failed to find volume for path " + legacyPath);
+        return null;
+    }
+
+    /**
+     * Framework-side twin of android::vold::Disk
+     */
+    private class Disk {
+        public static final int FLAG_ADOPTABLE = 1 << 0;
+        public static final int FLAG_DEFAULT_PRIMARY = 1 << 1;
+        public static final int FLAG_SD = 1 << 2;
+        public static final int FLAG_USB = 1 << 3;
+
+        public final String id;
+        public final int flags;
+        public long size;
+        public String label;
+
+        public ArrayList<Volume> volumes = new ArrayList<>();
+
+        public Disk(String id, int flags) {
+            this.id = id;
+            this.flags = flags;
+        }
+
+        public void partitionPublic() throws NativeDaemonConnectorException {
+            mConnector.execute("volume", "partition", id, "public");
+        }
+
+        public void partitionPrivate() throws NativeDaemonConnectorException {
+            mConnector.execute("volume", "partition", id, "private");
+        }
+
+        public void partitionMixed(int frac) throws NativeDaemonConnectorException {
+            mConnector.execute("volume", "partition", id, "mixed", frac);
+        }
+
+        public void dump(IndentingPrintWriter pw) {
+            pw.println("Disk:");
+            pw.increaseIndent();
+            pw.printPair("id", id);
+            pw.printPair("flags", DebugUtils.flagsToString(getClass(), "FLAG_", flags));
+            pw.printPair("size", size);
+            pw.printPair("label", label);
+            pw.decreaseIndent();
+            pw.println();
+        }
+    }
+
+    private static int sNextMtpIndex = 1;
+
+    /**
+     * Framework-side twin of android::vold::VolumeBase
+     */
+    private class Volume {
+        public static final String ID_EMULATED_INTERNAL = "emulated";
+
+        public static final int TYPE_PUBLIC = 0;
+        public static final int TYPE_PRIVATE = 1;
+        public static final int TYPE_EMULATED = 2;
+        public static final int TYPE_ASEC = 3;
+        public static final int TYPE_OBB = 4;
+
+        public static final int STATE_UNMOUNTED = 0;
+        public static final int STATE_MOUNTING = 1;
+        public static final int STATE_MOUNTED = 2;
+        public static final int STATE_FORMATTING = 3;
+        public static final int STATE_UNMOUNTING = 4;
+
+        public static final int FLAG_PRIMARY = 1 << 0;
+        public static final int FLAG_VISIBLE = 1 << 1;
+
+        /** vold state */
+        public final String id;
+        public final int type;
+        public int flags = 0;
+        public int userId = -1;
+        public int state = STATE_UNMOUNTED;
+        public String fsType;
+        public String fsUuid;
+        public String fsLabel;
+        public String path = "/dev/null";
+
+        /** Framework state */
+        public final int mtpIndex;
+
+        public Disk disk;
+
+        public Volume(String id, int type) {
+            this.id = id;
+            this.type = type;
+
+            if (ID_EMULATED_INTERNAL.equals(id)) {
+                mtpIndex = 0;
+            } else {
+                mtpIndex = sNextMtpIndex++;
+            }
+        }
+
+        public boolean isPrimary() {
+            return (flags & FLAG_PRIMARY) != 0;
+        }
+
+        public boolean isVisible() {
+            return (flags & FLAG_VISIBLE) != 0;
+        }
+
+        public boolean isVisibleToUser(int userId) {
+            if (type == TYPE_PUBLIC && this.userId == userId) {
+                return isVisible();
+            } else if (type == TYPE_EMULATED) {
+                return isVisible();
+            } else {
+                return false;
+            }
+        }
+
+        public void mount() throws NativeDaemonConnectorException {
+            mConnector.execute("volume", "mount", id, flags, userId);
+        }
+
+        public void unmount() throws NativeDaemonConnectorException {
+            mConnector.execute("volume", "unmount", id);
+        }
+
+        public void format() throws NativeDaemonConnectorException {
+            mConnector.execute("volume", "format", id);
+        }
+
+        public StorageVolume buildVolumeForUser(int userId) {
+            final File userPath;
+            final boolean removable;
+            final boolean emulated;
+            final boolean allowMassStorage = false;
+            final int mtpStorageId = MtpStorage.getStorageIdForIndex(mtpIndex);
+            final String envState = sStateToEnvironment.get(state);
+
+            int descriptionId = com.android.internal.R.string.unknownName;
+            long mtpReserveSize = 0;
+            long maxFileSize = 0;
+
+            if (type == TYPE_EMULATED) {
+                userPath = new File(path, Integer.toString(userId));
+                emulated = true;
+                mtpReserveSize = StorageManager.from(mContext).getStorageLowBytes(userPath);
+                descriptionId = com.android.internal.R.string.storage_internal;
+
+                if (ID_EMULATED_INTERNAL.equals(id)) {
+                    removable = false;
+                } else {
+                    removable = true;
+                }
+
+            } else if (type == TYPE_PUBLIC) {
+                userPath = new File(path);
+                emulated = false;
+                removable = true;
+
+                if (disk != null) {
+                    if ((disk.flags & Disk.FLAG_SD) != 0) {
+                        descriptionId = com.android.internal.R.string.storage_sd_card;
+                    } else if ((disk.flags & Disk.FLAG_USB) != 0) {
+                        descriptionId = com.android.internal.R.string.storage_usb;
+                    }
+                }
+
+                if ("vfat".equals(fsType)) {
+                    maxFileSize = 4294967295L;
+                }
+
+            } else {
+                throw new IllegalStateException("Unexpected volume type " + type);
+            }
+
+            return new StorageVolume(id, mtpStorageId, userPath, descriptionId, isPrimary(),
+                    removable, emulated, mtpReserveSize, allowMassStorage, maxFileSize,
+                    new UserHandle(userId), fsUuid, fsLabel, envState);
+        }
+
+        public void dump(IndentingPrintWriter pw) {
+            pw.println("Volume:");
+            pw.increaseIndent();
+            pw.printPair("id", id);
+            pw.printPair("type", DebugUtils.valueToString(getClass(), "TYPE_", type));
+            pw.printPair("flags", DebugUtils.flagsToString(getClass(), "FLAG_", flags));
+            pw.printPair("userId", userId);
+            pw.printPair("state", DebugUtils.valueToString(getClass(), "STATE_", state));
+            pw.println();
+            pw.printPair("fsType", fsType);
+            pw.printPair("fsUuid", fsUuid);
+            pw.printPair("fsLabel", fsLabel);
+            pw.println();
+            pw.printPair("path", path);
+            pw.printPair("mtpIndex", mtpIndex);
+            pw.decreaseIndent();
+            pw.println();
+        }
+    }
+
     /** List of crypto types.
       * These must match CRYPT_TYPE_XXX in cryptfs.h AND their
       * corresponding commands in CommandListener.cpp */
@@ -231,33 +459,20 @@
     private final Context mContext;
     private final NativeDaemonConnector mConnector;
 
-    private final Object mVolumesLock = new Object();
-
-    /** When defined, base template for user-specific {@link StorageVolume}. */
-    private StorageVolume mEmulatedTemplate;
-
-    // TODO: separate storage volumes on per-user basis
-
-    @GuardedBy("mVolumesLock")
-    private final ArrayList<StorageVolume> mVolumes = Lists.newArrayList();
-    /** Map from path to {@link StorageVolume} */
-    @GuardedBy("mVolumesLock")
-    private final HashMap<String, StorageVolume> mVolumesByPath = Maps.newHashMap();
-    /** Map from path to state */
-    @GuardedBy("mVolumesLock")
-    private final HashMap<String, String> mVolumeStates = Maps.newHashMap();
-
     private volatile boolean mSystemReady = false;
+    private volatile boolean mDaemonConnected = false;
 
     private PackageManagerService                 mPms;
-    private boolean                               mUmsEnabling;
-    private boolean                               mUmsAvailable = false;
     // Used as a lock for methods that register/unregister listeners.
     final private ArrayList<MountServiceBinderListener> mListeners =
             new ArrayList<MountServiceBinderListener>();
+
     private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
     private final CountDownLatch mAsecsScanned = new CountDownLatch(1);
-    private boolean                               mSendUmsConnectedOnBoot = false;
+
+    private final Object mUnmountLock = new Object();
+    @GuardedBy("mUnmountLock")
+    private CountDownLatch mUnmountSignal;
 
     /**
      * Private hash of currently mounted secure containers.
@@ -366,6 +581,7 @@
     final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
 
     class DefaultContainerConnection implements ServiceConnection {
+        @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             if (DEBUG_OBB)
                 Slog.i(TAG, "onServiceConnected");
@@ -373,6 +589,7 @@
             mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
         }
 
+        @Override
         public void onServiceDisconnected(ComponentName name) {
             if (DEBUG_OBB)
                 Slog.i(TAG, "onServiceDisconnected");
@@ -388,177 +605,27 @@
     private long mLastMaintenance;
 
     // Handler messages
-    private static final int H_UNMOUNT_PM_UPDATE = 1;
-    private static final int H_UNMOUNT_PM_DONE = 2;
-    private static final int H_UNMOUNT_MS = 3;
-    private static final int H_SYSTEM_READY = 4;
-    private static final int H_FSTRIM = 5;
-
-    private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
-    private static final int MAX_UNMOUNT_RETRIES = 4;
-
-    class UnmountCallBack {
-        final String path;
-        final boolean force;
-        final boolean removeEncryption;
-        int retries;
-
-        UnmountCallBack(String path, boolean force, boolean removeEncryption) {
-            retries = 0;
-            this.path = path;
-            this.force = force;
-            this.removeEncryption = removeEncryption;
-        }
-
-        void handleFinished() {
-            if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path);
-            doUnmountVolume(path, true, removeEncryption);
-        }
-    }
-
-    class UmsEnableCallBack extends UnmountCallBack {
-        final String method;
-
-        UmsEnableCallBack(String path, String method, boolean force) {
-            super(path, force, false);
-            this.method = method;
-        }
-
-        @Override
-        void handleFinished() {
-            super.handleFinished();
-            doShareUnshareVolume(path, method, true);
-        }
-    }
-
-    class ShutdownCallBack extends UnmountCallBack {
-        MountShutdownLatch mMountShutdownLatch;
-        ShutdownCallBack(String path, final MountShutdownLatch mountShutdownLatch) {
-            super(path, true, false);
-            mMountShutdownLatch = mountShutdownLatch;
-        }
-
-        @Override
-        void handleFinished() {
-            int ret = doUnmountVolume(path, true, removeEncryption);
-            Slog.i(TAG, "Unmount completed: " + path + ", result code: " + ret);
-            mMountShutdownLatch.countDown();
-        }
-    }
-
-    static class MountShutdownLatch {
-        private IMountShutdownObserver mObserver;
-        private AtomicInteger mCount;
-
-        MountShutdownLatch(final IMountShutdownObserver observer, int count) {
-            mObserver = observer;
-            mCount = new AtomicInteger(count);
-        }
-
-        void countDown() {
-            boolean sendShutdown = false;
-            if (mCount.decrementAndGet() == 0) {
-                sendShutdown = true;
-            }
-            if (sendShutdown && mObserver != null) {
-                try {
-                    mObserver.onShutDownComplete(StorageResultCode.OperationSucceeded);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "RemoteException when shutting down");
-                }
-            }
-        }
-    }
+    private static final int H_SYSTEM_READY = 1;
+    private static final int H_DAEMON_CONNECTED = 2;
+    private static final int H_SHUTDOWN = 3;
+    private static final int H_FSTRIM = 4;
+    private static final int H_VOLUME_MOUNT = 5;
+    private static final int H_VOLUME_BROADCAST = 6;
 
     class MountServiceHandler extends Handler {
-        ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>();
-        boolean mUpdatingStatus = false;
-
-        MountServiceHandler(Looper l) {
-            super(l);
+        public MountServiceHandler(Looper looper) {
+            super(looper);
         }
 
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case H_UNMOUNT_PM_UPDATE: {
-                    if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE");
-                    UnmountCallBack ucb = (UnmountCallBack) msg.obj;
-                    mForceUnmounts.add(ucb);
-                    if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus);
-                    // Register only if needed.
-                    if (!mUpdatingStatus) {
-                        if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
-                        mUpdatingStatus = true;
-                        mPms.updateExternalMediaStatus(false, true);
-                    }
-                    break;
-                }
-                case H_UNMOUNT_PM_DONE: {
-                    if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
-                    if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
-                    mUpdatingStatus = false;
-                    int size = mForceUnmounts.size();
-                    int sizeArr[] = new int[size];
-                    int sizeArrN = 0;
-                    // Kill processes holding references first
-                    ActivityManagerService ams = (ActivityManagerService)
-                    ServiceManager.getService("activity");
-                    for (int i = 0; i < size; i++) {
-                        UnmountCallBack ucb = mForceUnmounts.get(i);
-                        String path = ucb.path;
-                        boolean done = false;
-                        if (!ucb.force) {
-                            done = true;
-                        } else {
-                            int pids[] = getStorageUsers(path);
-                            if (pids == null || pids.length == 0) {
-                                done = true;
-                            } else {
-                                // Eliminate system process here?
-                                ams.killPids(pids, "unmount media", true);
-                                // Confirm if file references have been freed.
-                                pids = getStorageUsers(path);
-                                if (pids == null || pids.length == 0) {
-                                    done = true;
-                                }
-                            }
-                        }
-                        if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
-                            // Retry again
-                            Slog.i(TAG, "Retrying to kill storage users again");
-                            mHandler.sendMessageDelayed(
-                                    mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
-                                            ucb.retries++),
-                                    RETRY_UNMOUNT_DELAY);
-                        } else {
-                            if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
-                                Slog.i(TAG, "Failed to unmount media inspite of " +
-                                        MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
-                            }
-                            sizeArr[sizeArrN++] = i;
-                            mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
-                                    ucb));
-                        }
-                    }
-                    // Remove already processed elements from list.
-                    for (int i = (sizeArrN-1); i >= 0; i--) {
-                        mForceUnmounts.remove(sizeArr[i]);
-                    }
-                    break;
-                }
-                case H_UNMOUNT_MS: {
-                    if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
-                    UnmountCallBack ucb = (UnmountCallBack) msg.obj;
-                    ucb.handleFinished();
-                    break;
-                }
                 case H_SYSTEM_READY: {
-                    try {
-                        handleSystemReady();
-                    } catch (Exception ex) {
-                        Slog.e(TAG, "Boot-time mount exception", ex);
-                    }
+                    handleSystemReady();
+                    break;
+                }
+                case H_DAEMON_CONNECTED: {
+                    handleDaemonConnected();
                     break;
                 }
                 case H_FSTRIM: {
@@ -589,29 +656,68 @@
                     }
                     break;
                 }
+                case H_SHUTDOWN: {
+                    final IMountShutdownObserver obs = (IMountShutdownObserver) msg.obj;
+                    boolean success = false;
+                    try {
+                        success = mConnector.execute("volume", "shutdown").isClassOk();
+                    } catch (NativeDaemonConnectorException ignored) {
+                    }
+                    if (obs != null) {
+                        try {
+                            obs.onShutDownComplete(success ? 0 : -1);
+                        } catch (RemoteException ignored) {
+                        }
+                    }
+                    break;
+                }
+                case H_VOLUME_MOUNT: {
+                    final Volume vol = (Volume) msg.obj;
+                    try {
+                        vol.mount();
+                    } catch (NativeDaemonConnectorException ignored) {
+                    }
+                    break;
+                }
+                case H_VOLUME_BROADCAST: {
+                    final StorageVolume userVol = (StorageVolume) msg.obj;
+                    final String state = userVol.getState();
+                    Slog.d(TAG, "Volume " + userVol.getId() + " broadcasting " + state + " to "
+                            + userVol.getOwner());
+
+                    final String action = sEnvironmentToBroadcast.get(state);
+                    if (action != null) {
+                        final Intent intent = new Intent(action,
+                                Uri.fromFile(userVol.getPathFile()));
+                        intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, userVol);
+                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                        mContext.sendBroadcastAsUser(intent, userVol.getOwner());
+                    }
+                    break;
+                }
             }
         }
-    };
+    }
 
     private final Handler mHandler;
 
     @Override
     public void waitForAsecScan() {
-        waitForLatch(mAsecsScanned);
+        waitForLatch(mAsecsScanned, "mAsecsScanned");
     }
 
     private void waitForReady() {
-        waitForLatch(mConnectedSignal);
+        waitForLatch(mConnectedSignal, "mConnectedSignal");
     }
 
-    private void waitForLatch(CountDownLatch latch) {
+    private void waitForLatch(CountDownLatch latch, String condition) {
         for (;;) {
             try {
                 if (latch.await(5000, TimeUnit.MILLISECONDS)) {
                     return;
                 } else {
                     Slog.w(TAG, "Thread " + Thread.currentThread().getName()
-                            + " still waiting for MountService ready...");
+                            + " still waiting for " + condition + "...");
                 }
             } catch (InterruptedException e) {
                 Slog.w(TAG, "Interrupt while waiting for MountService to be ready.");
@@ -628,103 +734,72 @@
     }
 
     private void handleSystemReady() {
-        // Snapshot current volume states since it's not safe to call into vold
-        // while holding locks.
-        final HashMap<String, String> snapshot;
-        synchronized (mVolumesLock) {
-            snapshot = new HashMap<String, String>(mVolumeStates);
-        }
+        resetIfReadyAndConnected();
 
-        for (Map.Entry<String, String> entry : snapshot.entrySet()) {
-            final String path = entry.getKey();
-            final String state = entry.getValue();
-
-            if (state.equals(Environment.MEDIA_UNMOUNTED)) {
-                int rc = doMountVolume(path);
-                if (rc != StorageResultCode.OperationSucceeded) {
-                    Slog.e(TAG, String.format("Boot-time mount failed (%d)",
-                            rc));
-                }
-            } else if (state.equals(Environment.MEDIA_SHARED)) {
-                /*
-                 * Bootstrap UMS enabled state since vold indicates
-                 * the volume is shared (runtime restart while ums enabled)
-                 */
-                notifyVolumeStateChange(null, path, VolumeState.NoMedia,
-                        VolumeState.Shared);
-            }
-        }
-
-        // Push mounted state for all emulated storage
-        synchronized (mVolumesLock) {
-            for (StorageVolume volume : mVolumes) {
-                if (volume.isEmulated()) {
-                    updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
-                }
-            }
-        }
-
-        /*
-         * If UMS was connected on boot, send the connected event
-         * now that we're up.
-         */
-        if (mSendUmsConnectedOnBoot) {
-            sendUmsIntent(true);
-            mSendUmsConnectedOnBoot = false;
-        }
-
-        /*
-         * Start scheduling nominally-daily fstrim operations
-         */
+        // Start scheduling nominally-daily fstrim operations
         MountServiceIdler.scheduleIdlePass(mContext);
     }
 
-    private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
-            if (userId == -1) return;
-            final UserHandle user = new UserHandle(userId);
+    private void resetIfReadyAndConnected() {
+        Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
+                + ", mDaemonConnected=" + mDaemonConnected);
+        if (mSystemReady && mDaemonConnected) {
+            mDisks.clear();
+            mVolumes.clear();
 
-            final String action = intent.getAction();
-            if (Intent.ACTION_USER_ADDED.equals(action)) {
-                synchronized (mVolumesLock) {
-                    createEmulatedVolumeForUserLocked(user);
-                }
-
-            } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
-                synchronized (mVolumesLock) {
-                    final List<StorageVolume> toRemove = Lists.newArrayList();
-                    for (StorageVolume volume : mVolumes) {
-                        if (user.equals(volume.getOwner())) {
-                            toRemove.add(volume);
-                        }
-                    }
-                    for (StorageVolume volume : toRemove) {
-                        removeVolumeLocked(volume);
-                    }
-                }
+            try {
+                mConnector.execute("volume", "reset");
+            } catch (NativeDaemonConnectorException e) {
+                Slog.w(TAG, "Failed to reset vold", e);
             }
         }
-    };
+    }
 
-    private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            boolean available = (intent.getBooleanExtra(UsbManager.USB_CONNECTED, false) &&
-                    intent.getBooleanExtra(UsbManager.USB_FUNCTION_MASS_STORAGE, false));
-            notifyShareAvailabilityChange(available);
+    private void onStartUser(int userId) {
+        Slog.d(TAG, "onStartUser " + userId);
+
+        // We purposefully block here to make sure that user-specific
+        // staging area is ready so it's ready for zygote-forked apps to
+        // bind mount against.
+        try {
+            mConnector.execute("volume", "start_user", userId);
+        } catch (NativeDaemonConnectorException ignored) {
         }
-    };
+
+        // Record user as started so newly mounted volumes kick off events
+        // correctly, then synthesize events for any already-mounted volumes.
+        synchronized (mVolumes) {
+            for (Volume vol : mVolumes.values()) {
+                if (vol.isVisibleToUser(userId) && vol.state == Volume.STATE_MOUNTED) {
+                    final StorageVolume userVol = vol.buildVolumeForUser(userId);
+                    mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
+                }
+            }
+            mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userId);
+        }
+    }
+
+    private void onCleanupUser(int userId) {
+        Slog.d(TAG, "onCleanupUser " + userId);
+
+        try {
+            mConnector.execute("volume", "cleanup_user", userId);
+        } catch (NativeDaemonConnectorException ignored) {
+        }
+
+        synchronized (mVolumes) {
+            mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userId);
+        }
+    }
 
     private final class MountServiceBinderListener implements IBinder.DeathRecipient {
         final IMountServiceListener mListener;
 
         MountServiceBinderListener(IMountServiceListener listener) {
             mListener = listener;
-
         }
 
+        @Override
         public void binderDied() {
             if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
             synchronized (mListeners) {
@@ -741,7 +816,7 @@
     // Binder entry point for kicking off an immediate fstrim
     @Override
     public void runMaintenance() {
-        validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
         runIdleMaintenance(null);
     }
 
@@ -750,144 +825,35 @@
         return mLastMaintenance;
     }
 
-    private void doShareUnshareVolume(String path, String method, boolean enable) {
-        // TODO: Add support for multiple share methods
-        if (!method.equals("ums")) {
-            throw new IllegalArgumentException(String.format("Method %s not supported", method));
-        }
-
-        try {
-            mConnector.execute("volume", enable ? "share" : "unshare", path, method);
-        } catch (NativeDaemonConnectorException e) {
-            Slog.e(TAG, "Failed to share/unshare", e);
-        }
-    }
-
-    private void updatePublicVolumeState(StorageVolume volume, String state) {
-        final String path = volume.getPath();
-        final String oldState;
-        synchronized (mVolumesLock) {
-            oldState = mVolumeStates.put(path, state);
-            volume.setState(state);
-        }
-
-        if (state.equals(oldState)) {
-            Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s",
-                    state, state, path));
-            return;
-        }
-
-        Slog.d(TAG, "volume state changed for " + path + " (" + oldState + " -> " + state + ")");
-
-        // Tell PackageManager about changes to primary volume state, but only
-        // when not emulated.
-        if (volume.isPrimary() && !volume.isEmulated()) {
-            if (Environment.MEDIA_UNMOUNTED.equals(state)) {
-                mPms.updateExternalMediaStatus(false, false);
-
-                /*
-                 * Some OBBs might have been unmounted when this volume was
-                 * unmounted, so send a message to the handler to let it know to
-                 * remove those from the list of mounted OBBS.
-                 */
-                mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
-                        OBB_FLUSH_MOUNT_STATE, path));
-            } else if (Environment.MEDIA_MOUNTED.equals(state)) {
-                mPms.updateExternalMediaStatus(true, false);
-            }
-        }
-
-        synchronized (mListeners) {
-            for (int i = mListeners.size() -1; i >= 0; i--) {
-                MountServiceBinderListener bl = mListeners.get(i);
-                try {
-                    bl.mListener.onStorageStateChanged(path, oldState, state);
-                } catch (RemoteException rex) {
-                    Slog.e(TAG, "Listener dead");
-                    mListeners.remove(i);
-                } catch (Exception ex) {
-                    Slog.e(TAG, "Listener failed", ex);
-                }
-            }
-        }
-    }
-
     /**
      * Callback from NativeDaemonConnector
      */
+    @Override
     public void onDaemonConnected() {
+        mDaemonConnected = true;
+        mHandler.obtainMessage(H_DAEMON_CONNECTED).sendToTarget();
+    }
+
+    private void handleDaemonConnected() {
+        resetIfReadyAndConnected();
+
         /*
-         * Since we'll be calling back into the NativeDaemonConnector,
-         * we need to do our work in a new thread.
+         * Now that we've done our initialization, release
+         * the hounds!
          */
-        new Thread("MountService#onDaemonConnected") {
-            @Override
-            public void run() {
-                /**
-                 * Determine media state and UMS detection status
-                 */
-                try {
-                    final String[] vols = NativeDaemonEvent.filterMessageList(
-                            mConnector.executeForList("volume", "list", "broadcast"),
-                            VoldResponseCode.VolumeListResult);
-                    for (String volstr : vols) {
-                        String[] tok = volstr.split(" ");
-                        // FMT: <label> <mountpoint> <state>
-                        String path = tok[1];
-                        String state = Environment.MEDIA_REMOVED;
+        mConnectedSignal.countDown();
 
-                        final StorageVolume volume;
-                        synchronized (mVolumesLock) {
-                            volume = mVolumesByPath.get(path);
-                        }
+        // On an encrypted device we can't see system properties yet, so pull
+        // the system locale out of the mount service.
+        if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
+            copyLocaleFromMountService();
+        }
 
-                        int st = Integer.parseInt(tok[2]);
-                        if (st == VolumeState.NoMedia) {
-                            state = Environment.MEDIA_REMOVED;
-                        } else if (st == VolumeState.Idle) {
-                            state = Environment.MEDIA_UNMOUNTED;
-                        } else if (st == VolumeState.Mounted) {
-                            state = Environment.MEDIA_MOUNTED;
-                            Slog.i(TAG, "Media already mounted on daemon connection");
-                        } else if (st == VolumeState.Shared) {
-                            state = Environment.MEDIA_SHARED;
-                            Slog.i(TAG, "Media shared on daemon connection");
-                        } else {
-                            throw new Exception(String.format("Unexpected state %d", st));
-                        }
+        // Let package manager load internal ASECs.
+        mPms.scanAvailableAsecs();
 
-                        if (state != null) {
-                            if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
-                            updatePublicVolumeState(volume, state);
-                        }
-                    }
-                } catch (Exception e) {
-                    Slog.e(TAG, "Error processing initial volume state", e);
-                    final StorageVolume primary = getPrimaryPhysicalVolume();
-                    if (primary != null) {
-                        updatePublicVolumeState(primary, Environment.MEDIA_REMOVED);
-                    }
-                }
-
-                /*
-                 * Now that we've done our initialization, release
-                 * the hounds!
-                 */
-                mConnectedSignal.countDown();
-
-                // On an encrypted device we can't see system properties yet, so pull
-                // the system locale out of the mount service.
-                if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
-                    copyLocaleFromMountService();
-                }
-
-                // Let package manager load internal ASECs.
-                mPms.scanAvailableAsecs();
-
-                // Notify people waiting for ASECs to be scanned that it's done.
-                mAsecsScanned.countDown();
-            }
-        }.start();
+        // Notify people waiting for ASECs to be scanned that it's done.
+        mAsecsScanned.countDown();
     }
 
     private void copyLocaleFromMountService() {
@@ -919,6 +885,7 @@
     /**
      * Callback from NativeDaemonConnector
      */
+    @Override
     public boolean onCheckHoldWakeLock(int code) {
         return false;
     }
@@ -926,347 +893,182 @@
     /**
      * Callback from NativeDaemonConnector
      */
+    @Override
     public boolean onEvent(int code, String raw, String[] cooked) {
-        if (DEBUG_EVENTS) {
-            StringBuilder builder = new StringBuilder();
-            builder.append("onEvent::");
-            builder.append(" raw= " + raw);
-            if (cooked != null) {
-                builder.append(" cooked = " );
-                for (String str : cooked) {
-                    builder.append(" " + str);
-                }
-            }
-            Slog.i(TAG, builder.toString());
+        synchronized (mLock) {
+            return onEventLocked(code, raw, cooked);
         }
-        if (code == VoldResponseCode.VolumeStateChange) {
-            /*
-             * One of the volumes we're managing has changed state.
-             * Format: "NNN Volume <label> <path> state changed
-             * from <old_#> (<old_str>) to <new_#> (<new_str>)"
-             */
-            notifyVolumeStateChange(
-                    cooked[2], cooked[3], Integer.parseInt(cooked[7]),
-                            Integer.parseInt(cooked[10]));
-        } else if (code == VoldResponseCode.VolumeUuidChange) {
-            // Format: nnn <label> <path> <uuid>
-            final String path = cooked[2];
-            final String uuid = (cooked.length > 3) ? cooked[3] : null;
+    }
 
-            final StorageVolume vol = mVolumesByPath.get(path);
-            if (vol != null) {
-                vol.setUuid(uuid);
+    private boolean onEventLocked(int code, String raw, String[] cooked) {
+        switch (code) {
+            case VoldResponseCode.DISK_CREATED: {
+                if (cooked.length != 3) break;
+                final String id = cooked[1];
+                final int flags = Integer.parseInt(cooked[2]);
+                mDisks.put(id, new Disk(id, flags));
+                break;
             }
-
-        } else if (code == VoldResponseCode.VolumeUserLabelChange) {
-            // Format: nnn <label> <path> <label>
-            final String path = cooked[2];
-            final String userLabel = (cooked.length > 3) ? cooked[3] : null;
-
-            final StorageVolume vol = mVolumesByPath.get(path);
-            if (vol != null) {
-                vol.setUserLabel(userLabel);
-            }
-
-        } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
-                   (code == VoldResponseCode.VolumeDiskRemoved) ||
-                   (code == VoldResponseCode.VolumeBadRemoval)) {
-            // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
-            // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
-            // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
-            String action = null;
-            final String label = cooked[2];
-            final String path = cooked[3];
-            int major = -1;
-            int minor = -1;
-
-            try {
-                String devComp = cooked[6].substring(1, cooked[6].length() -1);
-                String[] devTok = devComp.split(":");
-                major = Integer.parseInt(devTok[0]);
-                minor = Integer.parseInt(devTok[1]);
-            } catch (Exception ex) {
-                Slog.e(TAG, "Failed to parse major/minor", ex);
-            }
-
-            final StorageVolume volume;
-            final String state;
-            synchronized (mVolumesLock) {
-                volume = mVolumesByPath.get(path);
-                state = mVolumeStates.get(path);
-            }
-
-            if (code == VoldResponseCode.VolumeDiskInserted) {
-                new Thread("MountService#VolumeDiskInserted") {
-                    @Override
-                    public void run() {
-                        try {
-                            int rc;
-                            if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
-                                Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));
-                            }
-                        } catch (Exception ex) {
-                            Slog.w(TAG, "Failed to mount media on insertion", ex);
-                        }
-                    }
-                }.start();
-            } else if (code == VoldResponseCode.VolumeDiskRemoved) {
-                /*
-                 * This event gets trumped if we're already in BAD_REMOVAL state
-                 */
-                if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
-                    return true;
+            case VoldResponseCode.DISK_SIZE_CHANGED: {
+                if (cooked.length != 3) break;
+                final Disk disk = mDisks.get(cooked[1]);
+                if (disk != null) {
+                    disk.size = Long.parseLong(cooked[2]);
                 }
-                /* Send the media unmounted event first */
-                if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
-                updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
-                sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
+                break;
+            }
+            case VoldResponseCode.DISK_LABEL_CHANGED: {
+                if (cooked.length != 3) break;
+                final Disk disk = mDisks.get(cooked[1]);
+                if (disk != null) {
+                    disk.label = cooked[2];
+                }
+                break;
+            }
+            case VoldResponseCode.DISK_VOLUME_CREATED: {
+                if (cooked.length != 3) break;
+                final Disk disk = mDisks.get(cooked[1]);
+                final Volume vol = mVolumes.get(cooked[2]);
+                if (disk != null && vol != null) {
+                    disk.volumes.add(vol);
+                }
+                break;
+            }
+            case VoldResponseCode.DISK_DESTROYED: {
+                if (cooked.length != 2) break;
+                mDisks.remove(cooked[1]);
+                break;
+            }
 
-                if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
-                updatePublicVolumeState(volume, Environment.MEDIA_REMOVED);
-                action = Intent.ACTION_MEDIA_REMOVED;
-            } else if (code == VoldResponseCode.VolumeBadRemoval) {
-                if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
-                /* Send the media unmounted event first */
-                updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
-                sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
+            case VoldResponseCode.VOLUME_CREATED: {
+                if (cooked.length != 3) break;
+                final String id = cooked[1];
+                final int type = Integer.parseInt(cooked[2]);
+                final Volume vol = new Volume(id, type);
+                mVolumes.put(id, vol);
+                onVolumeCreatedLocked(vol);
+                break;
+            }
+            case VoldResponseCode.VOLUME_STATE_CHANGED: {
+                if (cooked.length != 3) break;
+                final Volume vol = mVolumes.get(cooked[1]);
+                if (vol != null) {
+                    final int oldState = vol.state;
+                    final int newState = Integer.parseInt(cooked[2]);
+                    vol.state = newState;
+                    onVolumeStateChangedLocked(vol, oldState, newState);
+                }
+                break;
+            }
+            case VoldResponseCode.VOLUME_FS_TYPE_CHANGED: {
+                if (cooked.length != 3) break;
+                final Volume vol = mVolumes.get(cooked[1]);
+                if (vol != null) {
+                    vol.fsType = cooked[2];
+                }
+                break;
+            }
+            case VoldResponseCode.VOLUME_FS_UUID_CHANGED: {
+                if (cooked.length != 3) break;
+                final Volume vol = mVolumes.get(cooked[1]);
+                if (vol != null) {
+                    vol.fsUuid = cooked[2];
+                }
+                break;
+            }
+            case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: {
+                if (cooked.length != 3) break;
+                final Volume vol = mVolumes.get(cooked[1]);
+                if (vol != null) {
+                    vol.fsLabel = cooked[2];
+                }
+                break;
+            }
+            case VoldResponseCode.VOLUME_PATH_CHANGED: {
+                if (cooked.length != 3) break;
+                final Volume vol = mVolumes.get(cooked[1]);
+                if (vol != null) {
+                    vol.path = cooked[2];
+                }
+                break;
+            }
+            case VoldResponseCode.VOLUME_DESTROYED: {
+                if (cooked.length != 2) break;
+                mVolumes.remove(cooked[1]);
+                break;
+            }
 
-                if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
-                updatePublicVolumeState(volume, Environment.MEDIA_BAD_REMOVAL);
-                action = Intent.ACTION_MEDIA_BAD_REMOVAL;
-            } else if (code == VoldResponseCode.FstrimCompleted) {
+            case VoldResponseCode.FstrimCompleted: {
                 EventLogTags.writeFstrimFinish(SystemClock.elapsedRealtime());
-            } else {
-                Slog.e(TAG, String.format("Unknown code {%d}", code));
+                break;
             }
-
-            if (action != null) {
-                sendStorageIntent(action, volume, UserHandle.ALL);
+            default: {
+                Slog.d(TAG, "Unhandled vold event " + code);
             }
-        } else {
-            return false;
         }
 
         return true;
     }
 
-    private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
-        final StorageVolume volume;
-        final String state;
-        synchronized (mVolumesLock) {
-            volume = mVolumesByPath.get(path);
-            state = getVolumeState(path);
-        }
+    private void onVolumeCreatedLocked(Volume vol) {
+        final boolean primaryPhysical = SystemProperties.getBoolean(PROP_PRIMARY_PHYSICAL, false);
+        if (vol.type == Volume.TYPE_EMULATED && !primaryPhysical) {
+            vol.flags |= Volume.FLAG_PRIMARY;
+            vol.flags |= Volume.FLAG_VISIBLE;
+            mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
 
-        if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChange::" + state);
-
-        String action = null;
-
-        if (oldState == VolumeState.Shared && newState != oldState) {
-            if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
-            sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED, volume, UserHandle.ALL);
-        }
-
-        if (newState == VolumeState.Init) {
-        } else if (newState == VolumeState.NoMedia) {
-            // NoMedia is handled via Disk Remove events
-        } else if (newState == VolumeState.Idle) {
-            /*
-             * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
-             * if we're in the process of enabling UMS
-             */
-            if (!state.equals(
-                    Environment.MEDIA_BAD_REMOVAL) && !state.equals(
-                            Environment.MEDIA_NOFS) && !state.equals(
-                                    Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
-                if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
-                updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
-                action = Intent.ACTION_MEDIA_UNMOUNTED;
+        } else if (vol.type == Volume.TYPE_PUBLIC) {
+            if (primaryPhysical) {
+                vol.flags |= Volume.FLAG_PRIMARY;
             }
-        } else if (newState == VolumeState.Pending) {
-        } else if (newState == VolumeState.Checking) {
-            if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
-            updatePublicVolumeState(volume, Environment.MEDIA_CHECKING);
-            action = Intent.ACTION_MEDIA_CHECKING;
-        } else if (newState == VolumeState.Mounted) {
-            if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
-            updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
-            action = Intent.ACTION_MEDIA_MOUNTED;
-        } else if (newState == VolumeState.Unmounting) {
-            action = Intent.ACTION_MEDIA_EJECT;
-        } else if (newState == VolumeState.Formatting) {
-        } else if (newState == VolumeState.Shared) {
-            if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
-            /* Send the media unmounted event first */
-            updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
-            sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
+            vol.flags |= Volume.FLAG_VISIBLE;
+            vol.userId = UserHandle.USER_OWNER;
+            mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
 
-            if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
-            updatePublicVolumeState(volume, Environment.MEDIA_SHARED);
-            action = Intent.ACTION_MEDIA_SHARED;
-            if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
-        } else if (newState == VolumeState.SharedMnt) {
-            Slog.e(TAG, "Live shared mounts not supported yet!");
-            return;
         } else {
-            Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
-        }
-
-        if (action != null) {
-            sendStorageIntent(action, volume, UserHandle.ALL);
+            Slog.d(TAG, "Skipping automatic mounting of " + vol);
         }
     }
 
-    private int doMountVolume(String path) {
-        int rc = StorageResultCode.OperationSucceeded;
-
-        final StorageVolume volume;
-        synchronized (mVolumesLock) {
-            volume = mVolumesByPath.get(path);
+    private void onVolumeStateChangedLocked(Volume vol, int oldState, int newState) {
+        // Kick state changed event towards all started users. Any users
+        // started after this point will trigger additional
+        // user-specific broadcasts.
+        for (int userId : mStartedUsers) {
+            if (vol.isVisibleToUser(userId)) {
+                final StorageVolume userVol = vol.buildVolumeForUser(userId);
+                mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
+            }
         }
 
-        if (!volume.isEmulated() && hasUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA)) {
-            Slog.w(TAG, "User has restriction DISALLOW_MOUNT_PHYSICAL_MEDIA; cannot mount volume.");
-            return StorageResultCode.OperationFailedInternalError;
-        }
+        // Tell PackageManager about changes to primary volume state, but only
+        // when not emulated.
+        if (vol.isPrimary() && vol.type == Volume.TYPE_PUBLIC) {
+            if (vol.state == Volume.STATE_MOUNTED) {
+                mPms.updateExternalMediaStatus(true, false);
 
-        if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);
-        try {
-            mConnector.execute("volume", "mount", path);
-        } catch (NativeDaemonConnectorException e) {
-            /*
-             * Mount failed for some reason
-             */
-            String action = null;
-            int code = e.getCode();
-            if (code == VoldResponseCode.OpFailedNoMedia) {
+            } else if (vol.state == Volume.STATE_UNMOUNTING) {
+                mPms.updateExternalMediaStatus(false, false);
+
+                // TODO: this should eventually be handled by new ObbVolume state changes
                 /*
-                 * Attempt to mount but no media inserted
+                 * Some OBBs might have been unmounted when this volume was
+                 * unmounted, so send a message to the handler to let it know to
+                 * remove those from the list of mounted OBBS.
                  */
-                rc = StorageResultCode.OperationFailedNoMedia;
-            } else if (code == VoldResponseCode.OpFailedMediaBlank) {
-                if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");
-                /*
-                 * Media is blank or does not contain a supported filesystem
-                 */
-                updatePublicVolumeState(volume, Environment.MEDIA_NOFS);
-                action = Intent.ACTION_MEDIA_NOFS;
-                rc = StorageResultCode.OperationFailedMediaBlank;
-            } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
-                if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");
-                /*
-                 * Volume consistency check failed
-                 */
-                updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTABLE);
-                action = Intent.ACTION_MEDIA_UNMOUNTABLE;
-                rc = StorageResultCode.OperationFailedMediaCorrupt;
-            } else {
-                rc = StorageResultCode.OperationFailedInternalError;
-            }
-
-            /*
-             * Send broadcast intent (if required for the failure)
-             */
-            if (action != null) {
-                sendStorageIntent(action, volume, UserHandle.ALL);
+                mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
+                        OBB_FLUSH_MOUNT_STATE, vol.path));
             }
         }
 
-        return rc;
-    }
+        final String oldEnvState = sStateToEnvironment.get(oldState);
+        final String newEnvState = sStateToEnvironment.get(newState);
 
-    /*
-     * If force is not set, we do not unmount if there are
-     * processes holding references to the volume about to be unmounted.
-     * If force is set, all the processes holding references need to be
-     * killed via the ActivityManager before actually unmounting the volume.
-     * This might even take a while and might be retried after timed delays
-     * to make sure we dont end up in an instable state and kill some core
-     * processes.
-     * If removeEncryption is set, force is implied, and the system will remove any encryption
-     * mapping set on the volume when unmounting.
-     */
-    private int doUnmountVolume(String path, boolean force, boolean removeEncryption) {
-        if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
-            return VoldResponseCode.OpFailedVolNotMounted;
-        }
-
-        /*
-         * Force a GC to make sure AssetManagers in other threads of the
-         * system_server are cleaned up. We have to do this since AssetManager
-         * instances are kept as a WeakReference and it's possible we have files
-         * open on the external storage.
-         */
-        Runtime.getRuntime().gc();
-
-        // Redundant probably. But no harm in updating state again.
-        mPms.updateExternalMediaStatus(false, false);
-        try {
-            final Command cmd = new Command("volume", "unmount", path);
-            if (removeEncryption) {
-                cmd.appendArg("force_and_revert");
-            } else if (force) {
-                cmd.appendArg("force");
-            }
-            mConnector.execute(cmd);
-            // We unmounted the volume. None of the asec containers are available now.
-            synchronized (mAsecMountSet) {
-                mAsecMountSet.clear();
-            }
-            return StorageResultCode.OperationSucceeded;
-        } catch (NativeDaemonConnectorException e) {
-            // Don't worry about mismatch in PackageManager since the
-            // call back will handle the status changes any way.
-            int code = e.getCode();
-            if (code == VoldResponseCode.OpFailedVolNotMounted) {
-                return StorageResultCode.OperationFailedStorageNotMounted;
-            } else if (code == VoldResponseCode.OpFailedStorageBusy) {
-                return StorageResultCode.OperationFailedStorageBusy;
-            } else {
-                return StorageResultCode.OperationFailedInternalError;
-            }
-        }
-    }
-
-    private int doFormatVolume(String path) {
-        try {
-            mConnector.execute("volume", "format", path);
-            return StorageResultCode.OperationSucceeded;
-        } catch (NativeDaemonConnectorException e) {
-            int code = e.getCode();
-            if (code == VoldResponseCode.OpFailedNoMedia) {
-                return StorageResultCode.OperationFailedNoMedia;
-            } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
-                return StorageResultCode.OperationFailedMediaCorrupt;
-            } else {
-                return StorageResultCode.OperationFailedInternalError;
-            }
-        }
-    }
-
-    private boolean doGetVolumeShared(String path, String method) {
-        final NativeDaemonEvent event;
-        try {
-            event = mConnector.execute("volume", "shared", path, method);
-        } catch (NativeDaemonConnectorException ex) {
-            Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);
-            return false;
-        }
-
-        if (event.getCode() == VoldResponseCode.ShareEnabledResult) {
-            return event.getMessage().endsWith("enabled");
-        } else {
-            return false;
-        }
-    }
-
-    private void notifyShareAvailabilityChange(final boolean avail) {
         synchronized (mListeners) {
-            mUmsAvailable = avail;
             for (int i = mListeners.size() -1; i >= 0; i--) {
                 MountServiceBinderListener bl = mListeners.get(i);
                 try {
-                    bl.mListener.onUsbMassStorageConnectionChanged(avail);
+                    bl.mListener.onStorageStateChanged(vol.path, oldEnvState, newEnvState);
                 } catch (RemoteException rex) {
                     Slog.e(TAG, "Listener dead");
                     mListeners.remove(i);
@@ -1275,221 +1077,19 @@
                 }
             }
         }
-
-        if (mSystemReady == true) {
-            sendUmsIntent(avail);
-        } else {
-            mSendUmsConnectedOnBoot = avail;
-        }
-
-        final StorageVolume primary = getPrimaryPhysicalVolume();
-        if (avail == false && primary != null
-                && Environment.MEDIA_SHARED.equals(getVolumeState(primary.getPath()))) {
-            final String path = primary.getPath();
-            /*
-             * USB mass storage disconnected while enabled
-             */
-            new Thread("MountService#AvailabilityChange") {
-                @Override
-                public void run() {
-                    try {
-                        int rc;
-                        Slog.w(TAG, "Disabling UMS after cable disconnect");
-                        doShareUnshareVolume(path, "ums", false);
-                        if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
-                            Slog.e(TAG, String.format(
-                                    "Failed to remount {%s} on UMS enabled-disconnect (%d)",
-                                            path, rc));
-                        }
-                    } catch (Exception ex) {
-                        Slog.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex);
-                    }
-                }
-            }.start();
-        }
     }
 
-    private void sendStorageIntent(String action, StorageVolume volume, UserHandle user) {
-        final Intent intent = new Intent(action, Uri.parse("file://" + volume.getPath()));
-        intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, volume);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        Slog.d(TAG, "sendStorageIntent " + intent + " to " + user);
-        mContext.sendBroadcastAsUser(intent, user);
+    private void enforcePermission(String perm) {
+        mContext.enforceCallingOrSelfPermission(perm, perm);
     }
 
-    private void sendUmsIntent(boolean c) {
-        mContext.sendBroadcastAsUser(
-                new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)),
-                UserHandle.ALL);
-    }
-
-    private void validatePermission(String perm) {
-        if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException(String.format("Requires %s permission", perm));
-        }
-    }
-
-    private boolean hasUserRestriction(String restriction) {
+    private void enforceUserRestriction(String restriction) {
         UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        return um.hasUserRestriction(restriction, Binder.getCallingUserHandle());
-    }
-
-    private void validateUserRestriction(String restriction) {
-        if (hasUserRestriction(restriction)) {
+        if (um.hasUserRestriction(restriction, Binder.getCallingUserHandle())) {
             throw new SecurityException("User has restriction " + restriction);
         }
     }
 
-    // Storage list XML tags
-    private static final String TAG_STORAGE_LIST = "StorageList";
-    private static final String TAG_STORAGE = "storage";
-
-    private void readStorageListLocked() {
-        mVolumes.clear();
-        mVolumeStates.clear();
-
-        Resources resources = mContext.getResources();
-
-        int id = com.android.internal.R.xml.storage_list;
-        XmlResourceParser parser = resources.getXml(id);
-        AttributeSet attrs = Xml.asAttributeSet(parser);
-
-        try {
-            XmlUtils.beginDocument(parser, TAG_STORAGE_LIST);
-            while (true) {
-                XmlUtils.nextElement(parser);
-
-                String element = parser.getName();
-                if (element == null) break;
-
-                if (TAG_STORAGE.equals(element)) {
-                    TypedArray a = resources.obtainAttributes(attrs,
-                            com.android.internal.R.styleable.Storage);
-
-                    String path = a.getString(
-                            com.android.internal.R.styleable.Storage_mountPoint);
-                    int descriptionId = a.getResourceId(
-                            com.android.internal.R.styleable.Storage_storageDescription, -1);
-                    CharSequence description = a.getText(
-                            com.android.internal.R.styleable.Storage_storageDescription);
-                    boolean primary = a.getBoolean(
-                            com.android.internal.R.styleable.Storage_primary, false);
-                    boolean removable = a.getBoolean(
-                            com.android.internal.R.styleable.Storage_removable, false);
-                    boolean emulated = a.getBoolean(
-                            com.android.internal.R.styleable.Storage_emulated, false);
-                    int mtpReserve = a.getInt(
-                            com.android.internal.R.styleable.Storage_mtpReserve, 0);
-                    boolean allowMassStorage = a.getBoolean(
-                            com.android.internal.R.styleable.Storage_allowMassStorage, false);
-                    // resource parser does not support longs, so XML value is in megabytes
-                    long maxFileSize = a.getInt(
-                            com.android.internal.R.styleable.Storage_maxFileSize, 0) * 1024L * 1024L;
-
-                    Slog.d(TAG, "got storage path: " + path + " description: " + description +
-                            " primary: " + primary + " removable: " + removable +
-                            " emulated: " + emulated +  " mtpReserve: " + mtpReserve +
-                            " allowMassStorage: " + allowMassStorage +
-                            " maxFileSize: " + maxFileSize);
-
-                    if (emulated) {
-                        // For devices with emulated storage, we create separate
-                        // volumes for each known user.
-                        mEmulatedTemplate = new StorageVolume(null, descriptionId, true, false,
-                                true, mtpReserve, false, maxFileSize, null);
-
-                        final UserManagerService userManager = UserManagerService.getInstance();
-                        for (UserInfo user : userManager.getUsers(false)) {
-                            createEmulatedVolumeForUserLocked(user.getUserHandle());
-                        }
-
-                    } else {
-                        if (path == null || description == null) {
-                            Slog.e(TAG, "Missing storage path or description in readStorageList");
-                        } else {
-                            final StorageVolume volume = new StorageVolume(new File(path),
-                                    descriptionId, primary, removable, emulated, mtpReserve,
-                                    allowMassStorage, maxFileSize, null);
-                            addVolumeLocked(volume);
-
-                            // Until we hear otherwise, treat as unmounted
-                            mVolumeStates.put(volume.getPath(), Environment.MEDIA_UNMOUNTED);
-                            volume.setState(Environment.MEDIA_UNMOUNTED);
-                        }
-                    }
-
-                    a.recycle();
-                }
-            }
-        } catch (XmlPullParserException e) {
-            throw new RuntimeException(e);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        } finally {
-            // Compute storage ID for each physical volume; emulated storage is
-            // always 0 when defined.
-            int index = isExternalStorageEmulated() ? 1 : 0;
-            for (StorageVolume volume : mVolumes) {
-                if (!volume.isEmulated()) {
-                    volume.setStorageId(index++);
-                }
-            }
-            parser.close();
-        }
-    }
-
-    /**
-     * Create and add new {@link StorageVolume} for given {@link UserHandle}
-     * using {@link #mEmulatedTemplate} as template.
-     */
-    private void createEmulatedVolumeForUserLocked(UserHandle user) {
-        if (mEmulatedTemplate == null) {
-            throw new IllegalStateException("Missing emulated volume multi-user template");
-        }
-
-        final UserEnvironment userEnv = new UserEnvironment(user.getIdentifier());
-        final File path = userEnv.getExternalStorageDirectory();
-        final StorageVolume volume = StorageVolume.fromTemplate(mEmulatedTemplate, path, user);
-        volume.setStorageId(0);
-        addVolumeLocked(volume);
-
-        if (mSystemReady) {
-            updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
-        } else {
-            // Place stub status for early callers to find
-            mVolumeStates.put(volume.getPath(), Environment.MEDIA_MOUNTED);
-            volume.setState(Environment.MEDIA_MOUNTED);
-        }
-    }
-
-    private void addVolumeLocked(StorageVolume volume) {
-        Slog.d(TAG, "addVolumeLocked() " + volume);
-        mVolumes.add(volume);
-        final StorageVolume existing = mVolumesByPath.put(volume.getPath(), volume);
-        if (existing != null) {
-            throw new IllegalStateException(
-                    "Volume at " + volume.getPath() + " already exists: " + existing);
-        }
-    }
-
-    private void removeVolumeLocked(StorageVolume volume) {
-        Slog.d(TAG, "removeVolumeLocked() " + volume);
-        mVolumes.remove(volume);
-        mVolumesByPath.remove(volume.getPath());
-        mVolumeStates.remove(volume.getPath());
-    }
-
-    private StorageVolume getPrimaryPhysicalVolume() {
-        synchronized (mVolumesLock) {
-            for (StorageVolume volume : mVolumes) {
-                if (volume.isPrimary() && !volume.isEmulated()) {
-                    return volume;
-                }
-            }
-        }
-        return null;
-    }
-
     /**
      * Constructs a new MountService instance
      *
@@ -1500,10 +1100,6 @@
 
         mContext = context;
 
-        synchronized (mVolumesLock) {
-            readStorageListLocked();
-        }
-
         // XXX: This will go away soon in favor of IMountServiceObserver
         mPms = (PackageManagerService) ServiceManager.getService("package");
 
@@ -1511,19 +1107,6 @@
         hthread.start();
         mHandler = new MountServiceHandler(hthread.getLooper());
 
-        // Watch for user changes
-        final IntentFilter userFilter = new IntentFilter();
-        userFilter.addAction(Intent.ACTION_USER_ADDED);
-        userFilter.addAction(Intent.ACTION_USER_REMOVED);
-        mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
-
-        // Watch for USB changes on primary volume
-        final StorageVolume primary = getPrimaryPhysicalVolume();
-        if (primary != null && primary.allowMassStorage()) {
-            mContext.registerReceiver(
-                    mUsbReceiver, new IntentFilter(UsbManager.ACTION_USB_STATE), null, mHandler);
-        }
-
         // Add OBB Action Handler to MountService thread.
         mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
 
@@ -1550,6 +1133,7 @@
          */
         mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
                 null);
+        mConnector.setDebug(true);
 
         Thread thread = new Thread(mConnector, VOLD_TAG);
         thread.start();
@@ -1593,201 +1177,118 @@
         }
     }
 
+    @Override
     public void shutdown(final IMountShutdownObserver observer) {
-        validatePermission(android.Manifest.permission.SHUTDOWN);
+        enforcePermission(android.Manifest.permission.SHUTDOWN);
 
         Slog.i(TAG, "Shutting down");
-        synchronized (mVolumesLock) {
-            // Get all volumes to be unmounted.
-            MountShutdownLatch mountShutdownLatch = new MountShutdownLatch(observer,
-                                                            mVolumeStates.size());
-
-            for (String path : mVolumeStates.keySet()) {
-                String state = mVolumeStates.get(path);
-
-                if (state.equals(Environment.MEDIA_SHARED)) {
-                    /*
-                     * If the media is currently shared, unshare it.
-                     * XXX: This is still dangerous!. We should not
-                     * be rebooting at *all* if UMS is enabled, since
-                     * the UMS host could have dirty FAT cache entries
-                     * yet to flush.
-                     */
-                    setUsbMassStorageEnabled(false);
-                } else if (state.equals(Environment.MEDIA_CHECKING)) {
-                    /*
-                     * If the media is being checked, then we need to wait for
-                     * it to complete before being able to proceed.
-                     */
-                    // XXX: @hackbod - Should we disable the ANR timer here?
-                    int retries = 30;
-                    while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
-                        try {
-                            Thread.sleep(1000);
-                        } catch (InterruptedException iex) {
-                            Slog.e(TAG, "Interrupted while waiting for media", iex);
-                            break;
-                        }
-                        state = Environment.getExternalStorageState();
-                    }
-                    if (retries == 0) {
-                        Slog.e(TAG, "Timed out waiting for media to check");
-                    }
-                }
-
-                if (state.equals(Environment.MEDIA_MOUNTED)) {
-                    // Post a unmount message.
-                    ShutdownCallBack ucb = new ShutdownCallBack(path, mountShutdownLatch);
-                    mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
-                } else if (observer != null) {
-                    /*
-                     * Count down, since nothing will be done. The observer will be
-                     * notified when we are done so shutdown sequence can continue.
-                     */
-                    mountShutdownLatch.countDown();
-                    Slog.i(TAG, "Unmount completed: " + path +
-                        ", result code: " + StorageResultCode.OperationSucceeded);
-                }
-            }
-        }
+        mHandler.obtainMessage(H_SHUTDOWN, observer).sendToTarget();
     }
 
-    private boolean getUmsEnabling() {
-        synchronized (mListeners) {
-            return mUmsEnabling;
-        }
-    }
-
-    private void setUmsEnabling(boolean enable) {
-        synchronized (mListeners) {
-            mUmsEnabling = enable;
-        }
-    }
-
+    @Override
+    @Deprecated
     public boolean isUsbMassStorageConnected() {
-        waitForReady();
-
-        if (getUmsEnabling()) {
-            return true;
-        }
-        synchronized (mListeners) {
-            return mUmsAvailable;
-        }
+        return false;
     }
 
+    @Override
+    @Deprecated
     public void setUsbMassStorageEnabled(boolean enable) {
-        waitForReady();
-        validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
-        validateUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
-
-        final StorageVolume primary = getPrimaryPhysicalVolume();
-        if (primary == null) return;
-
-        // TODO: Add support for multiple share methods
-
-        /*
-         * If the volume is mounted and we're enabling then unmount it
-         */
-        String path = primary.getPath();
-        String vs = getVolumeState(path);
-        String method = "ums";
-        if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
-            // Override for isUsbMassStorageEnabled()
-            setUmsEnabling(enable);
-            UmsEnableCallBack umscb = new UmsEnableCallBack(path, method, true);
-            mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, umscb));
-            // Clear override
-            setUmsEnabling(false);
-        }
-        /*
-         * If we disabled UMS then mount the volume
-         */
-        if (!enable) {
-            doShareUnshareVolume(path, method, enable);
-            if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
-                Slog.e(TAG, "Failed to remount " + path +
-                        " after disabling share method " + method);
-                /*
-                 * Even though the mount failed, the unshare didn't so don't indicate an error.
-                 * The mountVolume() call will have set the storage state and sent the necessary
-                 * broadcasts.
-                 */
-            }
-        }
+        throw new UnsupportedOperationException();
     }
 
+    @Override
+    @Deprecated
     public boolean isUsbMassStorageEnabled() {
-        waitForReady();
-
-        final StorageVolume primary = getPrimaryPhysicalVolume();
-        if (primary != null) {
-            return doGetVolumeShared(primary.getPath(), "ums");
-        } else {
-            return false;
-        }
+        return false;
     }
 
     /**
      * @return state of the volume at the specified mount point
      */
+    @Override
+    @Deprecated
     public String getVolumeState(String mountPoint) {
-        synchronized (mVolumesLock) {
-            String state = mVolumeStates.get(mountPoint);
-            if (state == null) {
-                Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
-                if (SystemProperties.get("vold.encrypt_progress").length() != 0) {
-                    state = Environment.MEDIA_REMOVED;
-                } else {
-                    throw new IllegalArgumentException();
-                }
-            }
+        // TODO: pretend that we're unmounted when encrypting?
+        // SystemProperties.get("vold.encrypt_progress")
 
-            return state;
-        }
+        final Volume vol = findVolumeByLegacyPath(mountPoint);
+        return sStateToEnvironment.get(vol.state);
     }
 
     @Override
     public boolean isExternalStorageEmulated() {
-        return mEmulatedTemplate != null;
+        return Environment.isExternalStorageEmulated();
     }
 
+    @Override
     public int mountVolume(String path) {
-        validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
         waitForReady();
-        return doMountVolume(path);
+
+        final Volume vol = findVolumeByLegacyPath(path);
+        if (vol != null) {
+            if (vol.type == Volume.TYPE_PUBLIC || vol.type == Volume.TYPE_PRIVATE) {
+                enforceUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
+            }
+            try {
+                vol.mount();
+                return 0;
+            } catch (NativeDaemonConnectorException ignored) {
+            }
+        } else {
+            Slog.w(TAG, "Unknown volume for path " + path);
+        }
+        return -1;
     }
 
+    @Override
     public void unmountVolume(String path, boolean force, boolean removeEncryption) {
-        validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
         waitForReady();
 
-        String volState = getVolumeState(path);
-        if (DEBUG_UNMOUNT) {
-            Slog.i(TAG, "Unmounting " + path
-                    + " force = " + force
-                    + " removeEncryption = " + removeEncryption);
+        final Volume vol = findVolumeByLegacyPath(path);
+        if (vol != null) {
+            // TODO: expand PMS to know about multiple volumes
+            if (vol.isPrimary()) {
+                synchronized (mUnmountLock) {
+                    mUnmountSignal = new CountDownLatch(1);
+                    mPms.updateExternalMediaStatus(false, true);
+                    waitForLatch(mUnmountSignal, "mUnmountSignal");
+                    mUnmountSignal = null;
+                }
+            }
+
+            try {
+                vol.unmount();
+            } catch (NativeDaemonConnectorException ignored) {
+            }
+        } else {
+            Slog.w(TAG, "Unknown volume for path " + path);
         }
-        if (Environment.MEDIA_UNMOUNTED.equals(volState) ||
-                Environment.MEDIA_REMOVED.equals(volState) ||
-                Environment.MEDIA_SHARED.equals(volState) ||
-                Environment.MEDIA_UNMOUNTABLE.equals(volState)) {
-            // Media already unmounted or cannot be unmounted.
-            // TODO return valid return code when adding observer call back.
-            return;
-        }
-        UnmountCallBack ucb = new UnmountCallBack(path, force, removeEncryption);
-        mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
     }
 
+    @Override
     public int formatVolume(String path) {
-        validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
+        enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
         waitForReady();
 
-        return doFormatVolume(path);
+        final Volume vol = findVolumeByLegacyPath(path);
+        if (vol != null) {
+            try {
+                vol.format();
+                return 0;
+            } catch (NativeDaemonConnectorException ignored) {
+            }
+        } else {
+            Slog.w(TAG, "Unknown volume for path " + path);
+        }
+        return -1;
     }
 
+    @Override
     public int[] getStorageUsers(String path) {
-        validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
         waitForReady();
         try {
             final String[] r = NativeDaemonEvent.filterMessageList(
@@ -1813,22 +1314,20 @@
     }
 
     private void warnOnNotMounted() {
-        final StorageVolume primary = getPrimaryPhysicalVolume();
-        if (primary != null) {
-            boolean mounted = false;
-            try {
-                mounted = Environment.MEDIA_MOUNTED.equals(getVolumeState(primary.getPath()));
-            } catch (IllegalArgumentException e) {
-            }
-
-            if (!mounted) {
-                Slog.w(TAG, "getSecureContainerList() called when storage not mounted");
+        synchronized (mLock) {
+            for (Volume vol : mVolumes.values()) {
+                if (vol.isPrimary() && vol.state == Volume.STATE_MOUNTED) {
+                    // Cool beans, we have a mounted primary volume
+                    return;
+                }
             }
         }
+
+        Slog.w(TAG, "No primary storage mounted!");
     }
 
     public String[] getSecureContainerList() {
-        validatePermission(android.Manifest.permission.ASEC_ACCESS);
+        enforcePermission(android.Manifest.permission.ASEC_ACCESS);
         waitForReady();
         warnOnNotMounted();
 
@@ -1842,7 +1341,7 @@
 
     public int createSecureContainer(String id, int sizeMb, String fstype, String key,
             int ownerUid, boolean external) {
-        validatePermission(android.Manifest.permission.ASEC_CREATE);
+        enforcePermission(android.Manifest.permission.ASEC_CREATE);
         waitForReady();
         warnOnNotMounted();
 
@@ -1864,7 +1363,7 @@
 
     @Override
     public int resizeSecureContainer(String id, int sizeMb, String key) {
-        validatePermission(android.Manifest.permission.ASEC_CREATE);
+        enforcePermission(android.Manifest.permission.ASEC_CREATE);
         waitForReady();
         warnOnNotMounted();
 
@@ -1878,7 +1377,7 @@
     }
 
     public int finalizeSecureContainer(String id) {
-        validatePermission(android.Manifest.permission.ASEC_CREATE);
+        enforcePermission(android.Manifest.permission.ASEC_CREATE);
         warnOnNotMounted();
 
         int rc = StorageResultCode.OperationSucceeded;
@@ -1895,7 +1394,7 @@
     }
 
     public int fixPermissionsSecureContainer(String id, int gid, String filename) {
-        validatePermission(android.Manifest.permission.ASEC_CREATE);
+        enforcePermission(android.Manifest.permission.ASEC_CREATE);
         warnOnNotMounted();
 
         int rc = StorageResultCode.OperationSucceeded;
@@ -1912,7 +1411,7 @@
     }
 
     public int destroySecureContainer(String id, boolean force) {
-        validatePermission(android.Manifest.permission.ASEC_DESTROY);
+        enforcePermission(android.Manifest.permission.ASEC_DESTROY);
         waitForReady();
         warnOnNotMounted();
 
@@ -1952,7 +1451,7 @@
     }
 
     public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
-        validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
+        enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
         waitForReady();
         warnOnNotMounted();
 
@@ -1982,7 +1481,7 @@
     }
 
     public int unmountSecureContainer(String id, boolean force) {
-        validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
+        enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
         waitForReady();
         warnOnNotMounted();
 
@@ -2025,7 +1524,7 @@
     }
 
     public boolean isSecureContainerMounted(String id) {
-        validatePermission(android.Manifest.permission.ASEC_ACCESS);
+        enforcePermission(android.Manifest.permission.ASEC_ACCESS);
         waitForReady();
         warnOnNotMounted();
 
@@ -2035,7 +1534,7 @@
     }
 
     public int renameSecureContainer(String oldId, String newId) {
-        validatePermission(android.Manifest.permission.ASEC_RENAME);
+        enforcePermission(android.Manifest.permission.ASEC_RENAME);
         waitForReady();
         warnOnNotMounted();
 
@@ -2060,7 +1559,7 @@
     }
 
     public String getSecureContainerPath(String id) {
-        validatePermission(android.Manifest.permission.ASEC_ACCESS);
+        enforcePermission(android.Manifest.permission.ASEC_ACCESS);
         waitForReady();
         warnOnNotMounted();
 
@@ -2081,7 +1580,7 @@
     }
 
     public String getSecureContainerFilesystemPath(String id) {
-        validatePermission(android.Manifest.permission.ASEC_ACCESS);
+        enforcePermission(android.Manifest.permission.ASEC_ACCESS);
         waitForReady();
         warnOnNotMounted();
 
@@ -2101,8 +1600,13 @@
         }
     }
 
+    @Override
     public void finishMediaUpdate() {
-        mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
+        if (mUnmountSignal != null) {
+            mUnmountSignal.countDown();
+        } else {
+            Slog.w(TAG, "Odd, nobody asked to unmount?");
+        }
     }
 
     private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
@@ -2477,109 +1981,84 @@
                 Context.APP_OPS_SERVICE);
         appOps.checkPackage(Binder.getCallingUid(), callingPkg);
 
+        File appFile = null;
         try {
-            appPath = new File(appPath).getCanonicalPath();
+            appFile = new File(appPath).getCanonicalFile();
         } catch (IOException e) {
             Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
             return -1;
         }
 
-        if (!appPath.endsWith("/")) {
-            appPath = appPath + "/";
-        }
-
         // Try translating the app path into a vold path, but require that it
         // belong to the calling package.
-        String voldPath = maybeTranslatePathForVold(appPath,
-                userEnv.buildExternalStorageAppDataDirs(callingPkg),
-                userEnv.buildExternalStorageAppDataDirsForVold(callingPkg));
-        if (voldPath != null) {
+        if (FileUtils.contains(userEnv.buildExternalStorageAppDataDirs(callingPkg), appFile) ||
+                FileUtils.contains(userEnv.buildExternalStorageAppObbDirs(callingPkg), appFile) ||
+                FileUtils.contains(userEnv.buildExternalStorageAppMediaDirs(callingPkg), appFile)) {
+            appPath = appFile.getAbsolutePath();
+            if (!appPath.endsWith("/")) {
+                appPath = appPath + "/";
+            }
+
             try {
-                mConnector.execute("volume", "mkdirs", voldPath);
+                mConnector.execute("volume", "mkdirs", appPath);
                 return 0;
             } catch (NativeDaemonConnectorException e) {
                 return e.getCode();
             }
         }
 
-        voldPath = maybeTranslatePathForVold(appPath,
-                userEnv.buildExternalStorageAppObbDirs(callingPkg),
-                userEnv.buildExternalStorageAppObbDirsForVold(callingPkg));
-        if (voldPath != null) {
-            try {
-                mConnector.execute("volume", "mkdirs", voldPath);
-                return 0;
-            } catch (NativeDaemonConnectorException e) {
-                return e.getCode();
-            }
-        }
-
-        voldPath = maybeTranslatePathForVold(appPath,
-                userEnv.buildExternalStorageAppMediaDirs(callingPkg),
-                userEnv.buildExternalStorageAppMediaDirsForVold(callingPkg));
-        if (voldPath != null) {
-            try {
-                mConnector.execute("volume", "mkdirs", voldPath);
-                return 0;
-            } catch (NativeDaemonConnectorException e) {
-                return e.getCode();
-            }
-        }
-
-        throw new SecurityException("Invalid mkdirs path: " + appPath);
-    }
-
-    /**
-     * Translate the given path from an app-visible path to a vold-visible path,
-     * but only if it's under the given whitelisted paths.
-     *
-     * @param path a canonicalized app-visible path.
-     * @param appPaths list of app-visible paths that are allowed.
-     * @param voldPaths list of vold-visible paths directly corresponding to the
-     *            allowed app-visible paths argument.
-     * @return a vold-visible path representing the original path, or
-     *         {@code null} if the given path didn't have an app-to-vold
-     *         mapping.
-     */
-    @VisibleForTesting
-    public static String maybeTranslatePathForVold(
-            String path, File[] appPaths, File[] voldPaths) {
-        if (appPaths.length != voldPaths.length) {
-            throw new IllegalStateException("Paths must be 1:1 mapping");
-        }
-
-        for (int i = 0; i < appPaths.length; i++) {
-            final String appPath = appPaths[i].getAbsolutePath() + "/";
-            if (path.startsWith(appPath)) {
-                path = new File(voldPaths[i], path.substring(appPath.length()))
-                        .getAbsolutePath();
-                if (!path.endsWith("/")) {
-                    path = path + "/";
-                }
-                return path;
-            }
-        }
-        return null;
+        throw new SecurityException("Invalid mkdirs path: " + appFile);
     }
 
     @Override
-    public StorageVolume[] getVolumeList() {
-        final int callingUserId = UserHandle.getCallingUserId();
-        final boolean accessAll = (mContext.checkPermission(
-                android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE,
-                Binder.getCallingPid(), Binder.getCallingUid()) == PERMISSION_GRANTED);
+    public StorageVolume[] getVolumeList(int userId) {
+        if (UserHandle.getCallingUserId() != userId) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE, "getVolumeList");
+        }
 
-        synchronized (mVolumesLock) {
-            final ArrayList<StorageVolume> filtered = Lists.newArrayList();
-            for (StorageVolume volume : mVolumes) {
-                final UserHandle owner = volume.getOwner();
-                final boolean ownerMatch = owner == null || owner.getIdentifier() == callingUserId;
-                if (accessAll || ownerMatch) {
-                    filtered.add(volume);
+        final ArrayList<StorageVolume> res = Lists.newArrayList();
+        boolean foundPrimary = false;
+        synchronized (mLock) {
+            for (Volume vol : mVolumes.values()) {
+                if (vol.isVisibleToUser(userId)) {
+                    final StorageVolume userVol = vol.buildVolumeForUser(userId);
+                    if (vol.isPrimary()) {
+                        res.add(0, userVol);
+                        foundPrimary = true;
+                    } else {
+                        res.add(userVol);
+                    }
                 }
             }
-            return filtered.toArray(new StorageVolume[filtered.size()]);
         }
+
+        if (!foundPrimary) {
+            Slog.w(TAG, "No primary storage defined yet; hacking together a stub");
+
+            final boolean primaryPhysical = SystemProperties.getBoolean(
+                    PROP_PRIMARY_PHYSICAL, false);
+
+            final String id = "stub_primary";
+            final File path = Environment.getLegacyExternalStorageDirectory();
+            final int descriptionId = android.R.string.unknownName;
+            final boolean primary = true;
+            final boolean removable = primaryPhysical;
+            final boolean emulated = !primaryPhysical;
+            final long mtpReserveSize = 0L;
+            final boolean allowMassStorage = false;
+            final long maxFileSize = 0L;
+            final UserHandle owner = new UserHandle(userId);
+            final String uuid = null;
+            final String userLabel = null;
+            final String state = Environment.MEDIA_REMOVED;
+
+            res.add(0, new StorageVolume(id, MtpStorage.getStorageIdForIndex(0), path,
+                    descriptionId, primary, removable, emulated, mtpReserveSize,
+                    allowMassStorage, maxFileSize, owner, uuid, userLabel, state));
+        }
+
+        return res.toArray(new StorageVolume[res.size()]);
     }
 
     private void addObbStateLocked(ObbState obbState) throws RemoteException {
@@ -3073,21 +2552,13 @@
         if (path.startsWith(obbPath)) {
             path = path.substring(obbPath.length() + 1);
 
-            if (forVold) {
-                return new File(Environment.getEmulatedStorageObbSource(), path).getAbsolutePath();
-            } else {
-                final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
-                return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
-                        .getAbsolutePath();
-            }
+            final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
+            return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
+                    .getAbsolutePath();
         }
 
         // Handle normal external storage paths
-        if (forVold) {
-            return new File(Environment.getEmulatedStorageSource(userId), path).getAbsolutePath();
-        } else {
-            return new File(userEnv.getExternalDirsForApp()[0], path).getAbsolutePath();
-        }
+        return new File(userEnv.getExternalStorageDirectory(), path).getAbsolutePath();
     }
 
     @Override
@@ -3126,15 +2597,20 @@
             pw.decreaseIndent();
         }
 
-        synchronized (mVolumesLock) {
+        synchronized (mLock) {
             pw.println();
-            pw.println("mVolumes:");
+            pw.println("Disks:");
             pw.increaseIndent();
-            for (StorageVolume volume : mVolumes) {
-                pw.println(volume);
-                pw.increaseIndent();
-                pw.println("Current state: " + mVolumeStates.get(volume.getPath()));
-                pw.decreaseIndent();
+            for (Disk disk : mDisks.values()) {
+                disk.dump(pw);
+            }
+            pw.decreaseIndent();
+
+            pw.println();
+            pw.println("Volumes:");
+            pw.increaseIndent();
+            for (Volume vol : mVolumes.values()) {
+                vol.dump(pw);
             }
             pw.decreaseIndent();
         }
@@ -3153,6 +2629,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public void monitor() {
         if (mConnector != null) {
             mConnector.monitor();
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java
index d2dfc7b..78c7f38 100644
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ b/services/core/java/com/android/server/NativeDaemonConnector.java
@@ -48,8 +48,6 @@
  * {@code libsysutils} FrameworkListener protocol.
  */
 final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
-    private static final boolean LOGD = false;
-
     private final static boolean VDBG = false;
 
     private final String TAG;
@@ -58,6 +56,8 @@
     private OutputStream mOutputStream;
     private LocalLog mLocalLog;
 
+    private volatile boolean mDebug = false;
+
     private final ResponseQueue mResponseQueue;
 
     private final PowerManager.WakeLock mWakeLock;
@@ -99,6 +99,14 @@
         mLocalLog = new LocalLog(maxLogSize);
     }
 
+    /**
+     * Enable Set debugging mode, which causes messages to also be written to both
+     * {@link Slog} in addition to internal log.
+     */
+    public void setDebug(boolean debug) {
+        mDebug = debug;
+    }
+
     @Override
     public void run() {
         mCallbackHandler = new Handler(mLooper, this);
@@ -513,7 +521,7 @@
     }
 
     private void log(String logstring) {
-        if (LOGD) Slog.d(TAG, logstring);
+        if (mDebug) Slog.d(TAG, logstring);
         mLocalLog.log(logstring);
     }
 
diff --git a/services/core/java/com/android/server/PersistentDataBlockService.java b/services/core/java/com/android/server/PersistentDataBlockService.java
index 97d16c0..b36f515 100644
--- a/services/core/java/com/android/server/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/PersistentDataBlockService.java
@@ -70,6 +70,7 @@
     // Limit to 100k as blocks larger than this might cause strain on Binder.
     private static final int MAX_DATA_BLOCK_SIZE = 1024 * 100;
     public static final int DIGEST_SIZE_BYTES = 32;
+    private static final String OEM_UNLOCK_PROP = "sys.oem_unlock_allowed";
 
     private final Context mContext;
     private final String mDataBlockFile;
@@ -108,11 +109,14 @@
     }
 
     private void formatIfOemUnlockEnabled() {
-        if (doGetOemUnlockEnabled()) {
+        boolean enabled = doGetOemUnlockEnabled();
+        if (enabled) {
             synchronized (mLock) {
                 formatPartitionLocked(true);
             }
         }
+
+        SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
     }
 
     private void enforceOemUnlockPermission() {
@@ -132,7 +136,6 @@
             throw new SecurityException("Only the Owner is allowed to change OEM unlock state");
         }
     }
-
     private int getTotalDataSizeLocked(DataInputStream inputStream) throws IOException {
         // skip over checksum
         inputStream.skipBytes(DIGEST_SIZE_BYTES);
@@ -290,6 +293,7 @@
             Slog.e(TAG, "unable to access persistent partition", e);
             return;
         } finally {
+            SystemProperties.set(OEM_UNLOCK_PROP, enabled ? "1" : "0");
             IoUtils.closeQuietly(outputStream);
         }
     }
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 6ad128c..4c9d7d3 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -71,9 +71,11 @@
     public static final class PermissionEntry {
         public final String name;
         public int[] gids;
+        public boolean perUser;
 
-        PermissionEntry(String _name) {
-            name = _name;
+        PermissionEntry(String name, boolean perUser) {
+            this.name = name;
+            this.perUser = perUser;
         }
     }
 
@@ -363,14 +365,14 @@
 
     void readPermission(XmlPullParser parser, String name)
             throws IOException, XmlPullParserException {
-
-        name = name.intern();
-
-        PermissionEntry perm = mPermissions.get(name);
-        if (perm == null) {
-            perm = new PermissionEntry(name);
-            mPermissions.put(name, perm);
+        if (mPermissions.containsKey(name)) {
+            throw new IllegalStateException("Duplicate permission definition for " + name);
         }
+
+        final boolean perUser = XmlUtils.readBooleanAttribute(parser, "perUser", false);
+        final PermissionEntry perm = new PermissionEntry(name, perUser);
+        mPermissions.put(name, perm);
+
         int outerDepth = parser.getDepth();
         int type;
         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 87dc420..64f3070 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -157,7 +157,6 @@
     @Override
     public void onStart() {
         final Context context = getContext();
-        mTwilightManager = getLocalService(TwilightManager.class);
 
         final PowerManager powerManager =
                 (PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -188,7 +187,11 @@
         mNightMode = Settings.Secure.getInt(context.getContentResolver(),
                 Settings.Secure.UI_NIGHT_MODE, defaultNightMode);
 
-        mTwilightManager.registerListener(mTwilightListener, mHandler);
+        // Update the initial, static configurations.
+        synchronized (this) {
+            updateConfigurationLocked();
+            sendConfigurationLocked();
+        }
 
         publishBinderService(Context.UI_MODE_SERVICE, mService);
     }
@@ -297,8 +300,11 @@
                     pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode));
             pw.print("  mHoldingConfiguration="); pw.print(mHoldingConfiguration);
                     pw.print(" mSystemReady="); pw.println(mSystemReady);
-            pw.print("  mTwilightService.getCurrentState()=");
-                    pw.println(mTwilightManager.getCurrentState());
+            if (mTwilightManager != null) {
+                // We may not have a TwilightManager.
+                pw.print("  mTwilightService.getCurrentState()=");
+                pw.println(mTwilightManager.getCurrentState());
+            }
         }
     }
 
@@ -306,6 +312,10 @@
     public void onBootPhase(int phase) {
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
             synchronized (mLock) {
+                mTwilightManager = getLocalService(TwilightManager.class);
+                if (mTwilightManager != null) {
+                    mTwilightManager.registerListener(mTwilightListener, mHandler);
+                }
                 mSystemReady = true;
                 mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR;
                 updateComputedNightModeLocked();
@@ -623,9 +633,11 @@
     }
 
     private void updateComputedNightModeLocked() {
-        TwilightState state = mTwilightManager.getCurrentState();
-        if (state != null) {
-            mComputedNightMode = state.isNight();
+        if (mTwilightManager != null) {
+            TwilightState state = mTwilightManager.getCurrentState();
+            if (state != null) {
+                mComputedNightMode = state.isNight();
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index a64e6c3..1b32f57 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -109,7 +109,7 @@
 
     private static final int TIMEOUT_DELAY_MS = 1000 * 60;
     private static final String DATABASE_NAME = "accounts.db";
-    private static final int DATABASE_VERSION = 6;
+    private static final int DATABASE_VERSION = 7;
 
     private final Context mContext;
 
@@ -131,6 +131,8 @@
     private static final String ACCOUNTS_TYPE_COUNT = "count(type)";
     private static final String ACCOUNTS_PASSWORD = "password";
     private static final String ACCOUNTS_PREVIOUS_NAME = "previous_name";
+    private static final String ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS =
+            "last_password_entry_time_millis_epoch";
 
     private static final String TABLE_AUTHTOKENS = "authtokens";
     private static final String AUTHTOKENS_ID = "_id";
@@ -697,7 +699,8 @@
         long identityToken = clearCallingIdentity();
         try {
             new Session(fromAccounts, response, account.type, false,
-                    false /* stripAuthTokenFromResult */) {
+                    false /* stripAuthTokenFromResult */, account.name,
+                    false /* authDetailsRequired */) {
                 @Override
                 protected String toDebugString(long now) {
                     return super.toDebugString(now) + ", getAccountCredentialsForClone"
@@ -725,12 +728,43 @@
         }
     }
 
+    @Override
+    public boolean accountAuthenticated(final Account account) {
+        if (account == null) {
+            throw new IllegalArgumentException("account is null");
+        }
+        checkAuthenticateAccountsPermission(account);
+
+        final UserAccounts accounts = getUserAccountsForCaller();
+        int userId = Binder.getCallingUserHandle().getIdentifier();
+        if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) {
+            return false;
+        }
+        synchronized (accounts.cacheLock) {
+            final ContentValues values = new ContentValues();
+            values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
+            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
+            int i = db.update(
+                    TABLE_ACCOUNTS,
+                    values,
+                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
+                    new String[] {
+                            account.name, account.type
+                    });
+            if (i > 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void completeCloningAccount(IAccountManagerResponse response,
             final Bundle accountCredentials, final Account account, final UserAccounts targetUser) {
         long id = clearCallingIdentity();
         try {
             new Session(targetUser, response, account.type, false,
-                    false /* stripAuthTokenFromResult */) {
+                    false /* stripAuthTokenFromResult */, account.name,
+                    false /* authDetailsRequired */) {
                 @Override
                 protected String toDebugString(long now) {
                     return super.toDebugString(now) + ", getAccountCredentialsForClone"
@@ -795,6 +829,7 @@
                 values.put(ACCOUNTS_NAME, account.name);
                 values.put(ACCOUNTS_TYPE, account.type);
                 values.put(ACCOUNTS_PASSWORD, password);
+                values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
                 long accountId = db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
                 if (accountId < 0) {
                     Log.w(TAG, "insertAccountIntoDatabase: " + account
@@ -885,7 +920,8 @@
         public TestFeaturesSession(UserAccounts accounts, IAccountManagerResponse response,
                 Account account, String[] features) {
             super(accounts, response, account.type, false /* expectActivityLaunch */,
-                    true /* stripAuthTokenFromResult */);
+                    true /* stripAuthTokenFromResult */, account.name,
+                    false /* authDetailsRequired */);
             mFeatures = features;
             mAccount = account;
         }
@@ -1184,7 +1220,8 @@
         public RemoveAccountSession(UserAccounts accounts, IAccountManagerResponse response,
                 Account account, boolean expectActivityLaunch) {
             super(accounts, response, account.type, expectActivityLaunch,
-                    true /* stripAuthTokenFromResult */);
+                    true /* stripAuthTokenFromResult */, account.name,
+                    false /* authDetailsRequired */);
             mAccount = account;
         }
 
@@ -1419,6 +1456,13 @@
             try {
                 final ContentValues values = new ContentValues();
                 values.put(ACCOUNTS_PASSWORD, password);
+                long time = 0;
+                // Only set current time, if it is a valid password. For clear password case, it
+                // should not be set.
+                if (password != null) {
+                    time = System.currentTimeMillis();
+                }
+                values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, time);
                 final long accountId = getAccountIdLocked(db, account);
                 if (accountId >= 0) {
                     final String[] argsAccountId = {String.valueOf(accountId)};
@@ -1547,8 +1591,9 @@
         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
         long identityToken = clearCallingIdentity();
         try {
-            new Session(accounts, response, accountType, false,
-                    false /* stripAuthTokenFromResult */) {
+            new Session(accounts, response, accountType, false /* expectActivityLaunch */,
+                    false /* stripAuthTokenFromResult */,  null /* accountName */,
+                    false /* authDetailsRequired */) {
                 @Override
                 protected String toDebugString(long now) {
                     return super.toDebugString(now) + ", getAuthTokenLabel"
@@ -1648,7 +1693,8 @@
             }
 
             new Session(accounts, response, account.type, expectActivityLaunch,
-                    false /* stripAuthTokenFromResult */) {
+                    false /* stripAuthTokenFromResult */, account.name,
+                    false /* authDetailsRequired */) {
                 @Override
                 protected String toDebugString(long now) {
                     if (loginOptions != null) loginOptions.keySet();
@@ -1842,7 +1888,8 @@
         long identityToken = clearCallingIdentity();
         try {
             new Session(accounts, response, accountType, expectActivityLaunch,
-                    true /* stripAuthTokenFromResult */) {
+                    true /* stripAuthTokenFromResult */, null /* accountName */,
+                    false /* authDetailsRequired */) {
                 @Override
                 public void run() throws RemoteException {
                     mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
@@ -1917,7 +1964,8 @@
         long identityToken = clearCallingIdentity();
         try {
             new Session(accounts, response, accountType, expectActivityLaunch,
-                    true /* stripAuthTokenFromResult */) {
+                    true /* stripAuthTokenFromResult */, null /* accountName */,
+                    false /* authDetailsRequired */) {
                 @Override
                 public void run() throws RemoteException {
                     mAuthenticator.addAccount(this, mAccountType, authTokenType, requiredFeatures,
@@ -1973,7 +2021,8 @@
         long identityToken = clearCallingIdentity();
         try {
             new Session(accounts, response, account.type, expectActivityLaunch,
-                    true /* stripAuthTokenFromResult */) {
+                    true /* stripAuthTokenFromResult */, account.name,
+                    true /* authDetailsRequired */) {
                 @Override
                 public void run() throws RemoteException {
                     mAuthenticator.confirmCredentials(this, account, options);
@@ -2009,7 +2058,8 @@
         long identityToken = clearCallingIdentity();
         try {
             new Session(accounts, response, account.type, expectActivityLaunch,
-                    true /* stripAuthTokenFromResult */) {
+                    true /* stripAuthTokenFromResult */, account.name,
+                    false /* authDetailsRequired */) {
                 @Override
                 public void run() throws RemoteException {
                     mAuthenticator.updateCredentials(this, account, authTokenType, loginOptions);
@@ -2045,7 +2095,8 @@
         long identityToken = clearCallingIdentity();
         try {
             new Session(accounts, response, accountType, expectActivityLaunch,
-                    true /* stripAuthTokenFromResult */) {
+                    true /* stripAuthTokenFromResult */, null /* accountName */,
+                    false /* authDetailsRequired */) {
                 @Override
                 public void run() throws RemoteException {
                     mAuthenticator.editProperties(this, mAccountType);
@@ -2071,7 +2122,8 @@
         public GetAccountsByTypeAndFeatureSession(UserAccounts accounts,
                 IAccountManagerResponse response, String type, String[] features, int callingUid) {
             super(accounts, response, type, false /* expectActivityLaunch */,
-                    true /* stripAuthTokenFromResult */);
+                    true /* stripAuthTokenFromResult */, null /* accountName */,
+                    false /* authDetailsRequired */);
             mCallingUid = callingUid;
             mFeatures = features;
         }
@@ -2437,6 +2489,9 @@
         final String mAccountType;
         final boolean mExpectActivityLaunch;
         final long mCreationTime;
+        final String mAccountName;
+        // Indicates if we need to add auth details(like last credential time)
+        final boolean mAuthDetailsRequired;
 
         public int mNumResults = 0;
         private int mNumRequestContinued = 0;
@@ -2448,7 +2503,8 @@
         protected final UserAccounts mAccounts;
 
         public Session(UserAccounts accounts, IAccountManagerResponse response, String accountType,
-                boolean expectActivityLaunch, boolean stripAuthTokenFromResult) {
+                boolean expectActivityLaunch, boolean stripAuthTokenFromResult, String accountName,
+                boolean authDetailsRequired) {
             super();
             //if (response == null) throw new IllegalArgumentException("response is null");
             if (accountType == null) throw new IllegalArgumentException("accountType is null");
@@ -2458,6 +2514,9 @@
             mAccountType = accountType;
             mExpectActivityLaunch = expectActivityLaunch;
             mCreationTime = SystemClock.elapsedRealtime();
+            mAccountName = accountName;
+            mAuthDetailsRequired = authDetailsRequired;
+
             synchronized (mSessions) {
                 mSessions.put(toString(), this);
             }
@@ -2592,6 +2651,16 @@
         public void onResult(Bundle result) {
             mNumResults++;
             Intent intent = null;
+            if (result != null && mAuthDetailsRequired) {
+                long lastAuthenticatedTime = DatabaseUtils.longForQuery(
+                        mAccounts.openHelper.getReadableDatabase(),
+                        "select " + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " from " +
+                                TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
+                                + ACCOUNTS_TYPE + "=?",
+                        new String[]{mAccountName, mAccountType});
+                result.putLong(AccountManager.KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH,
+                        lastAuthenticatedTime);
+            }
             if (result != null
                     && (intent = result.getParcelable(AccountManager.KEY_INTENT)) != null) {
                 /*
@@ -2798,6 +2867,7 @@
                     + ACCOUNTS_TYPE + " TEXT NOT NULL, "
                     + ACCOUNTS_PASSWORD + " TEXT, "
                     + ACCOUNTS_PREVIOUS_NAME + " TEXT, "
+                    + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " INTEGER DEFAULT 0, "
                     + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
 
             db.execSQL("CREATE TABLE " + TABLE_AUTHTOKENS + " (  "
@@ -2833,6 +2903,11 @@
                     + "UNIQUE(" + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE + "))");
         }
 
+        private void addLastSuccessfullAuthenticatedTimeColumn(SQLiteDatabase db) {
+            db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN "
+                    + ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS + " DEFAULT 0");
+        }
+
         private void addOldAccountNameColumn(SQLiteDatabase db) {
             db.execSQL("ALTER TABLE " + TABLE_ACCOUNTS + " ADD COLUMN " + ACCOUNTS_PREVIOUS_NAME);
         }
@@ -2892,6 +2967,11 @@
                 oldVersion++;
             }
 
+            if (oldVersion == 6) {
+                addLastSuccessfullAuthenticatedTimeColumn(db);
+                oldVersion++;
+            }
+
             if (oldVersion != newVersion) {
                 Log.e(TAG, "failed to upgrade version " + oldVersion + " to version " + newVersion);
             }
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index fd4974e..7a74e45 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -72,7 +72,7 @@
     static final boolean DEBUG_TRANSITION = DEBUG_ALL || false;
     static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false;
     static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
-    static final boolean DEBUG_VISBILITY = DEBUG_ALL || false;
+    static final boolean DEBUG_VISIBILITY = DEBUG_ALL || false;
 
     static final String POSTFIX_BACKUP = (APPEND_CATEGORY_NAME) ? "_Backup" : "";
     static final String POSTFIX_BROADCAST = (APPEND_CATEGORY_NAME) ? "_Broadcast" : "";
@@ -80,11 +80,29 @@
     static final String POSTFIX_CONFIGURATION = (APPEND_CATEGORY_NAME) ? "_Configuration" : "";
     static final String POSTFIX_FOCUS = (APPEND_CATEGORY_NAME) ? "_Focus" : "";
     static final String POSTFIX_IMMERSIVE = (APPEND_CATEGORY_NAME) ? "_Immersive" : "";
+    static final String POSTFIX_LOCKSCREEN = (APPEND_CATEGORY_NAME) ? "_LOCKSCREEN" : "";
     static final String POSTFIX_LRU = (APPEND_CATEGORY_NAME) ? "_LRU" : "";
     static final String POSTFIX_MU = "_MU";
     static final String POSTFIX_OOM_ADJ = (APPEND_CATEGORY_NAME) ? "_OomAdj" : "";
+    static final String POSTFIX_PAUSE = (APPEND_CATEGORY_NAME) ? "_Pause" : "";
+    static final String POSTFIX_POWER = (APPEND_CATEGORY_NAME) ? "_Power" : "";
+    static final String POSTFIX_PROCESS_OBSERVERS = (APPEND_CATEGORY_NAME)
+            ? "_ProcessObservers" : "";
+    static final String POSTFIX_PROCESSES = (APPEND_CATEGORY_NAME) ? "_Processes" : "";
+    static final String POSTFIX_PROVIDER = (APPEND_CATEGORY_NAME) ? "_Provider" : "";
+    static final String POSTFIX_PSS = (APPEND_CATEGORY_NAME) ? "_Pss" : "";
+    static final String POSTFIX_RESULTS = (APPEND_CATEGORY_NAME) ? "_Results" : "";
+    static final String POSTFIX_RECENTS = (APPEND_CATEGORY_NAME) ? "_Recents" : "";
     static final String POSTFIX_SERVICE = (APPEND_CATEGORY_NAME) ? "_Service" : "";
     static final String POSTFIX_SERVICE_EXECUTING =
             (APPEND_CATEGORY_NAME) ? "_ServiceExecuting" : "";
+    static final String POSTFIX_STACK = (APPEND_CATEGORY_NAME) ? "_Stack" : "";
+    static final String POSTFIX_SWITCH = (APPEND_CATEGORY_NAME) ? "_Switch" : "";
+    static final String POSTFIX_TASKS = (APPEND_CATEGORY_NAME) ? "_Tasks" : "";
+    static final String POSTFIX_THUMBNAILS = (APPEND_CATEGORY_NAME) ? "_Thumbnails" : "";
+    static final String POSTFIX_TRANSITION = (APPEND_CATEGORY_NAME) ? "_Transition" : "";
+    static final String POSTFIX_URI_PERMISSION = (APPEND_CATEGORY_NAME) ? "_UriPermission" : "";
+    static final String POSTFIX_USER_LEAVING = (APPEND_CATEGORY_NAME) ? "_UserLeaving" : "";
+    static final String POSTFIX_VISIBILITY = (APPEND_CATEGORY_NAME) ? "_Visibility" : "";
 
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 11c3ea6..b0b410b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -26,12 +26,12 @@
 import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
 import static com.android.internal.util.XmlUtils.writeIntAttribute;
 import static com.android.internal.util.XmlUtils.writeLongAttribute;
-import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
-import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
-import static org.xmlpull.v1.XmlPullParser.START_TAG;
 import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
 import static com.android.server.am.ActivityManagerDebugConfig.*;
 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
 import android.Manifest;
 import android.app.AppOpsManager;
@@ -76,6 +76,7 @@
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.os.Zygote;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.MemInfoReader;
@@ -258,37 +259,30 @@
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
     private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
     private static final String TAG_IMMERSIVE = TAG + POSTFIX_IMMERSIVE;
+    private static final String TAG_LOCKSCREEN = TAG + POSTFIX_LOCKSCREEN;
     private static final String TAG_LRU = TAG + POSTFIX_LRU;
     private static final String TAG_MU = TAG + POSTFIX_MU;
     private static final String TAG_OOM_ADJ = TAG + POSTFIX_OOM_ADJ;
+    private static final String TAG_POWER = TAG + POSTFIX_POWER;
+    private static final String TAG_PROCESS_OBSERVERS = TAG + POSTFIX_PROCESS_OBSERVERS;
+    private static final String TAG_PROCESSES = TAG + POSTFIX_PROCESSES;
+    private static final String TAG_PROVIDER = TAG + POSTFIX_PROVIDER;
+    private static final String TAG_PSS = TAG + POSTFIX_PSS;
+    private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
+    private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE;
+    private static final String TAG_STACK = TAG + POSTFIX_STACK;
+    private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+    private static final String TAG_URI_PERMISSION = TAG + POSTFIX_URI_PERMISSION;
+    private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
 
-    // TODO(ogunwale): Migrate all the constants below to use ActivityManagerDebugConfig class.
-    static final boolean DEBUG_PAUSE = DEBUG_ALL || false;
-    static final boolean DEBUG_POWER = DEBUG_ALL || false;
-    static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false;
-    static final boolean DEBUG_PROCESS_OBSERVERS = DEBUG_ALL || false;
-    static final boolean DEBUG_PROCESSES = DEBUG_ALL || false;
-    static final boolean DEBUG_PROVIDER = DEBUG_ALL || false;
-    static final boolean DEBUG_RESULTS = DEBUG_ALL || false;
-    static final boolean DEBUG_SERVICE = DEBUG_ALL || false;
-    static final boolean DEBUG_SERVICE_EXECUTING = DEBUG_ALL || false;
-    static final boolean DEBUG_STACK = DEBUG_ALL || false;
-    static final boolean DEBUG_SWITCH = DEBUG_ALL || false;
-    static final boolean DEBUG_TASKS = DEBUG_ALL || false;
-    static final boolean DEBUG_THUMBNAILS = DEBUG_ALL || false;
-    static final boolean DEBUG_TRANSITION = DEBUG_ALL || false;
-    static final boolean DEBUG_URI_PERMISSION = DEBUG_ALL || false;
-    static final boolean DEBUG_USER_LEAVING = DEBUG_ALL || false;
-    static final boolean DEBUG_VISBILITY = DEBUG_ALL || false;
-    static final boolean DEBUG_PSS = DEBUG_ALL || false;
-    static final boolean DEBUG_LOCKSCREEN = DEBUG_ALL || false;
-    static final boolean DEBUG_RECENTS = DEBUG_ALL || false;
-
-    // Control over CPU and battery monitoring.
-    static final long BATTERY_STATS_TIME = 30*60*1000;      // write battery stats every 30 minutes.
+    /** Control over CPU and battery monitoring */
+    // write battery stats every 30 minutes.
+    static final long BATTERY_STATS_TIME = 30 * 60 * 1000;
     static final boolean MONITOR_CPU_USAGE = true;
-    static final long MONITOR_CPU_MIN_TIME = 5*1000;        // don't sample cpu less than every 5 seconds.
-    static final long MONITOR_CPU_MAX_TIME = 0x0fffffff;    // wait possibly forever for next cpu sample.
+    // don't sample cpu less than every 5 seconds.
+    static final long MONITOR_CPU_MIN_TIME = 5 * 1000;
+    // wait possibly forever for next cpu sample.
+    static final long MONITOR_CPU_MAX_TIME = 0x0fffffff;
     static final boolean MONITOR_THREAD_CPU_USAGE = false;
 
     // The flags that are set for all calls we make to the package manager.
@@ -1231,7 +1225,7 @@
                 TAG, "Death received in " + this
                 + " for thread " + mAppThread.asBinder());
             synchronized(ActivityManagerService.this) {
-                appDiedLocked(mApp, mPid, mAppThread);
+                appDiedLocked(mApp, mPid, mAppThread, true);
             }
         }
     }
@@ -1846,8 +1840,8 @@
                     return;
                 }
 
-                if (DEBUG_PSS) Slog.d(TAG, "Showing dump heap notification from "
-                        + procName + "/" + uid);
+                if (DEBUG_PSS) Slog.d(TAG_PSS,
+                        "Showing dump heap notification from " + procName + "/" + uid);
 
                 INotificationManager inm = NotificationManager.getService();
                 if (inm == null) {
@@ -1945,7 +1939,7 @@
                     }
                     memInfo.readMemInfo();
                     synchronized (ActivityManagerService.this) {
-                        if (DEBUG_PSS) Slog.d(TAG, "Collected native and kernel memory in "
+                        if (DEBUG_PSS) Slog.d(TAG_PSS, "Collected native and kernel memory in "
                                 + (SystemClock.uptimeMillis()-start) + "ms");
                         final long cachedKb = memInfo.getCachedSizeKb();
                         final long freeKb = memInfo.getFreeSizeKb();
@@ -1967,8 +1961,9 @@
                     long lastPssTime;
                     synchronized (ActivityManagerService.this) {
                         if (mPendingPssProcesses.size() <= 0) {
-                            if (mTestPssMode || DEBUG_PSS) Slog.d(TAG, "Collected PSS of " + num
-                                    + " processes in " + (SystemClock.uptimeMillis()-start) + "ms");
+                            if (mTestPssMode || DEBUG_PSS) Slog.d(TAG_PSS,
+                                    "Collected PSS of " + num + " processes in "
+                                    + (SystemClock.uptimeMillis() - start) + "ms");
                             mPendingPssProcesses.clear();
                             return;
                         }
@@ -2187,7 +2182,7 @@
         systemDir.mkdirs();
         mBatteryStatsService = new BatteryStatsService(systemDir, mHandler);
         mBatteryStatsService.getActiveStatistics().readLocked();
-        mBatteryStatsService.getActiveStatistics().writeAsyncLocked();
+        mBatteryStatsService.scheduleWriteToDisk();
         mOnBattery = DEBUG_POWER ? true
                 : mBatteryStatsService.getActiveStatistics().getIsOnBattery();
         mBatteryStatsService.getActiveStatistics().setCallback(this);
@@ -2431,7 +2426,7 @@
 
                 if (mLastWriteTime < (now-BATTERY_STATS_TIME)) {
                     mLastWriteTime = now;
-                    mBatteryStatsService.getActiveStatistics().writeAsyncLocked();
+                    mBatteryStatsService.scheduleWriteToDisk();
                 }
             }
         }
@@ -2527,7 +2522,7 @@
 
     @Override
     public void notifyActivityDrawn(IBinder token) {
-        if (DEBUG_VISBILITY) Slog.d(TAG, "notifyActivityDrawn: token=" + token);
+        if (DEBUG_VISIBILITY) Slog.d(TAG_VISIBILITY, "notifyActivityDrawn: token=" + token);
         synchronized (this) {
             ActivityRecord r = mStackSupervisor.isInAnyStackLocked(token);
             if (r != null) {
@@ -2827,7 +2822,7 @@
         } else if (proc != null && !keepIfLarge
                 && mLastMemoryLevel > ProcessStats.ADJ_MEM_FACTOR_NORMAL
                 && proc.setProcState >= ActivityManager.PROCESS_STATE_CACHED_EMPTY) {
-            if (DEBUG_PSS) Slog.d(TAG, "May not keep " + proc + ": pss=" + proc.lastCachedPss);
+            if (DEBUG_PSS) Slog.d(TAG_PSS, "May not keep " + proc + ": pss=" + proc.lastCachedPss);
             if (proc.lastCachedPss >= mProcessList.getCachedRestoreThresholdKb()) {
                 if (proc.baseProcessTracker != null) {
                     proc.baseProcessTracker.reportCachedKill(proc.pkgList, proc.lastCachedPss);
@@ -2909,7 +2904,7 @@
         //     object attached to it so we know it couldn't have crashed; and
         // (3) There is a pid assigned to it, so it is either starting or
         //     already running.
-        if (DEBUG_PROCESSES) Slog.v(TAG, "startProcess: name=" + processName
+        if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "startProcess: name=" + processName
                 + " app=" + app + " knownToBeDead=" + knownToBeDead
                 + " thread=" + (app != null ? app.thread : null)
                 + " pid=" + (app != null ? app.pid : -1));
@@ -2917,7 +2912,7 @@
             if (!knownToBeDead || app.thread == null) {
                 // We already have the app running, or are waiting for it to
                 // come up (we have a pid but not yet its thread), so keep it.
-                if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app);
+                if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App already running: " + app);
                 // If this is a new package in the process, add the package to the list
                 app.addPackage(info.packageName, info.versionCode, mProcessStats);
                 checkTime(startTime, "startProcess: done, added package to proc");
@@ -2926,7 +2921,7 @@
 
             // An application record is attached to a previous process,
             // clean it up now.
-            if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG, "App died: " + app);
+            if (DEBUG_PROCESSES || DEBUG_CLEANUP) Slog.v(TAG_PROCESSES, "App died: " + app);
             checkTime(startTime, "startProcess: bad proc running, killing");
             Process.killProcessGroup(app.info.uid, app.pid);
             handleAppDiedLocked(app, true, true);
@@ -2941,7 +2936,7 @@
                 // If we are in the background, then check to see if this process
                 // is bad.  If so, we will just silently fail.
                 if (mBadProcesses.get(info.processName, info.uid) != null) {
-                    if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
+                    if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "Bad process: " + info.uid
                             + "/" + info.processName);
                     return null;
                 }
@@ -2950,7 +2945,7 @@
                 // crash count so that we won't make it bad until they see at
                 // least one crash dialog again, and make the process good again
                 // if it had been bad.
-                if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
+                if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "Clearing bad process: " + info.uid
                         + "/" + info.processName);
                 mProcessCrashTimes.remove(info.processName, info.uid);
                 if (mBadProcesses.get(info.processName, info.uid) != null) {
@@ -2993,7 +2988,8 @@
             if (!mProcessesOnHold.contains(app)) {
                 mProcessesOnHold.add(app);
             }
-            if (DEBUG_PROCESSES) Slog.v(TAG, "System not ready, putting on hold: " + app);
+            if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES,
+                    "System not ready, putting on hold: " + app);
             checkTime(startTime, "startProcess: returning with proc on hold");
             return app;
         }
@@ -3028,7 +3024,7 @@
             app.setPid(0);
         }
 
-        if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG,
+        if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG_PROCESSES,
                 "startProcessLocked removing on hold: " + app);
         mProcessesOnHold.remove(app);
 
@@ -3040,25 +3036,14 @@
             int uid = app.uid;
 
             int[] gids = null;
-            int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
+            int mountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
             if (!app.isolated) {
                 int[] permGids = null;
                 try {
                     checkTime(startTime, "startProcess: getting gids from package manager");
-                    final PackageManager pm = mContext.getPackageManager();
-                    permGids = pm.getPackageGids(app.info.packageName);
-
-                    if (Environment.isExternalStorageEmulated()) {
-                        checkTime(startTime, "startProcess: checking external storage perm");
-                        if (pm.checkPermission(
-                                android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE,
-                                app.info.packageName) == PERMISSION_GRANTED) {
-                            mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL;
-                        } else {
-                            mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
-                        }
-                    }
-                } catch (PackageManager.NameNotFoundException e) {
+                    permGids = AppGlobals.getPackageManager().getPackageGids(app.info.packageName,
+                            app.userId);
+                } catch (RemoteException e) {
                     Slog.w(TAG, "Unable to retrieve gids", e);
                 }
 
@@ -3066,7 +3051,7 @@
                  * Add shared application and profile GIDs so applications can share some
                  * resources like shared libraries and access user-wide resources
                  */
-                if (permGids == null) {
+                if (ArrayUtils.isEmpty(permGids)) {
                     gids = new int[2];
                 } else {
                     gids = new int[permGids.length + 2];
@@ -3208,7 +3193,8 @@
     }
 
     void updateUsageStats(ActivityRecord component, boolean resumed) {
-        if (DEBUG_SWITCH) Slog.d(TAG, "updateUsageStats: comp=" + component + "res=" + resumed);
+        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH,
+                "updateUsageStats: comp=" + component + "res=" + resumed);
         final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
         if (resumed) {
             if (mUsageStatsService != null) {
@@ -3426,7 +3412,8 @@
             mPendingProcessChanges.toArray(mActiveProcessChanges);
             mAvailProcessChanges.addAll(mPendingProcessChanges);
             mPendingProcessChanges.clear();
-            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "*** Delivering " + N + " process changes");
+            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                    "*** Delivering " + N + " process changes");
         }
 
         int i = mProcessObservers.beginBroadcast();
@@ -3438,15 +3425,16 @@
                     for (int j=0; j<N; j++) {
                         ProcessChangeItem item = mActiveProcessChanges[j];
                         if ((item.changes&ProcessChangeItem.CHANGE_ACTIVITIES) != 0) {
-                            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "ACTIVITIES CHANGED pid="
-                                    + item.pid + " uid=" + item.uid + ": "
-                                    + item.foregroundActivities);
+                            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                                    "ACTIVITIES CHANGED pid=" + item.pid + " uid="
+                                    + item.uid + ": " + item.foregroundActivities);
                             observer.onForegroundActivitiesChanged(item.pid, item.uid,
                                     item.foregroundActivities);
                         }
                         if ((item.changes&ProcessChangeItem.CHANGE_PROCESS_STATE) != 0) {
-                            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "PROCSTATE CHANGED pid="
-                                    + item.pid + " uid=" + item.uid + ": " + item.processState);
+                            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                                    "PROCSTATE CHANGED pid=" + item.pid + " uid=" + item.uid
+                                    + ": " + item.processState);
                             observer.onProcessStateChanged(item.pid, item.uid, item.processState);
                         }
                     }
@@ -4206,17 +4194,13 @@
             finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info);
         }
 
-        if (!restarting) {
-            if (!mStackSupervisor.resumeTopActivitiesLocked()) {
-                // If there was nothing to resume, and we are not already
-                // restarting this process, but there is a visible activity that
-                // is hosted by the process...  then make sure all visible
-                // activities are running, taking care of restarting this
-                // process.
-                if (hasVisibleActivities) {
-                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
-                }
-            }
+        if (!restarting && hasVisibleActivities && !mStackSupervisor.resumeTopActivitiesLocked()) {
+            // If there was nothing to resume, and we are not already
+            // restarting this process, but there is a visible activity that
+            // is hosted by the process...  then make sure all visible
+            // activities are running, taking care of restarting this
+            // process.
+            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
         }
     }
 
@@ -4303,10 +4287,11 @@
     }
 
     final void appDiedLocked(ProcessRecord app) {
-       appDiedLocked(app, app.pid, app.thread);
+       appDiedLocked(app, app.pid, app.thread, false);
     }
 
-    final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread) {
+    final void appDiedLocked(ProcessRecord app, int pid, IApplicationThread thread,
+            boolean fromBinderDied) {
         // First check if this ProcessRecord is actually active for the pid.
         synchronized (mPidsSelfLocked) {
             ProcessRecord curProc = mPidsSelfLocked.get(pid);
@@ -4322,7 +4307,9 @@
         }
 
         if (!app.killed) {
-            Process.killProcessQuiet(pid);
+            if (!fromBinderDied) {
+                Process.killProcessQuiet(pid);
+            }
             Process.killProcessGroup(app.info.uid, pid);
             app.killed = true;
         }
@@ -4359,7 +4346,7 @@
                     + ") has died and restarted (pid " + app.pid + ").");
             EventLog.writeEvent(EventLogTags.AM_PROC_DIED, app.userId, app.pid, app.processName);
         } else if (DEBUG_PROCESSES) {
-            Slog.d(TAG, "Received spurious death notification for thread "
+            Slog.d(TAG_PROCESSES, "Received spurious death notification for thread "
                     + thread.asBinder());
         }
     }
@@ -5394,9 +5381,8 @@
             boolean callerWillRestart, boolean allowRestart, String reason) {
         final String name = app.processName;
         final int uid = app.uid;
-        if (DEBUG_PROCESSES) Slog.d(
-            TAG, "Force removing proc " + app.toShortString() + " (" + name
-            + "/" + uid + ")");
+        if (DEBUG_PROCESSES) Slog.d(TAG_PROCESSES,
+            "Force removing proc " + app.toShortString() + " (" + name + "/" + uid + ")");
 
         mProcessNames.remove(name, uid);
         mIsolatedProcesses.remove(app.uid);
@@ -5640,7 +5626,7 @@
 
         // Remove this record from the list of starting applications.
         mPersistentStartingProcesses.remove(app);
-        if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG,
+        if (DEBUG_PROCESSES && mProcessesOnHold.contains(app)) Slog.v(TAG_PROCESSES,
                 "Attach application locked removing on hold: " + app);
         mProcessesOnHold.remove(app);
 
@@ -5848,7 +5834,7 @@
                 ArrayList<ProcessRecord> procs =
                     new ArrayList<ProcessRecord>(mProcessesOnHold);
                 for (int ip=0; ip<NP; ip++) {
-                    if (DEBUG_PROCESSES) Slog.v(TAG, "Starting process on hold: "
+                    if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "Starting process on hold: "
                             + procs.get(ip));
                     startProcessLocked(procs.get(ip), "on-hold", null);
                 }
@@ -5989,7 +5975,7 @@
 
     @Override
     public final void activityDestroyed(IBinder token) {
-        if (DEBUG_SWITCH) Slog.v(TAG, "ACTIVITY DESTROYED: " + token);
+        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token);
         synchronized (this) {
             ActivityStack stack = ActivityRecord.getStackLocked(token);
             if (stack != null) {
@@ -6322,31 +6308,38 @@
         }
         try {
             PendingIntentRecord res = (PendingIntentRecord)pendingResult;
-            Intent intent = res.key.requestIntent;
-            if (intent != null) {
-                if (res.lastTag != null && res.lastTagPrefix == prefix && (res.lastTagPrefix == null
-                        || res.lastTagPrefix.equals(prefix))) {
-                    return res.lastTag;
-                }
-                res.lastTagPrefix = prefix;
-                StringBuilder sb = new StringBuilder(128);
-                if (prefix != null) {
-                    sb.append(prefix);
-                }
-                if (intent.getAction() != null) {
-                    sb.append(intent.getAction());
-                } else if (intent.getComponent() != null) {
-                    intent.getComponent().appendShortString(sb);
-                } else {
-                    sb.append("?");
-                }
-                return res.lastTag = sb.toString();
+            synchronized (this) {
+                return getTagForIntentSenderLocked(res, prefix);
             }
         } catch (ClassCastException e) {
         }
         return null;
     }
 
+    String getTagForIntentSenderLocked(PendingIntentRecord res, String prefix) {
+        final Intent intent = res.key.requestIntent;
+        if (intent != null) {
+            if (res.lastTag != null && res.lastTagPrefix == prefix && (res.lastTagPrefix == null
+                    || res.lastTagPrefix.equals(prefix))) {
+                return res.lastTag;
+            }
+            res.lastTagPrefix = prefix;
+            final StringBuilder sb = new StringBuilder(128);
+            if (prefix != null) {
+                sb.append(prefix);
+            }
+            if (intent.getAction() != null) {
+                sb.append(intent.getAction());
+            } else if (intent.getComponent() != null) {
+                intent.getComponent().appendShortString(sb);
+            } else {
+                sb.append("?");
+            }
+            return res.lastTag = sb.toString();
+        }
+        return null;
+    }
+
     @Override
     public void setProcessLimit(int max) {
         enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
@@ -6530,7 +6523,7 @@
         if (permission == null) {
             return PackageManager.PERMISSION_DENIED;
         }
-        return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true);
+        return checkComponentPermission(permission, pid, uid, -1, true);
     }
 
     @Override
@@ -6550,7 +6543,7 @@
             pid = tlsIdentity.pid;
         }
 
-        return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true);
+        return checkComponentPermission(permission, pid, uid, -1, true);
     }
 
     /**
@@ -6587,7 +6580,7 @@
      */
     private final boolean checkHoldingPermissionsLocked(
             IPackageManager pm, ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) {
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
                 "checkHoldingPermissionsLocked: uri=" + grantUri + " uid=" + uid);
         if (UserHandle.getUserId(uid) != grantUri.sourceUserId) {
             if (ActivityManager.checkComponentPermission(INTERACT_ACROSS_USERS, uid, -1, true)
@@ -6635,8 +6628,8 @@
                     if (pp.match(path)) {
                         if (!readMet) {
                             final String pprperm = pp.getReadPermission();
-                            if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Checking read perm for "
-                                    + pprperm + " for " + pp.getPath()
+                            if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
+                                    "Checking read perm for " + pprperm + " for " + pp.getPath()
                                     + ": match=" + pp.match(path)
                                     + " check=" + pm.checkUidPermission(pprperm, uid));
                             if (pprperm != null) {
@@ -6650,8 +6643,8 @@
                         }
                         if (!writeMet) {
                             final String ppwperm = pp.getWritePermission();
-                            if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Checking write perm "
-                                    + ppwperm + " for " + pp.getPath()
+                            if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
+                                    "Checking write perm " + ppwperm + " for " + pp.getPath()
                                     + ": match=" + pp.match(path)
                                     + " check=" + pm.checkUidPermission(ppwperm, uid));
                             if (ppwperm != null) {
@@ -6796,7 +6789,7 @@
         }
 
         if (targetPkg != null) {
-            if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+            if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
                     "Checking grant " + targetPkg + " permission to " + grantUri);
         }
 
@@ -6804,7 +6797,7 @@
 
         // If this is not a content: uri, we can't do anything with it.
         if (!ContentResolver.SCHEME_CONTENT.equals(grantUri.uri.getScheme())) {
-            if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+            if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
                     "Can't grant URI permission for non-content URI: " + grantUri);
             return -1;
         }
@@ -6822,7 +6815,7 @@
             try {
                 targetUid = pm.getPackageUid(targetPkg, UserHandle.getUserId(callingUid));
                 if (targetUid < 0) {
-                    if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+                    if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
                             "Can't grant URI permission no uid for: " + targetPkg);
                     return -1;
                 }
@@ -6835,7 +6828,7 @@
             // First...  does the target actually need this permission?
             if (checkHoldingPermissionsLocked(pm, pi, grantUri, targetUid, modeFlags)) {
                 // No need to grant the target this permission.
-                if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+                if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
                         "Target " + targetPkg + " already has full permission to " + grantUri);
                 return -1;
             }
@@ -6932,7 +6925,7 @@
         // to the uri, and the target doesn't.  Let's now give this to
         // the target.
 
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
                 "Granting " + targetPkg + "/" + targetUid + " permission to " + grantUri);
 
         final String authority = grantUri.uri.getAuthority();
@@ -6990,7 +6983,7 @@
      */
     NeededUriGrants checkGrantUriPermissionFromIntentLocked(int callingUid,
             String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId) {
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
                 "Checking URI perm to data=" + (intent != null ? intent.getData() : null)
                 + " clip=" + (intent != null ? intent.getClipData() : null)
                 + " from " + intent + "; flags=0x"
@@ -7024,10 +7017,9 @@
                 return null;
             }
             if (targetUid < 0) {
-                if (DEBUG_URI_PERMISSION) {
-                    Slog.v(TAG, "Can't grant URI permission no uid for: " + targetPkg
-                            + " on user " + targetUserId);
-                }
+                if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
+                        "Can't grant URI permission no uid for: " + targetPkg
+                        + " on user " + targetUserId);
                 return null;
             }
         }
@@ -7134,7 +7126,7 @@
             final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(
                     perm.targetUid);
             if (perms != null) {
-                if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+                if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
                         "Removing " + perm.targetUid + " permission to " + perm.uri);
 
                 perms.remove(perm.uri);
@@ -7146,7 +7138,8 @@
     }
 
     private void revokeUriPermissionLocked(int callingUid, GrantUri grantUri, final int modeFlags) {
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG, "Revoking all granted permissions to " + grantUri);
+        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
+                "Revoking all granted permissions to " + grantUri);
 
         final IPackageManager pm = AppGlobals.getPackageManager();
         final String authority = grantUri.uri.getAuthority();
@@ -7168,9 +7161,9 @@
                     final UriPermission perm = it.next();
                     if (perm.uri.sourceUserId == grantUri.sourceUserId
                             && perm.uri.uri.isPathPrefixMatch(grantUri.uri)) {
-                        if (DEBUG_URI_PERMISSION)
-                            Slog.v(TAG, "Revoking non-owned " + perm.targetUid +
-                                    " permission to " + perm.uri);
+                        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
+                                "Revoking non-owned " + perm.targetUid
+                                + " permission to " + perm.uri);
                         persistChanged |= perm.revokeModes(
                                 modeFlags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
                         if (perm.modeFlags == 0) {
@@ -7200,8 +7193,7 @@
                 final UriPermission perm = it.next();
                 if (perm.uri.sourceUserId == grantUri.sourceUserId
                         && perm.uri.uri.isPathPrefixMatch(grantUri.uri)) {
-                    if (DEBUG_URI_PERMISSION)
-                        Slog.v(TAG,
+                    if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
                                 "Revoking " + perm.targetUid + " permission to " + perm.uri);
                     persistChanged |= perm.revokeModes(
                             modeFlags | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, true);
@@ -7386,7 +7378,7 @@
     }
 
     private void writeGrantedUriPermissions() {
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG, "writeGrantedUriPermissions()");
+        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "writeGrantedUriPermissions()");
 
         // Snapshot permissions so we can persist without lock
         ArrayList<UriPermission.Snapshot> persist = Lists.newArrayList();
@@ -7434,7 +7426,7 @@
     }
 
     private void readGrantedUriPermissionsLocked() {
-        if (DEBUG_URI_PERMISSION) Slog.v(TAG, "readGrantedUriPermissions()");
+        if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "readGrantedUriPermissions()");
 
         final long now = System.currentTimeMillis();
 
@@ -7612,9 +7604,8 @@
         for (int i = 0; i < trimCount; i++) {
             final UriPermission perm = persisted.get(i);
 
-            if (DEBUG_URI_PERMISSION) {
-                Slog.v(TAG, "Trimming grant created at " + perm.persistedCreateTime);
-            }
+            if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION,
+                    "Trimming grant created at " + perm.persistedCreateTime);
 
             perm.releasePersistableModes(~0);
             removeUriPermissionIfNeededLocked(perm);
@@ -7844,7 +7835,7 @@
                 TaskRecord tr = mRecentTasks.get(i);
                 // Only add calling user or related users recent tasks
                 if (!includedUsers.contains(Integer.valueOf(tr.userId))) {
-                    if (DEBUG_RECENTS) Slog.d(TAG, "Skipping, not user: " + tr);
+                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not user: " + tr);
                     continue;
                 }
 
@@ -7863,25 +7854,27 @@
                         // If the caller doesn't have the GET_TASKS permission, then only
                         // allow them to see a small subset of tasks -- their own and home.
                         if (!tr.isHomeTask() && tr.effectiveUid != callingUid) {
-                            if (DEBUG_RECENTS) Slog.d(TAG, "Skipping, not allowed: " + tr);
+                            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Skipping, not allowed: " + tr);
                             continue;
                         }
                     }
                     if ((flags & ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS) != 0) {
                         if (tr.stack != null && tr.stack.isHomeStack()) {
-                            if (DEBUG_RECENTS) Slog.d(TAG, "Skipping, home stack task: " + tr);
+                            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                                    "Skipping, home stack task: " + tr);
                             continue;
                         }
                     }
                     if (tr.autoRemoveRecents && tr.getTopActivity() == null) {
                         // Don't include auto remove tasks that are finished or finishing.
-                        if (DEBUG_RECENTS) Slog.d(TAG, "Skipping, auto-remove without activity: "
-                                + tr);
+                        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                                "Skipping, auto-remove without activity: " + tr);
                         continue;
                     }
                     if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0
                             && !tr.isAvailable) {
-                        if (DEBUG_RECENTS) Slog.d(TAG, "Skipping, unavail real act: " + tr);
+                        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                                "Skipping, unavail real act: " + tr);
                         continue;
                     }
 
@@ -8215,10 +8208,9 @@
      */
     @Override
     public void moveTaskToFront(int taskId, int flags, Bundle options) {
-        enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
-                "moveTaskToFront()");
+        enforceCallingPermission(android.Manifest.permission.REORDER_TASKS, "moveTaskToFront()");
 
-        if (DEBUG_STACK) Slog.d(TAG, "moveTaskToFront: moving taskId=" + taskId);
+        if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToFront: moving taskId=" + taskId);
         synchronized(this) {
             moveTaskToFrontLocked(taskId, flags, options);
         }
@@ -8378,8 +8370,8 @@
         synchronized (this) {
             long ident = Binder.clearCallingIdentity();
             try {
-                if (DEBUG_STACK) Slog.d(TAG, "moveTaskToStack: moving task=" + taskId + " to stackId="
-                        + stackId + " toTop=" + toTop);
+                if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
+                        + " to stackId=" + stackId + " toTop=" + toTop);
                 mStackSupervisor.moveTaskToStackLocked(taskId, stackId, toTop);
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -8790,7 +8782,7 @@
             for (int i=0; i<r.conProviders.size(); i++) {
                 ContentProviderConnection conn = r.conProviders.get(i);
                 if (conn.provider == cpr) {
-                    if (DEBUG_PROVIDER) Slog.v(TAG,
+                    if (DEBUG_PROVIDER) Slog.v(TAG_PROVIDER,
                             "Adding provider requested by "
                             + r.processName + " from process "
                             + cpr.info.processName + ": " + cpr.name.flattenToShortString()
@@ -8826,7 +8818,7 @@
             ContentProviderRecord cpr, IBinder externalProcessToken, boolean stable) {
         if (conn != null) {
             cpr = conn.provider;
-            if (DEBUG_PROVIDER) Slog.v(TAG,
+            if (DEBUG_PROVIDER) Slog.v(TAG_PROVIDER,
                     "Removing provider requested by "
                     + conn.client.processName + " from process "
                     + cpr.info.processName + ": " + cpr.name.flattenToShortString()
@@ -8954,7 +8946,7 @@
                     checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
                     boolean success = updateOomAdjLocked(cpr.proc);
                     checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
-                    if (DEBUG_PROVIDER) Slog.i(TAG, "Adjust success: " + success);
+                    if (DEBUG_PROVIDER) Slog.i(TAG_PROVIDER, "Adjust success: " + success);
                     // NOTE: there is still a race here where a signal could be
                     // pending on the process even though we managed to update its
                     // adj level.  Not sure what to do about this, but at least
@@ -8964,8 +8956,7 @@
                         // has been killed on us.  We need to wait for a new
                         // process to be started, and make sure its death
                         // doesn't kill our process.
-                        Slog.i(TAG,
-                                "Existing provider " + cpr.name.flattenToShortString()
+                        Slog.i(TAG, "Existing provider " + cpr.name.flattenToShortString()
                                 + " is crashing; detaching " + r);
                         boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
                         checkTime(startTime, "getContentProviderImpl: before appDied");
@@ -9076,18 +9067,16 @@
                     return cpr.newHolder(null);
                 }
 
-                if (DEBUG_PROVIDER) {
-                    RuntimeException e = new RuntimeException("here");
-                    Slog.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + (r != null ? r.uid : null)
-                          + " pruid " + cpr.appInfo.uid + "): " + cpr.info.name, e);
-                }
+                if (DEBUG_PROVIDER) Slog.w(TAG_PROVIDER, "LAUNCHING REMOTE PROVIDER (myuid "
+                            + (r != null ? r.uid : null) + " pruid " + cpr.appInfo.uid + "): "
+                            + cpr.info.name + " callers=" + Debug.getCallers(6));
 
                 // This is single process, and our app is now connecting to it.
                 // See if we are already in the process of launching this
                 // provider.
                 final int N = mLaunchingProviders.size();
                 int i;
-                for (i=0; i<N; i++) {
+                for (i = 0; i < N; i++) {
                     if (mLaunchingProviders.get(i) == cpr) {
                         break;
                     }
@@ -9116,9 +9105,8 @@
                         ProcessRecord proc = getProcessRecordLocked(
                                 cpi.processName, cpr.appInfo.uid, false);
                         if (proc != null && proc.thread != null) {
-                            if (DEBUG_PROVIDER) {
-                                Slog.d(TAG, "Installing in existing process " + proc);
-                            }
+                            if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER,
+                                    "Installing in existing process " + proc);
                             if (!proc.pubProviders.containsKey(cpi.name)) {
                                 checkTime(startTime, "getContentProviderImpl: scheduling install");
                                 proc.pubProviders.put(cpi.name, cpr);
@@ -9833,7 +9821,7 @@
     }
 
     void logLockScreen(String msg) {
-        if (DEBUG_LOCKSCREEN) Slog.d(TAG, Debug.getCallers(2) + ":" + msg
+        if (DEBUG_LOCKSCREEN) Slog.d(TAG_LOCKSCREEN, Debug.getCallers(2) + ":" + msg
                 + " mLockScreenShown=" + lockScreenShownToString() + " mWakefulness="
                 + PowerManagerInternal.wakefulnessToString(mWakefulness)
                 + " mSleeping=" + mSleeping);
@@ -10475,17 +10463,21 @@
         if (!(sender instanceof PendingIntentRecord)) {
             return;
         }
-        BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+        final PendingIntentRecord rec = (PendingIntentRecord)sender;
+        final String tag;
+        synchronized (this) {
+            tag = getTagForIntentSenderLocked(rec, "*walarm*:");
+        }
+        final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
         synchronized (stats) {
             if (mBatteryStatsService.isOnBattery()) {
                 mBatteryStatsService.enforceCallingPermission();
-                PendingIntentRecord rec = (PendingIntentRecord)sender;
                 int MY_UID = Binder.getCallingUid();
                 int uid = rec.uid == MY_UID ? Process.SYSTEM_UID : rec.uid;
                 BatteryStatsImpl.Uid.Pkg pkg =
                     stats.getPackageStatsLocked(sourceUid >= 0 ? sourceUid : uid,
                             sourcePkg != null ? sourcePkg : rec.key.packageName);
-                pkg.incWakeupsLocked();
+                pkg.noteWakeupAlarmLocked(tag);
             }
         }
     }
@@ -14901,8 +14893,8 @@
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
 
-        if (DEBUG_SERVICE)
-            Slog.v(TAG, "startService: " + service + " type=" + resolvedType);
+        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
+                "startService: " + service + " type=" + resolvedType);
         synchronized(this) {
             final int callingPid = Binder.getCallingPid();
             final int callingUid = Binder.getCallingUid();
@@ -14917,8 +14909,8 @@
     ComponentName startServiceInPackage(int uid, Intent service, String resolvedType, int userId)
             throws TransactionTooLargeException {
         synchronized(this) {
-            if (DEBUG_SERVICE)
-                Slog.v(TAG, "startServiceInPackage: " + service + " type=" + resolvedType);
+            if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
+                    "startServiceInPackage: " + service + " type=" + resolvedType);
             final long origId = Binder.clearCallingIdentity();
             ComponentName res = mServices.startServiceLocked(null, service,
                     resolvedType, -1, uid, userId);
@@ -17384,11 +17376,11 @@
      * Record new PSS sample for a process.
      */
     void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss, long now) {
-        EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss*1024, uss*1024);
+        EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024);
         proc.lastPssTime = now;
         proc.baseProcessTracker.addPss(pss, uss, true, proc.pkgList);
-        if (DEBUG_PSS) Slog.d(TAG, "PSS of " + proc.toShortString()
-                + ": " + pss + " lastPss=" + proc.lastPss
+        if (DEBUG_PSS) Slog.d(TAG_PSS,
+                "PSS of " + proc.toShortString() + ": " + pss + " lastPss=" + proc.lastPss
                 + " state=" + ProcessList.makeProcStateString(procState));
         if (proc.initialIdlePss == 0) {
             proc.initialIdlePss = pss;
@@ -17400,7 +17392,7 @@
 
         Long check = mMemWatchProcesses.get(proc.processName);
         if (check != null) {
-            if ((pss*1024) >= check && proc.thread != null && mMemWatchDumpProcName == null) {
+            if ((pss * 1024) >= check && proc.thread != null && mMemWatchDumpProcName == null) {
                 boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
                 if (!isDebuggable) {
                     if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
@@ -17434,7 +17426,7 @@
                                 IApplicationThread thread = myProc.thread;
                                 if (thread != null) {
                                     try {
-                                        if (DEBUG_PSS) Slog.d(TAG, "Requesting dump heap from "
+                                        if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting dump heap from "
                                                 + myProc + " to " + heapdumpFile);
                                         thread.dumpHeap(true, heapdumpFile.toString(), fd);
                                     } catch (RemoteException e) {
@@ -17470,7 +17462,7 @@
         if (mPendingPssProcesses.size() == 0) {
             mBgHandler.sendEmptyMessage(COLLECT_PSS_BG_MSG);
         }
-        if (DEBUG_PSS) Slog.d(TAG, "Requesting PSS of: " + proc);
+        if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting PSS of: " + proc);
         proc.pssProcState = procState;
         mPendingPssProcesses.add(proc);
     }
@@ -17485,13 +17477,17 @@
                 return;
             }
         }
-        if (DEBUG_PSS) Slog.d(TAG, "Requesting PSS of all procs!  memLowered=" + memLowered);
+        if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting PSS of all procs!  memLowered=" + memLowered);
         mLastFullPssTime = now;
         mFullPssPending = true;
         mPendingPssProcesses.ensureCapacity(mLruProcesses.size());
         mPendingPssProcesses.clear();
-        for (int i=mLruProcesses.size()-1; i>=0; i--) {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
             ProcessRecord app = mLruProcesses.get(i);
+            if (app.thread == null
+                    || app.curProcState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
+                continue;
+            }
             if (memLowered || now > (app.lastStateTime+ProcessList.PSS_ALL_INTERVAL)) {
                 app.pssProcState = app.setProcState;
                 app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState, true,
@@ -17697,7 +17693,7 @@
                     sb.append(" (");
                     sb.append((wtimeUsed*100)/realtimeSince);
                     sb.append("%)");
-                    Slog.i(TAG, sb.toString());
+                    Slog.i(TAG_POWER, sb.toString());
                     sb.setLength(0);
                     sb.append("CPU for ");
                     app.toShortString(sb);
@@ -17708,7 +17704,7 @@
                     sb.append(" (");
                     sb.append((cputimeUsed*100)/uptimeSince);
                     sb.append("%)");
-                    Slog.i(TAG, sb.toString());
+                    Slog.i(TAG_POWER, sb.toString());
                 }
                 // If a process has held a wake lock for more
                 // than 50% of the time during this period,
@@ -17807,8 +17803,8 @@
                 }
             }
         }
-        if (app.setProcState < 0 || ProcessList.procStatesDifferForMem(app.curProcState,
-                app.setProcState)) {
+        if (app.setProcState == ActivityManager.PROCESS_STATE_NONEXISTENT
+                || ProcessList.procStatesDifferForMem(app.curProcState, app.setProcState)) {
             if (false && mTestPssMode && app.setProcState >= 0 && app.lastStateTime <= (now-200)) {
                 // Experimental code to more aggressively collect pss while
                 // running test...  the problem is that this tends to collect
@@ -17825,7 +17821,7 @@
             app.lastStateTime = now;
             app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState, true,
                     mTestPssMode, isSleeping(), now);
-            if (DEBUG_PSS) Slog.d(TAG, "Process state change from "
+            if (DEBUG_PSS) Slog.d(TAG_PSS, "Process state change from "
                     + ProcessList.makeProcStateString(app.setProcState) + " to "
                     + ProcessList.makeProcStateString(app.curProcState) + " next pss in "
                     + (app.nextPssTime-now) + ": " + app);
@@ -17836,9 +17832,8 @@
                 requestPssLocked(app, app.setProcState);
                 app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState, false,
                         mTestPssMode, isSleeping(), now);
-            } else if (false && DEBUG_PSS) {
-                Slog.d(TAG, "Not requesting PSS of " + app + ": next=" + (app.nextPssTime-now));
-            }
+            } else if (false && DEBUG_PSS) Slog.d(TAG_PSS,
+                    "Not requesting PSS of " + app + ": next=" + (app.nextPssTime-now));
         }
         if (app.setProcState != app.curProcState) {
             if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ,
@@ -17871,13 +17866,15 @@
         }
 
         if (changes != 0) {
-            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Changes in " + app + ": " + changes);
+            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                    "Changes in " + app + ": " + changes);
             int i = mPendingProcessChanges.size()-1;
             ProcessChangeItem item = null;
             while (i >= 0) {
                 item = mPendingProcessChanges.get(i);
                 if (item.pid == app.pid) {
-                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Re-using existing item: " + item);
+                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                            "Re-using existing item: " + item);
                     break;
                 }
                 i--;
@@ -17887,16 +17884,18 @@
                 final int NA = mAvailProcessChanges.size();
                 if (NA > 0) {
                     item = mAvailProcessChanges.remove(NA-1);
-                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Retreiving available item: " + item);
+                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                            "Retreiving available item: " + item);
                 } else {
                     item = new ProcessChangeItem();
-                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Allocating new item: " + item);
+                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                            "Allocating new item: " + item);
                 }
                 item.changes = 0;
                 item.pid = app.pid;
                 item.uid = app.info.uid;
                 if (mPendingProcessChanges.size() == 0) {
-                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG,
+                    if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
                             "*** Enqueueing dispatch processes changed!");
                     mHandler.obtainMessage(DISPATCH_PROCESSES_CHANGED).sendToTarget();
                 }
@@ -17905,8 +17904,8 @@
             item.changes |= changes;
             item.processState = app.repProcState;
             item.foregroundActivities = app.repForegroundActivities;
-            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG, "Item "
-                    + Integer.toHexString(System.identityHashCode(item))
+            if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
+                    "Item " + Integer.toHexString(System.identityHashCode(item))
                     + " " + app.toShortString() + ": changes=" + item.changes
                     + " procState=" + item.processState
                     + " foreground=" + item.foregroundActivities
@@ -18662,7 +18661,7 @@
                         + " does not match last path " + mMemWatchDumpFile);
                 return;
             }
-            if (DEBUG_PSS) Slog.d(TAG, "Dump heap finished for " + path);
+            if (DEBUG_PSS) Slog.d(TAG_PSS, "Dump heap finished for " + path);
             mHandler.sendEmptyMessage(POST_DUMP_HEAP_NOTIFICATION_MSG);
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index d34b33b..f3b18f5 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -17,8 +17,6 @@
 package com.android.server.am;
 
 import static com.android.server.am.ActivityManagerDebugConfig.*;
-import static com.android.server.am.ActivityManagerService.DEBUG_SWITCH;
-import static com.android.server.am.ActivityManagerService.DEBUG_THUMBNAILS;
 import static com.android.server.am.TaskPersister.DEBUG_PERSISTER;
 import static com.android.server.am.TaskPersister.DEBUG_RESTORER;
 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
@@ -75,6 +73,8 @@
  */
 final class ActivityRecord {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityRecord" : TAG_AM;
+    private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+    private static final String TAG_THUMBNAILS = TAG + POSTFIX_THUMBNAILS;
 
     private static final boolean SHOW_ACTIVITY_START_TIME = true;
     static final boolean DEBUG_SAVED_STATE = ActivityStackSupervisor.DEBUG_SAVED_STATE;
@@ -354,7 +354,7 @@
             synchronized (mService) {
                 ActivityRecord r = tokenToActivityRecordLocked(this);
                 if (r != null) {
-                    if (DEBUG_SWITCH) Log.v(TAG, "windowsGone(): " + r);
+                    if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsGone(): " + r);
                     r.nowVisible = false;
                     return;
                 }
@@ -863,7 +863,7 @@
 
     void updateThumbnailLocked(Bitmap newThumbnail, CharSequence description) {
         if (newThumbnail != null) {
-            if (DEBUG_THUMBNAILS) Slog.i(TAG,
+            if (DEBUG_THUMBNAILS) Slog.i(TAG_THUMBNAILS,
                     "Setting thumbnail of " + this + " to " + newThumbnail);
             boolean thumbnailUpdated = task.setLastThumbnail(newThumbnail);
             if (thumbnailUpdated && isPersistable()) {
@@ -1014,7 +1014,7 @@
 
     void windowsVisibleLocked() {
         mStackSupervisor.reportActivityVisibleLocked(this);
-        if (DEBUG_SWITCH) Log.v(TAG, "windowsVisibleLocked(): " + this);
+        if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "windowsVisibleLocked(): " + this);
         if (!nowVisible) {
             nowVisible = true;
             lastVisibleTime = SystemClock.uptimeMillis();
@@ -1030,7 +1030,7 @@
                 if (size > 0) {
                     for (int i = 0; i < size; i++) {
                         ActivityRecord r = mStackSupervisor.mWaitingVisibleActivities.get(i);
-                        if (DEBUG_SWITCH) Log.v(TAG, "Was waiting for visible: " + r);
+                        if (DEBUG_SWITCH) Log.v(TAG_SWITCH, "Was waiting for visible: " + r);
                     }
                     mStackSupervisor.mWaitingVisibleActivities.clear();
                     mStackSupervisor.scheduleIdleLocked();
@@ -1041,24 +1041,21 @@
     }
 
     ActivityRecord getWaitingHistoryRecordLocked() {
-        // First find the real culprit...  if we are waiting
-        // for another app to start, then we have paused dispatching
-        // for this activity.
-        ActivityRecord r = this;
-        if (mStackSupervisor.mWaitingVisibleActivities.contains(this)) {
+        // First find the real culprit...  if this activity is waiting for
+        // another activity to start or has stopped, then the key dispatching
+        // timeout should not be caused by this.
+        if (mStackSupervisor.mWaitingVisibleActivities.contains(this) || stopped) {
             final ActivityStack stack = mStackSupervisor.getFocusedStack();
-            // Hmmm, who might we be waiting for?
-            r = stack.mResumedActivity;
+            // Try to use the one which is closest to top.
+            ActivityRecord r = stack.mResumedActivity;
             if (r == null) {
                 r = stack.mPausingActivity;
             }
-            // Both of those null?  Fall back to 'this' again
-            if (r == null) {
-                r = this;
+            if (r != null) {
+                return r;
             }
         }
-
-        return r;
+        return this;
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index b102a07..8299be8 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -17,14 +17,6 @@
 package com.android.server.am;
 
 import static com.android.server.am.ActivityManagerDebugConfig.*;
-import static com.android.server.am.ActivityManagerService.DEBUG_PAUSE;
-import static com.android.server.am.ActivityManagerService.DEBUG_RESULTS;
-import static com.android.server.am.ActivityManagerService.DEBUG_STACK;
-import static com.android.server.am.ActivityManagerService.DEBUG_SWITCH;
-import static com.android.server.am.ActivityManagerService.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerService.DEBUG_TRANSITION;
-import static com.android.server.am.ActivityManagerService.DEBUG_USER_LEAVING;
-import static com.android.server.am.ActivityManagerService.DEBUG_VISBILITY;
 
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
@@ -96,6 +88,14 @@
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStack" : TAG_AM;
     private static final String TAG_CLEANUP = TAG + POSTFIX_CLEANUP;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
+    private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
+    private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
+    private static final String TAG_STACK = TAG + POSTFIX_STACK;
+    private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+    private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
+    private static final String TAG_TRANSITION = TAG + POSTFIX_TRANSITION;
+    private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
+    private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
 
     private static final boolean VALIDATE_TOKENS = false;
 
@@ -538,23 +538,23 @@
         // If documentData is non-null then it must match the existing task data.
         Uri documentData = isDocument ? intent.getData() : null;
 
-        if (DEBUG_TASKS) Slog.d(TAG, "Looking for task of " + target + " in " + this);
+        if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + target + " in " + this);
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
             if (task.voiceSession != null) {
                 // We never match voice sessions; those always run independently.
-                if (DEBUG_TASKS) Slog.d(TAG, "Skipping " + task + ": voice session");
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": voice session");
                 continue;
             }
             if (task.userId != userId) {
                 // Looking for a different task.
-                if (DEBUG_TASKS) Slog.d(TAG, "Skipping " + task + ": different user");
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": different user");
                 continue;
             }
             final ActivityRecord r = task.getTopActivity();
             if (r == null || r.finishing || r.userId != userId ||
                     r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
-                if (DEBUG_TASKS) Slog.d(TAG, "Skipping " + task + ": mismatch root " + r);
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping " + task + ": mismatch root " + r);
                 continue;
             }
 
@@ -573,34 +573,32 @@
                 taskDocumentData = null;
             }
 
-            if (DEBUG_TASKS) Slog.d(TAG, "Comparing existing cls="
+            if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Comparing existing cls="
                     + taskIntent.getComponent().flattenToShortString()
                     + "/aff=" + r.task.rootAffinity + " to new cls="
                     + intent.getComponent().flattenToShortString() + "/aff=" + info.taskAffinity);
             if (!isDocument && !taskIsDocument && task.rootAffinity != null) {
                 if (task.rootAffinity.equals(target.taskAffinity)) {
-                    if (DEBUG_TASKS) Slog.d(TAG, "Found matching affinity!");
+                    if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching affinity!");
                     return r;
                 }
             } else if (taskIntent != null && taskIntent.getComponent() != null &&
                     taskIntent.getComponent().compareTo(cls) == 0 &&
                     Objects.equals(documentData, taskDocumentData)) {
-                if (DEBUG_TASKS) Slog.d(TAG, "Found matching class!");
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching class!");
                 //dump();
-                if (DEBUG_TASKS) Slog.d(TAG, "For Intent " + intent + " bringing to top: "
-                        + r.intent);
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS,
+                        "For Intent " + intent + " bringing to top: " + r.intent);
                 return r;
             } else if (affinityIntent != null && affinityIntent.getComponent() != null &&
                     affinityIntent.getComponent().compareTo(cls) == 0 &&
                     Objects.equals(documentData, taskDocumentData)) {
-                if (DEBUG_TASKS) Slog.d(TAG, "Found matching class!");
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Found matching class!");
                 //dump();
-                if (DEBUG_TASKS) Slog.d(TAG, "For Intent " + intent + " bringing to top: "
-                        + r.intent);
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS,
+                        "For Intent " + intent + " bringing to top: " + r.intent);
                 return r;
-            } else if (DEBUG_TASKS) {
-                Slog.d(TAG, "Not a match: " + task);
-            }
+            } else if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Not a match: " + task);
         }
 
         return null;
@@ -652,7 +650,7 @@
         for (int i = 0; i < index; ) {
             TaskRecord task = mTaskHistory.get(i);
             if (isCurrentProfileLocked(task.userId)) {
-                if (DEBUG_TASKS) Slog.d(TAG, "switchUserLocked: stack=" + getStackId() +
+                if (DEBUG_TASKS) Slog.d(TAG_TASKS, "switchUserLocked: stack=" + getStackId() +
                         " moving " + task + " to top");
                 mTaskHistory.remove(i);
                 mTaskHistory.add(task);
@@ -739,14 +737,15 @@
     boolean checkReadyForSleepLocked() {
         if (mResumedActivity != null) {
             // Still have something resumed; can't sleep until it is paused.
-            if (DEBUG_PAUSE) Slog.v(TAG, "Sleep needs to pause " + mResumedActivity);
-            if (DEBUG_USER_LEAVING) Slog.v(TAG, "Sleep => pause with userLeaving=false");
+            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep needs to pause " + mResumedActivity);
+            if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
+                    "Sleep => pause with userLeaving=false");
             startPausingLocked(false, true, false, false);
             return true;
         }
         if (mPausingActivity != null) {
             // Still waiting for something to pause; can't sleep yet.
-            if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still waiting to pause " + mPausingActivity);
+            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still waiting to pause " + mPausingActivity);
             return true;
         }
 
@@ -829,7 +828,7 @@
         }
 
         if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSING: " + prev);
-        else if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev);
+        else if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Start pausing: " + prev);
         mResumedActivity = null;
         mPausingActivity = prev;
         mLastPausedActivity = prev;
@@ -847,7 +846,7 @@
         mService.updateCpuStats();
 
         if (prev.app != null && prev.app.thread != null) {
-            if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev);
+            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
             try {
                 EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
                         prev.userId, System.identityHashCode(prev),
@@ -881,8 +880,8 @@
             // key dispatch; the same activity will pick it up again on wakeup.
             if (!uiSleeping) {
                 prev.pauseKeyDispatchingLocked();
-            } else {
-                if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off");
+            } else if (DEBUG_PAUSE) {
+                 Slog.v(TAG_PAUSE, "Key dispatch not paused for screen off");
             }
 
             if (dontWait) {
@@ -899,14 +898,14 @@
                 msg.obj = prev;
                 prev.pauseTime = SystemClock.uptimeMillis();
                 mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);
-                if (DEBUG_PAUSE) Slog.v(TAG, "Waiting for pause to complete...");
+                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Waiting for pause to complete...");
                 return true;
             }
 
         } else {
             // This activity failed to schedule the
             // pause, so just treat it as being paused now.
-            if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next.");
+            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");
             if (!resuming) {
                 mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
             }
@@ -915,8 +914,8 @@
     }
 
     final void activityPausedLocked(IBinder token, boolean timeout) {
-        if (DEBUG_PAUSE) Slog.v(
-            TAG, "Activity paused: token=" + token + ", timeout=" + timeout);
+        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE,
+            "Activity paused: token=" + token + ", timeout=" + timeout);
 
         final ActivityRecord r = isInStackLocked(token);
         if (r != null) {
@@ -977,18 +976,18 @@
 
     private void completePauseLocked(boolean resumeNext) {
         ActivityRecord prev = mPausingActivity;
-        if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev);
+        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause: " + prev);
 
         if (prev != null) {
             prev.state = ActivityState.PAUSED;
             if (prev.finishing) {
-                if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev);
+                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
                 prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false);
             } else if (prev.app != null) {
-                if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev);
+                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending stop: " + prev);
                 if (mStackSupervisor.mWaitingVisibleActivities.remove(prev)) {
-                    if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(
-                            TAG, "Complete pause, no longer waiting: " + prev);
+                    if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(TAG_PAUSE,
+                            "Complete pause, no longer waiting: " + prev);
                 }
                 if (prev.configDestroy) {
                     // The previous is being paused because the configuration
@@ -996,7 +995,7 @@
                     // To juggle the fact that we are also starting a new
                     // instance right now, we need to first completely stop
                     // the current instance before starting the new one.
-                    if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
+                    if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Destroying after pause: " + prev);
                     destroyActivityLocked(prev, true, "pause-config");
                 } else if (!hasVisibleBehindActivity()) {
                     // If we were visible then resumeTopActivities will release resources before
@@ -1008,16 +1007,20 @@
                         // then give up on things going idle and start clearing
                         // them out. Or if r is the last of activity of the last task the stack
                         // will be empty and must be cleared immediately.
-                        if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle");
+                        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "To many pending stops, forcing idle");
                         mStackSupervisor.scheduleIdleLocked();
                     } else {
                         mStackSupervisor.checkReadyForSleepLocked();
                     }
                 }
             } else {
-                if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev);
+                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "App died during pause, not stopping: " + prev);
                 prev = null;
             }
+            // It is possible the activity was freezing the screen before it was paused.
+            // In that case go ahead and remove the freeze this activity has on the screen
+            // since it is no longer visible.
+            prev.stopFreezingScreenLocked(true /*force*/);
             mPausingActivity = null;
         }
 
@@ -1112,7 +1115,7 @@
         }
     }
 
-    private void setVisibile(ActivityRecord r, boolean visible) {
+    private void setVisible(ActivityRecord r, boolean visible) {
         r.visible = visible;
         mWindowManager.setAppVisibility(r.appToken, visible);
         final ArrayList<ActivityContainer> containers = r.mChildContainers;
@@ -1242,10 +1245,15 @@
         if (top == null) {
             return;
         }
-        if (DEBUG_VISBILITY) Slog.v(
-                TAG, "ensureActivitiesVisible behind " + top
+        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+                "ensureActivitiesVisible behind " + top
                 + " configChanges=0x" + Integer.toHexString(configChanges));
 
+        if (DEBUG_STATES && starting != null && starting.task.stack == this) {
+            Slog.d(TAG, "ensureActivitiesVisibleLocked: starting=" + starting + " state="
+                    + starting.state + " fullscreen=" + starting.fullscreen + " top=" + top
+                    + " state=" + top.state + " fullscreen=" + top.fullscreen);
+        }
         if (mTranslucentActivityWaiting != top) {
             mUndrawnActivitiesBelowTopTranslucent.clear();
             if (mTranslucentActivityWaiting != null) {
@@ -1276,8 +1284,8 @@
                 // mLaunchingBehind: Activities launching behind are at the back of the task stack
                 // but must be drawn initially for the animation as though they were visible.
                 if (!behindFullscreen || r.mLaunchTaskBehind) {
-                    if (DEBUG_VISBILITY) Slog.v(
-                            TAG, "Make visible? " + r + " finishing=" + r.finishing
+                    if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+                            "Make visible? " + r + " finishing=" + r.finishing
                             + " state=" + r.state);
 
                     // First: if this is not the current activity being started, make
@@ -1290,14 +1298,15 @@
                         // This activity needs to be visible, but isn't even
                         // running...  get it started, but don't resume it
                         // at this point.
-                        if (DEBUG_VISBILITY) Slog.v(TAG, "Start and freeze screen for " + r);
+                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+                                "Start and freeze screen for " + r);
                         if (r != starting) {
                             r.startFreezingScreenLocked(r.app, configChanges);
                         }
                         if (!r.visible || r.mLaunchTaskBehind) {
-                            if (DEBUG_VISBILITY) Slog.v(
-                                    TAG, "Starting and making visible: " + r);
-                            setVisibile(r, true);
+                            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+                                    "Starting and making visible: " + r);
+                            setVisible(r, true);
                         }
                         if (r != starting) {
                             mStackSupervisor.startSpecificActivityLocked(r, false, false);
@@ -1306,7 +1315,8 @@
                     } else if (r.visible) {
                         // If this activity is already visible, then there is nothing
                         // else to do here.
-                        if (DEBUG_VISBILITY) Slog.v(TAG, "Skipping: already visible at " + r);
+                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+                                "Skipping: already visible at " + r);
                         r.stopFreezingScreenLocked(false);
                         try {
                             if (r.returningOptions != null) {
@@ -1322,14 +1332,14 @@
                         if (r.state != ActivityState.RESUMED && r != starting) {
                             // If this activity is paused, tell it
                             // to now show its window.
-                            if (DEBUG_VISBILITY) Slog.v(
-                                    TAG, "Making visible and scheduling visibility: " + r);
+                            if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+                                    "Making visible and scheduling visibility: " + r);
                             try {
                                 if (mTranslucentActivityWaiting != null) {
                                     r.updateOptionsLocked(r.returningOptions);
                                     mUndrawnActivitiesBelowTopTranslucent.add(r);
                                 }
-                                setVisibile(r, true);
+                                setVisible(r, true);
                                 r.sleeping = false;
                                 r.app.pendingUiClean = true;
                                 r.app.thread.scheduleWindowVisibility(r.appToken, true);
@@ -1348,29 +1358,28 @@
 
                     if (r.fullscreen) {
                         // At this point, nothing else needs to be shown
-                        if (DEBUG_VISBILITY) Slog.v(TAG, "Fullscreen: at " + r);
+                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r);
                         behindFullscreen = true;
                     } else if (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()) {
-                        if (DEBUG_VISBILITY) Slog.v(TAG, "Showing home: at " + r);
+                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Showing home: at " + r);
                         behindFullscreen = true;
                     }
                 } else {
-                    if (DEBUG_VISBILITY) Slog.v(
-                        TAG, "Make invisible? " + r + " finishing=" + r.finishing
-                        + " state=" + r.state
-                        + " behindFullscreen=" + behindFullscreen);
+                    if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+                        "Make invisible? " + r + " finishing=" + r.finishing
+                        + " state=" + r.state + " behindFullscreen=" + behindFullscreen);
                     // Now for any activities that aren't visible to the user, make
                     // sure they no longer are keeping the screen frozen.
                     if (r.visible) {
-                        if (DEBUG_VISBILITY) Slog.v(TAG, "Making invisible: " + r);
+                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r);
                         try {
-                            setVisibile(r, false);
+                            setVisible(r, false);
                             switch (r.state) {
                                 case STOPPING:
                                 case STOPPED:
                                     if (r.app != null && r.app.thread != null) {
-                                        if (DEBUG_VISBILITY) Slog.v(
-                                                TAG, "Scheduling invisibility: " + r);
+                                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+                                                "Scheduling invisibility: " + r);
                                         r.app.thread.scheduleWindowVisibility(r.appToken, false);
                                     }
                                     break;
@@ -1401,7 +1410,7 @@
                                     + r.intent.getComponent(), e);
                         }
                     } else {
-                        if (DEBUG_VISBILITY) Slog.v(TAG, "Already invisible: " + r);
+                        if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Already invisible: " + r);
                     }
                 }
             }
@@ -1471,7 +1480,8 @@
                 }
 
                 if (r.state == ActivityState.INITIALIZING && r.mStartingWindowShown) {
-                    if (DEBUG_VISBILITY) Slog.w(TAG, "Found orphaned starting window " + r);
+                    if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY,
+                            "Found orphaned starting window " + r);
                     r.mStartingWindowShown = false;
                     mWindowManager.removeAppStartingWindow(r.appToken);
                 }
@@ -1514,7 +1524,7 @@
     }
 
     private boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {
-        if (ActivityManagerService.DEBUG_LOCKSCREEN) mService.logLockScreen("");
+        if (DEBUG_LOCKSCREEN) mService.logLockScreen("");
 
         if (!mService.mBooting && !mService.mBooted) {
             // Not ready yet!
@@ -1632,12 +1642,12 @@
         next.sleeping = false;
         mStackSupervisor.mWaitingVisibleActivities.remove(next);
 
-        if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next);
+        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
 
         // If we are currently pausing an activity, then don't do anything
         // until that is done.
         if (!mStackSupervisor.allPausedActivitiesComplete()) {
-            if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG,
+            if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG_PAUSE,
                     "resumeTopActivityLocked: Skip resume: some activity pausing.");
             if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             return false;
@@ -1712,8 +1722,8 @@
             if (!mStackSupervisor.mWaitingVisibleActivities.contains(prev)
                     && next != null && !next.nowVisible) {
                 mStackSupervisor.mWaitingVisibleActivities.add(prev);
-                if (DEBUG_SWITCH) Slog.v(
-                        TAG, "Resuming top, waiting visible to hide: " + prev);
+                if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
+                        "Resuming top, waiting visible to hide: " + prev);
             } else {
                 // The next activity is already visible, so hide the previous
                 // activity's windows right now so we can show the new one ASAP.
@@ -1725,16 +1735,16 @@
                 // new one is found to be full-screen or not.
                 if (prev.finishing) {
                     mWindowManager.setAppVisibility(prev.appToken, false);
-                    if (DEBUG_SWITCH) Slog.v(TAG, "Not waiting for visible to hide: "
-                            + prev + ", waitingVisible="
+                    if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
+                            "Not waiting for visible to hide: " + prev + ", waitingVisible="
                             + mStackSupervisor.mWaitingVisibleActivities.contains(prev)
                             + ", nowVisible=" + next.nowVisible);
                 } else {
-                    if (DEBUG_SWITCH) Slog.v(TAG,
+                    if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
                             "Previous already visible but still waiting to hide: " + prev
-                                    + ", waitingVisible="
-                                    + mStackSupervisor.mWaitingVisibleActivities.contains(prev)
-                                    + ", nowVisible=" + next.nowVisible);
+                            + ", waitingVisible="
+                            + mStackSupervisor.mWaitingVisibleActivities.contains(prev)
+                            + ", nowVisible=" + next.nowVisible);
                 }
             }
         }
@@ -1756,7 +1766,7 @@
         boolean anim = true;
         if (prev != null) {
             if (prev.finishing) {
-                if (DEBUG_TRANSITION) Slog.v(TAG,
+                if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                         "Prepare close transition: prev=" + prev);
                 if (mNoAnimActivities.contains(prev)) {
                     anim = false;
@@ -1769,7 +1779,8 @@
                 mWindowManager.setAppWillBeHidden(prev.appToken);
                 mWindowManager.setAppVisibility(prev.appToken, false);
             } else {
-                if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare open transition: prev=" + prev);
+                if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
+                        "Prepare open transition: prev=" + prev);
                 if (mNoAnimActivities.contains(next)) {
                     anim = false;
                     mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
@@ -1786,7 +1797,7 @@
                 mWindowManager.setAppVisibility(prev.appToken, false);
             }
         } else {
-            if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare open transition: no previous");
+            if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
             if (mNoAnimActivities.contains(next)) {
                 anim = false;
                 mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
@@ -1808,7 +1819,7 @@
 
         ActivityStack lastStack = mStackSupervisor.getLastStack();
         if (next.app != null && next.app.thread != null) {
-            if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: " + next);
+            if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resume running: " + next);
 
             // This activity is now becoming visible.
             mWindowManager.setAppVisibility(next.appToken, true);
@@ -1873,9 +1884,8 @@
                 if (a != null) {
                     final int N = a.size();
                     if (!next.finishing && N > 0) {
-                        if (DEBUG_RESULTS) Slog.v(
-                                TAG, "Delivering results to " + next
-                                + ": " + a);
+                        if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
+                                "Delivering results to " + next + ": " + a);
                         next.app.thread.scheduleSendResult(next.appToken, a);
                     }
                 }
@@ -1952,7 +1962,7 @@
                             next.labelRes, next.icon, next.logo, next.windowFlags,
                             null, true);
                 }
-                if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next);
+                if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
             }
             if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Restarting " + next);
             mStackSupervisor.startSpecificActivityLocked(next, true, true);
@@ -2074,7 +2084,7 @@
         // activity
         if (task == r.task && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) {
             mStackSupervisor.mUserLeaving = false;
-            if (DEBUG_USER_LEAVING) Slog.v(TAG,
+            if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
                     "startActivity() behind front, mUserLeaving=false");
         }
 
@@ -2099,9 +2109,9 @@
             if (proc == null || proc.thread == null) {
                 showStartingIcon = true;
             }
-            if (DEBUG_TRANSITION) Slog.v(TAG,
+            if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                     "Prepare open transition: starting " + r);
-            if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+            if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
                 mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, keepCurTransition);
                 mNoAnimActivities.add(r);
             } else {
@@ -2271,13 +2281,13 @@
                     // same task affinity as the one we are moving,
                     // then merge it into the same task.
                     targetTask = bottom.task;
-                    if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+                    if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target
                             + " out to bottom task " + bottom.task);
                 } else {
                     targetTask = createTaskRecord(mStackSupervisor.getNextTaskId(), target.info,
                             null, null, null, false);
                     targetTask.affinityIntent = target.intent;
-                    if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
+                    if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target
                             + " out to new task " + target.task);
                 }
 
@@ -2302,8 +2312,8 @@
                     if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing activity " + p + " from task="
                             + task + " adding to task=" + targetTask
                             + " Callers=" + Debug.getCallers(4));
-                    if (DEBUG_TASKS) Slog.v(TAG, "Pushing next activity " + p
-                            + " out to target's task " + target.task);
+                    if (DEBUG_TASKS) Slog.v(TAG_TASKS,
+                            "Pushing next activity " + p + " out to target's task " + target.task);
                     p.setTask(targetTask, null);
                     targetTask.addActivityAtBottom(p);
 
@@ -2345,7 +2355,7 @@
                             noOptions = false;
                         }
                     }
-                    if (DEBUG_TASKS) Slog.w(TAG,
+                    if (DEBUG_TASKS) Slog.w(TAG_TASKS,
                             "resetTaskIntendedTask: calling finishActivity on " + p);
                     if (finishActivityLocked(
                             p, Activity.RESULT_CANCELED, null, "reset-task", false)) {
@@ -2419,7 +2429,8 @@
                 // in a task that is not currently on top.)
                 if (forceReset || finishOnTaskLaunch) {
                     final int start = replyChainEnd >= 0 ? replyChainEnd : i;
-                    if (DEBUG_TASKS) Slog.v(TAG, "Finishing task at index " + start + " to " + i);
+                    if (DEBUG_TASKS) Slog.v(TAG_TASKS,
+                            "Finishing task at index " + start + " to " + i);
                     for (int srcPos = start; srcPos >= i; --srcPos) {
                         final ActivityRecord p = activities.get(srcPos);
                         if (p.finishing) {
@@ -2435,8 +2446,9 @@
                     }
 
                     final int start = replyChainEnd >= 0 ? replyChainEnd : i;
-                    if (DEBUG_TASKS) Slog.v(TAG, "Reparenting from task=" + affinityTask + ":"
-                            + start + "-" + i + " to task=" + task + ":" + taskInsertionPoint);
+                    if (DEBUG_TASKS) Slog.v(TAG_TASKS,
+                            "Reparenting from task=" + affinityTask + ":" + start + "-" + i
+                            + " to task=" + task + ":" + taskInsertionPoint);
                     for (int srcPos = start; srcPos >= i; --srcPos) {
                         final ActivityRecord p = activities.get(srcPos);
                         p.setTask(task, null);
@@ -2445,8 +2457,8 @@
                         if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Removing and adding activity " + p
                                 + " to stack at " + task,
                                 new RuntimeException("here").fillInStackTrace());
-                        if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p + " from " + srcPos
-                                + " in to resetting task " + task);
+                        if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Pulling activity " + p
+                                + " from " + srcPos + " in to resetting task " + task);
                         mWindowManager.setAppTask(p.appToken, taskId);
                     }
                     mWindowManager.moveTaskToTop(taskId);
@@ -2604,7 +2616,7 @@
     }
 
     final void stopActivityLocked(ActivityRecord r) {
-        if (DEBUG_SWITCH) Slog.d(TAG, "Stopping: " + r);
+        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + r);
         if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
                 || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
             if (!r.finishing) {
@@ -2629,8 +2641,8 @@
                 if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPING: " + r
                         + " (stop requested)");
                 r.state = ActivityState.STOPPING;
-                if (DEBUG_VISBILITY) Slog.v(
-                        TAG, "Stopping visible=" + r.visible + " for " + r);
+                if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
+                        "Stopping visible=" + r.visible + " for " + r);
                 if (!r.visible) {
                     mWindowManager.setAppVisibility(r.appToken, false);
                 }
@@ -2767,7 +2779,7 @@
         // send the result
         ActivityRecord resultTo = r.resultTo;
         if (resultTo != null) {
-            if (DEBUG_RESULTS) Slog.v(TAG, "Adding result to " + resultTo
+            if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "Adding result to " + resultTo
                     + " who=" + r.resultWho + " req=" + r.requestCode
                     + " res=" + resultCode + " data=" + resultData);
             if (resultTo.userId != r.userId) {
@@ -2784,7 +2796,7 @@
                                      resultData);
             r.resultTo = null;
         }
-        else if (DEBUG_RESULTS) Slog.v(TAG, "No result destination from " + r);
+        else if (DEBUG_RESULTS) Slog.v(TAG_RESULTS, "No result destination from " + r);
 
         // Make sure this HistoryRecord is not holding on to other resources,
         // because clients have remote IPC references to this object so we
@@ -2832,7 +2844,7 @@
 
         if (mResumedActivity == r) {
             boolean endTask = index <= 0;
-            if (DEBUG_VISBILITY || DEBUG_TRANSITION) Slog.v(TAG,
+            if (DEBUG_VISIBILITY || DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                     "Prepare close transition: finishing " + r);
             mWindowManager.prepareAppTransition(endTask
                     ? AppTransition.TRANSIT_TASK_CLOSE
@@ -2842,8 +2854,9 @@
             mWindowManager.setAppVisibility(r.appToken, false);
 
             if (mPausingActivity == null) {
-                if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r);
-                if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false");
+                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish needs to pause: " + r);
+                if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
+                        "finish() => pause with userLeaving=false");
                 startPausingLocked(false, false, false, false);
             }
 
@@ -2853,10 +2866,10 @@
         } else if (r.state != ActivityState.PAUSING) {
             // If the activity is PAUSING, we will complete the finish once
             // it is done pausing; else we can just directly finish it here.
-            if (DEBUG_PAUSE) Slog.v(TAG, "Finish not pausing: " + r);
+            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish not pausing: " + r);
             return finishCurrentActivityLocked(r, FINISH_AFTER_PAUSE, oomAdj) == null;
         } else {
-            if (DEBUG_PAUSE) Slog.v(TAG, "Finish waiting for pause of: " + r);
+            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish waiting for pause of: " + r);
         }
 
         return false;
@@ -3151,7 +3164,7 @@
         }
         final TaskRecord task = r.task;
         if (task != null && task.removeActivity(r)) {
-            if (DEBUG_STACK) Slog.i(TAG,
+            if (DEBUG_STACK) Slog.i(TAG_STACK,
                     "removeActivityFromHistoryLocked: last activity removed from " + this);
             if (mStackSupervisor.isFrontStack(this) && task == topTask() &&
                     task.isOverHomeStack()) {
@@ -3204,7 +3217,7 @@
                     continue;
                 }
                 if (r.isDestroyable()) {
-                    if (DEBUG_SWITCH) Slog.v(TAG, "Destroying " + r + " in state " + r.state
+                    if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Destroying " + r + " in state " + r.state
                             + " resumed=" + mResumedActivity
                             + " pausing=" + mPausingActivity + " for reason " + reason);
                     if (destroyActivityLocked(r, true, reason)) {
@@ -3220,8 +3233,8 @@
 
     final boolean safelyDestroyActivityLocked(ActivityRecord r, String reason) {
         if (r.isDestroyable()) {
-            if (DEBUG_SWITCH) Slog.v(TAG, "Destroying " + r + " in state " + r.state
-                    + " resumed=" + mResumedActivity
+            if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
+                    "Destroying " + r + " in state " + r.state + " resumed=" + mResumedActivity
                     + " pausing=" + mPausingActivity + " for reason " + reason);
             return destroyActivityLocked(r, true, reason);
         }
@@ -3279,9 +3292,9 @@
      * but then create a new client-side object for this same HistoryRecord.
      */
     final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
-        if (DEBUG_SWITCH || DEBUG_CLEANUP) Slog.v(
-            TAG, "Removing activity from " + reason + ": token=" + r
-              + ", app=" + (r.app != null ? r.app.processName : "(null)"));
+        if (DEBUG_SWITCH || DEBUG_CLEANUP) Slog.v(TAG_SWITCH,
+                "Removing activity from " + reason + ": token=" + r
+                + ", app=" + (r.app != null ? r.app.processName : "(null)"));
         EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
                 r.userId, System.identityHashCode(r),
                 r.task.taskId, r.shortComponentName, reason);
@@ -3313,7 +3326,7 @@
             boolean skipDestroy = false;
 
             try {
-                if (DEBUG_SWITCH) Slog.i(TAG, "Destroying: " + r);
+                if (DEBUG_SWITCH) Slog.i(TAG_SWITCH, "Destroying: " + r);
                 r.app.thread.scheduleDestroyActivity(r.appToken, r.finishing,
                         r.configChangeFlags);
             } catch (Exception e) {
@@ -3483,6 +3496,9 @@
                 if (DEBUG_CLEANUP) Slog.v(TAG_CLEANUP,
                         "Record #" + i + " " + r + ": app=" + r.app);
                 if (r.app == app) {
+                    if (r.visible) {
+                        hasVisibleActivities = true;
+                    }
                     final boolean remove;
                     if ((!r.haveState && !r.stateNotNeeded) || r.finishing) {
                         // Don't currently have state for the activity, or
@@ -3522,9 +3538,6 @@
                         // it can be restarted later when needed.
                         if (DEBUG_ALL) Slog.v(
                             TAG, "Keeping entry, setting app to null");
-                        if (r.visible) {
-                            hasVisibleActivities = true;
-                        }
                         if (DEBUG_APP) Slog.v(TAG, "Clearing app during removeHistory for activity "
                                 + r);
                         r.app = null;
@@ -3575,8 +3588,8 @@
         for (int taskNdx = top; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
             if (task.taskType == homeStackTaskType) {
-                if (DEBUG_TASKS || DEBUG_STACK)
-                    Slog.d(TAG, "moveHomeStackTaskToTop: moving " + task);
+                if (DEBUG_TASKS || DEBUG_STACK) Slog.d(TAG_STACK,
+                        "moveHomeStackTaskToTop: moving " + task);
                 mTaskHistory.remove(taskNdx);
                 mTaskHistory.add(top, task);
                 updateTaskMovement(task, true);
@@ -3587,7 +3600,7 @@
 
     final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, Bundle options,
             String reason) {
-        if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
+        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
 
         final int numTasks = mTaskHistory.size();
         final int index = mTaskHistory.indexOf(tr);
@@ -3609,7 +3622,7 @@
         ActivityRecord r = topRunningActivityLocked(null);
         mService.setFocusedActivityLocked(r, reason);
 
-        if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare to front transition: task=" + tr);
+        if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
         if (noAnimation) {
             mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
             if (r != null) {
@@ -3673,7 +3686,7 @@
             }
         }
 
-        if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare to back transition: task=" + taskId);
+        if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task=" + taskId);
 
         boolean prevIsHome = false;
         if (tr.isOverHomeStack()) {
@@ -3896,16 +3909,15 @@
         return true;
     }
 
-    private boolean relaunchActivityLocked(ActivityRecord r,
-            int changes, boolean andResume) {
+    private boolean relaunchActivityLocked(ActivityRecord r, int changes, boolean andResume) {
         List<ResultInfo> results = null;
         List<ReferrerIntent> newIntents = null;
         if (andResume) {
             results = r.results;
             newIntents = r.newIntents;
         }
-        if (DEBUG_SWITCH) Slog.v(TAG, "Relaunching: " + r
-                + " with results=" + results + " newIntents=" + newIntents
+        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
+                "Relaunching: " + r + " with results=" + results + " newIntents=" + newIntents
                 + " andResume=" + andResume);
         EventLog.writeEvent(andResume ? EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY
                 : EventLogTags.AM_RELAUNCH_ACTIVITY, r.userId, System.identityHashCode(r),
@@ -3916,9 +3928,8 @@
         mStackSupervisor.removeChildActivityContainers(r);
 
         try {
-            if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG,
-                    (andResume ? "Relaunching to RESUMED " : "Relaunching to PAUSED ")
-                    + r);
+            if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH,
+                    "Moving to " + (andResume ? "RESUMED" : "PAUSED") + " Relaunching " + r);
             r.forceNewConfig = false;
             r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes,
                     !andResume, new Configuration(mService.mConfiguration),
@@ -3927,7 +3938,7 @@
             // the caller will only pass in 'andResume' if this activity is
             // currently resumed, which implies we aren't sleeping.
         } catch (RemoteException e) {
-            if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG, "Relaunch failed", e);
+            if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH, "Relaunch failed", e);
         }
 
         if (andResume) {
@@ -4080,8 +4091,7 @@
 
     public void unhandledBackLocked() {
         final int top = mTaskHistory.size() - 1;
-        if (DEBUG_SWITCH) Slog.d(
-            TAG, "Performing unhandledBack(): top activity at " + top);
+        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Performing unhandledBack(): top activity at " + top);
         if (top >= 0) {
             final ArrayList<ActivityRecord> activities = mTaskHistory.get(top).mActivities;
             int activityTop = activities.size() - 1;
@@ -4099,7 +4109,7 @@
      */
     boolean handleAppDiedLocked(ProcessRecord app) {
         if (mPausingActivity != null && mPausingActivity.app == app) {
-            if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG,
+            if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG_PAUSE,
                     "App died while pausing: " + mPausingActivity);
             mPausingActivity = null;
         }
@@ -4239,7 +4249,7 @@
         }
 
         if (mTaskHistory.isEmpty()) {
-            if (DEBUG_STACK) Slog.i(TAG, "removeTask: removing stack=" + this);
+            if (DEBUG_STACK) Slog.i(TAG_STACK, "removeTask: removing stack=" + this);
             final boolean notHomeStack = !isHomeStack();
             if (isOnHomeDisplay()) {
                 String myReason = reason + " leftTaskHistoryEmpty";
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index e9e6496..669dff8 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -23,17 +23,11 @@
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static com.android.server.am.ActivityManagerDebugConfig.*;
-import static com.android.server.am.ActivityManagerService.DEBUG_PAUSE;
-import static com.android.server.am.ActivityManagerService.DEBUG_RECENTS;
-import static com.android.server.am.ActivityManagerService.DEBUG_RESULTS;
-import static com.android.server.am.ActivityManagerService.DEBUG_STACK;
-import static com.android.server.am.ActivityManagerService.DEBUG_SWITCH;
-import static com.android.server.am.ActivityManagerService.DEBUG_TASKS;
-import static com.android.server.am.ActivityManagerService.DEBUG_USER_LEAVING;
 import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityStack.ActivityState.*;
 
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -120,6 +114,13 @@
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityStackSupervisor" : TAG_AM;
     private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
     private static final String TAG_FOCUS = TAG + POSTFIX_FOCUS;
+    private static final String TAG_PAUSE = TAG + POSTFIX_PAUSE;
+    private static final String TAG_RESULTS = TAG + POSTFIX_RESULTS;
+    private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
+    private static final String TAG_STACK = TAG + POSTFIX_STACK;
+    private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+    private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
+    private static final String TAG_USER_LEAVING = TAG + POSTFIX_USER_LEAVING;
 
     static final boolean DEBUG = DEBUG_ALL || false;
     static final boolean DEBUG_ADD_REMOVE = DEBUG || false;
@@ -129,7 +130,7 @@
     static final boolean DEBUG_RELEASE = DEBUG || false;
     static final boolean DEBUG_SAVED_STATE = DEBUG || false;
     static final boolean DEBUG_SCREENSHOTS = DEBUG || false;
-    static final boolean DEBUG_STATES = DEBUG || false;
+    static final boolean DEBUG_STATES = DEBUG || true;
     static final boolean DEBUG_VISIBLE_BEHIND = DEBUG || false;
 
     public static final int HOME_STACK_ID = 0;
@@ -273,8 +274,8 @@
      * until the task exits or #stopLockTaskMode() is called. */
     TaskRecord mLockTaskModeTask;
     /** Store the current lock task mode. Possible values:
-     * {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActicityManager#LOCK_TASK_MODE_LOCKED},
-     * {@link ActicityManager#LOCK_TASK_MODE_PINNED}
+     * {@link ActivityManager#LOCK_TASK_MODE_NONE}, {@link ActivityManager#LOCK_TASK_MODE_LOCKED},
+     * {@link ActivityManager#LOCK_TASK_MODE_PINNED}
      */
     private int mLockTaskModeState;
     /**
@@ -381,7 +382,7 @@
     }
 
     void notifyActivityDrawnForKeyguard() {
-        if (ActivityManagerService.DEBUG_LOCKSCREEN) mService.logLockScreen("");
+        if (DEBUG_LOCKSCREEN) mService.logLockScreen("");
         mWindowManager.notifyActivityDrawnForKeyguard();
     }
 
@@ -422,7 +423,7 @@
         // The home stack should either be at the top or bottom of the stack list.
         if ((toFront && (stacks.get(topNdx) != mHomeStack))
                 || (!toFront && (stacks.get(0) != mHomeStack))) {
-            if (DEBUG_STACK) Slog.d(TAG, "moveHomeTask: topStack old="
+            if (DEBUG_STACK) Slog.d(TAG_STACK, "moveHomeTask: topStack old="
                     + ((lastFocusedStack != null) ? lastFocusedStack : stacks.get(topNdx))
                     + " new=" + mFocusedStack);
             stacks.remove(mHomeStack);
@@ -510,10 +511,10 @@
         }
 
         // Don't give up! Look in recents.
-        if (DEBUG_RECENTS) Slog.v(TAG, "Looking for task id=" + id + " in recents");
+        if (DEBUG_RECENTS) Slog.v(TAG_RECENTS, "Looking for task id=" + id + " in recents");
         TaskRecord task = mRecentTasks.taskForIdLocked(id);
         if (task == null) {
-            if (DEBUG_RECENTS) Slog.d(TAG, "\tDidn't find task id=" + id + " in recents");
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "\tDidn't find task id=" + id + " in recents");
             return null;
         }
 
@@ -522,10 +523,11 @@
         }
 
         if (!restoreRecentTaskLocked(task)) {
-            if (DEBUG_RECENTS) Slog.w(TAG, "Couldn't restore task id=" + id + " found in recents");
+            if (DEBUG_RECENTS) Slog.w(TAG_RECENTS,
+                    "Couldn't restore task id=" + id + " found in recents");
             return null;
         }
-        if (DEBUG_RECENTS) Slog.w(TAG, "Restored task id=" + id + " from in recents");
+        if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Restored task id=" + id + " from in recents");
         return task;
     }
 
@@ -633,14 +635,14 @@
                 final ActivityStack stack = stacks.get(stackNdx);
                 if (isFrontStack(stack)) {
                     final ActivityRecord r = stack.mResumedActivity;
-                    if (r != null && r.state != ActivityState.RESUMED) {
+                    if (r != null && r.state != RESUMED) {
                         return false;
                     }
                 }
             }
         }
         // TODO: Not sure if this should check if all Paused are complete too.
-        if (DEBUG_STACK) Slog.d(TAG,
+        if (DEBUG_STACK) Slog.d(TAG_STACK,
                 "allResumedActivitiesComplete: mLastFocusedStack changing from=" +
                 mLastFocusedStack + " to=" + mFocusedStack);
         mLastFocusedStack = mFocusedStack;
@@ -648,17 +650,21 @@
     }
 
     boolean allResumedActivitiesVisible() {
+        boolean foundResumed = false;
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
             for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = stacks.get(stackNdx);
                 final ActivityRecord r = stack.mResumedActivity;
-                if (r != null && (!r.nowVisible || mWaitingVisibleActivities.contains(r))) {
-                    return false;
+                if (r != null) {
+                    if (!r.nowVisible || mWaitingVisibleActivities.contains(r)) {
+                        return false;
+                    }
+                    foundResumed = true;
                 }
             }
         }
-        return true;
+        return foundResumed;
     }
 
     /**
@@ -690,9 +696,7 @@
             for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = stacks.get(stackNdx);
                 final ActivityRecord r = stack.mPausingActivity;
-                if (r != null && r.state != ActivityState.PAUSED
-                        && r.state != ActivityState.STOPPED
-                        && r.state != ActivityState.STOPPING) {
+                if (r != null && r.state != PAUSED && r.state != STOPPED && r.state != STOPPING) {
                     if (DEBUG_STATES) {
                         Slog.d(TAG, "allPausedActivitiesComplete: r=" + r + " state=" + r.state);
                         pausing = false;
@@ -897,7 +901,7 @@
         ActivityContainer container = (ActivityContainer)iContainer;
         synchronized (mService) {
             if (container != null && container.mParentActivity != null &&
-                    container.mParentActivity.state != ActivityState.RESUMED) {
+                    container.mParentActivity.state != RESUMED) {
                 // Cannot start a child activity if the parent is not resumed.
                 return ActivityManager.START_CANCELED;
             }
@@ -1027,7 +1031,7 @@
                     } while (!outResult.timeout && outResult.who == null);
                 } else if (res == ActivityManager.START_TASK_TO_FRONT) {
                     ActivityRecord r = stack.topRunningActivityLocked(null);
-                    if (r.nowVisible && r.state == ActivityState.RESUMED) {
+                    if (r.nowVisible && r.state == RESUMED) {
                         outResult.timeout = false;
                         outResult.who = new ComponentName(r.info.packageName, r.info.name);
                         outResult.totalTime = 0;
@@ -1132,12 +1136,13 @@
             ProcessRecord app, boolean andResume, boolean checkConfig)
             throws RemoteException {
 
-        r.startFreezingScreenLocked(app, 0);
-        if (false) Slog.d(TAG, "realStartActivity: setting app visibility true");
-        mWindowManager.setAppVisibility(r.appToken, true);
+        if (andResume) {
+            r.startFreezingScreenLocked(app, 0);
+            mWindowManager.setAppVisibility(r.appToken, true);
 
-        // schedule launch ticks to collect information about slow apps.
-        r.startLaunchTickingLocked();
+            // schedule launch ticks to collect information about slow apps.
+            r.startLaunchTickingLocked();
+        }
 
         // Have the window manager re-evaluate the orientation of
         // the screen based on the new activity order.  Note that
@@ -1177,10 +1182,9 @@
                 results = r.results;
                 newIntents = r.newIntents;
             }
-            if (DEBUG_SWITCH) Slog.v(TAG, "Launching: " + r
-                    + " icicle=" + r.icicle
-                    + " with results=" + results + " newIntents=" + newIntents
-                    + " andResume=" + andResume);
+            if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
+                    "Launching: " + r + " icicle=" + r.icicle + " with results=" + results
+                    + " newIntents=" + newIntents + " andResume=" + andResume);
             if (andResume) {
                 EventLog.writeEvent(EventLogTags.AM_RESTART_ACTIVITY,
                         r.userId, System.identityHashCode(r),
@@ -1195,34 +1199,37 @@
             r.forceNewConfig = false;
             mService.showAskCompatModeDialogLocked(r);
             r.compat = mService.compatibilityInfoForPackageLocked(r.info.applicationInfo);
-            String profileFile = null;
-            ParcelFileDescriptor profileFd = null;
+            ProfilerInfo profilerInfo = null;
             if (mService.mProfileApp != null && mService.mProfileApp.equals(app.processName)) {
                 if (mService.mProfileProc == null || mService.mProfileProc == app) {
                     mService.mProfileProc = app;
-                    profileFile = mService.mProfileFile;
-                    profileFd = mService.mProfileFd;
-                }
-            }
-            app.hasShownUi = true;
-            app.pendingUiClean = true;
-            if (profileFd != null) {
-                try {
-                    profileFd = profileFd.dup();
-                } catch (IOException e) {
-                    if (profileFd != null) {
-                        try {
-                            profileFd.close();
-                        } catch (IOException o) {
+                    final String profileFile = mService.mProfileFile;
+                    if (profileFile != null) {
+                        ParcelFileDescriptor profileFd = mService.mProfileFd;
+                        if (profileFd != null) {
+                            try {
+                                profileFd = profileFd.dup();
+                            } catch (IOException e) {
+                                if (profileFd != null) {
+                                    try {
+                                        profileFd.close();
+                                    } catch (IOException o) {
+                                    }
+                                    profileFd = null;
+                                }
+                            }
                         }
-                        profileFd = null;
+
+                        profilerInfo = new ProfilerInfo(profileFile, profileFd,
+                                mService.mSamplingInterval, mService.mAutoStopProfiler);
                     }
                 }
             }
 
-            ProfilerInfo profilerInfo = profileFile != null
-                    ? new ProfilerInfo(profileFile, profileFd, mService.mSamplingInterval,
-                    mService.mAutoStopProfiler) : null;
+            if (andResume) {
+                app.hasShownUi = true;
+                app.pendingUiClean = true;
+            }
             app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP);
             app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                     System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
@@ -1286,7 +1293,7 @@
             // other state.
             if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPED: " + r
                     + " (starting in stopped state)");
-            r.state = ActivityState.STOPPED;
+            r.state = STOPPED;
             r.stopped = true;
         }
 
@@ -1377,8 +1384,8 @@
         ActivityRecord resultRecord = null;
         if (resultTo != null) {
             sourceRecord = isInAnyStackLocked(resultTo);
-            if (DEBUG_RESULTS) Slog.v(
-                TAG, "Will send result to " + resultTo + " " + sourceRecord);
+            if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
+                    "Will send result to " + resultTo + " " + sourceRecord);
             if (sourceRecord != null) {
                 if (requestCode >= 0 && !sourceRecord.finishing) {
                     resultRecord = sourceRecord;
@@ -1722,7 +1729,8 @@
         // We'll invoke onUserLeaving before onPause only if the launching
         // activity did not explicitly state that this is an automated launch.
         mUserLeaving = (launchFlags & Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
-        if (DEBUG_USER_LEAVING) Slog.v(TAG, "startActivity() => mUserLeaving=" + mUserLeaving);
+        if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
+                "startActivity() => mUserLeaving=" + mUserLeaving);
 
         // If the caller has asked not to resume at this point, we make note
         // of this in the record so that we can skip it when trying to find
@@ -1929,7 +1937,7 @@
                         }
                     }
                     if (!movedToFront) {
-                        if (DEBUG_TASKS) Slog.d(TAG, "Bring to front target: " + targetStack
+                        if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Bring to front target: " + targetStack
                                 + " from " + intentActivity);
                         targetStack.moveToFront("intentActivityFound");
                     }
@@ -2135,8 +2143,8 @@
                         newTaskIntent != null ? newTaskIntent : intent,
                         voiceSession, voiceInteractor, !launchTaskBehind /* toTop */),
                         taskToAffiliate);
-                if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " +
-                        r.task);
+                if (DEBUG_TASKS) Slog.v(TAG_TASKS,
+                        "Starting new activity " + r + " in new task " + r.task);
             } else {
                 r.setTask(reuseTask, taskToAffiliate);
             }
@@ -2203,7 +2211,7 @@
             // to keep the new one in the same task as the one that is starting
             // it.
             r.setTask(sourceTask, null);
-            if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r
                     + " in existing task " + r.task + " from source " + sourceRecord);
 
         } else if (inTask != null) {
@@ -2242,7 +2250,7 @@
             }
 
             r.setTask(inTask, null);
-            if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r
                     + " in explicit task " + r.task);
 
         } else {
@@ -2255,7 +2263,7 @@
             r.setTask(prev != null ? prev.task : targetStack.createTaskRecord(getNextTaskId(),
                             r.info, intent, null, null, true), null);
             mWindowManager.moveTaskToTop(r.task.taskId);
-            if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
+            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + r
                     + " in new guessed " + r.task);
         }
 
@@ -2595,8 +2603,8 @@
             return;
         }
         task.stack.moveTaskToFrontLocked(task, false /* noAnimation */, options, reason);
-        if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack="
-                + task.stack);
+        if (DEBUG_STACK) Slog.d(TAG_STACK,
+                "findTaskToMoveToFront: moved to front of stack=" + task.stack);
     }
 
     ActivityStack getStack(int stackId) {
@@ -2789,20 +2797,20 @@
             stack = createStackOnDisplay(getNextStackId(), Display.DEFAULT_DISPLAY);
             // Restore home stack to top.
             moveHomeStack(true, "restoreRecentTask");
-            if (DEBUG_RECENTS)
-                Slog.v(TAG, "Created stack=" + stack + " for recents restoration.");
+            if (DEBUG_RECENTS) Slog.v(TAG_RECENTS,
+                    "Created stack=" + stack + " for recents restoration.");
         }
 
         if (stack == null) {
             // What does this mean??? Not sure how we would get here...
-            if (DEBUG_RECENTS)
-                Slog.v(TAG, "Unable to find/create stack to restore recent task=" + task);
+            if (DEBUG_RECENTS) Slog.v(TAG_RECENTS,
+                    "Unable to find/create stack to restore recent task=" + task);
             return false;
         }
 
         stack.addTask(task, false, false);
-        if (DEBUG_RECENTS)
-            Slog.v(TAG, "Added restored task=" + task + " to stack=" + stack);
+        if (DEBUG_RECENTS) Slog.v(TAG_RECENTS,
+                "Added restored task=" + task + " to stack=" + stack);
         final ArrayList<ActivityRecord> activities = task.mActivities;
         for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
             final ActivityRecord r = activities.get(activityNdx);
@@ -2835,18 +2843,18 @@
     }
 
     ActivityRecord findTaskLocked(ActivityRecord r) {
-        if (DEBUG_TASKS) Slog.d(TAG, "Looking for task of " + r);
+        if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Looking for task of " + r);
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
             for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = stacks.get(stackNdx);
                 if (!r.isApplicationActivity() && !stack.isHomeStack()) {
-                    if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: (home activity) " + stack);
+                    if (DEBUG_TASKS) Slog.d(TAG_TASKS, "Skipping stack: (home activity) " + stack);
                     continue;
                 }
                 if (!stack.mActivityContainer.isEligibleForNewTasks()) {
-                    if (DEBUG_TASKS) Slog.d(TAG, "Skipping stack: (new task not allowed) " +
-                            stack);
+                    if (DEBUG_TASKS) Slog.d(TAG_TASKS,
+                            "Skipping stack: (new task not allowed) " + stack);
                     continue;
                 }
                 final ActivityRecord ar = stack.findTaskLocked(r);
@@ -2855,7 +2863,7 @@
                 }
             }
         }
-        if (DEBUG_TASKS) Slog.d(TAG, "No task found");
+        if (DEBUG_TASKS) Slog.d(TAG_TASKS, "No task found");
         return null;
     }
 
@@ -2964,7 +2972,7 @@
 
             if (mStoppingActivities.size() > 0) {
                 // Still need to tell some activities to stop; can't sleep yet.
-                if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still need to stop "
+                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to stop "
                         + mStoppingActivities.size() + " activities");
                 scheduleIdleLocked();
                 dontSleep = true;
@@ -2972,7 +2980,7 @@
 
             if (mGoingToSleepActivities.size() > 0) {
                 // Still need to tell some activities to sleep; can't sleep yet.
-                if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still need to sleep "
+                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still need to sleep "
                         + mGoingToSleepActivities.size() + " activities");
                 dontSleep = true;
             }
@@ -3123,16 +3131,14 @@
             // First, if we find an activity that is in the process of being destroyed,
             // then we just aren't going to do anything for now; we want things to settle
             // down before we try to prune more activities.
-            if (r.finishing || r.state == ActivityState.DESTROYING
-                    || r.state == ActivityState.DESTROYED) {
+            if (r.finishing || r.state == DESTROYING || r.state == DESTROYED) {
                 if (DEBUG_RELEASE) Slog.d(TAG, "Abort release; already destroying: " + r);
                 return;
             }
             // Don't consider any activies that are currently not in a state where they
             // can be destroyed.
-            if (r.visible || !r.stopped || !r.haveState
-                    || r.state == ActivityState.RESUMED || r.state == ActivityState.PAUSING
-                    || r.state == ActivityState.PAUSED || r.state == ActivityState.STOPPING) {
+            if (r.visible || !r.stopped || !r.haveState || r.state == RESUMED || r.state == PAUSING
+                    || r.state == PAUSED || r.state == STOPPING) {
                 if (DEBUG_RELEASE) Slog.d(TAG, "Not releasing in-use activity: " + r);
                 continue;
             }
@@ -3250,39 +3256,34 @@
     }
 
     void validateTopActivitiesLocked() {
-        // FIXME
-/*        for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = stacks.get(stackNdx);
-            final ActivityRecord r = stack.topRunningActivityLocked(null);
-            final ActivityState state = r == null ? ActivityState.DESTROYED : r.state;
-            if (isFrontStack(stack)) {
-                if (r == null) {
-                    Slog.e(TAG, "validateTop...: null top activity, stack=" + stack);
+        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+            final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
+            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = stacks.get(stackNdx);
+                final ActivityRecord r = stack.topRunningActivityLocked(null);
+                final ActivityState state = r == null ? DESTROYED : r.state;
+                if (isFrontStack(stack)) {
+                    if (r == null) Slog.e(TAG,
+                            "validateTop...: null top activity, stack=" + stack);
+                    else {
+                        final ActivityRecord pausing = stack.mPausingActivity;
+                        if (pausing != null && pausing == r) Slog.e(TAG,
+                                "validateTop...: top stack has pausing activity r=" + r
+                                + " state=" + state);
+                        if (state != INITIALIZING && state != RESUMED) Slog.e(TAG,
+                                "validateTop...: activity in front not resumed r=" + r
+                                + " state=" + state);
+                    }
                 } else {
-                    final ActivityRecord pausing = stack.mPausingActivity;
-                    if (pausing != null && pausing == r) {
-                        Slog.e(TAG, "validateTop...: top stack has pausing activity r=" + r +
-                            " state=" + state);
-                    }
-                    if (state != ActivityState.INITIALIZING && state != ActivityState.RESUMED) {
-                        Slog.e(TAG, "validateTop...: activity in front not resumed r=" + r +
-                                " state=" + state);
-                    }
-                }
-            } else {
-                final ActivityRecord resumed = stack.mResumedActivity;
-                if (resumed != null && resumed == r) {
-                    Slog.e(TAG, "validateTop...: back stack has resumed activity r=" + r +
-                        " state=" + state);
-                }
-                if (r != null && (state == ActivityState.INITIALIZING
-                        || state == ActivityState.RESUMED)) {
-                    Slog.e(TAG, "validateTop...: activity in back resumed r=" + r +
-                            " state=" + state);
+                    final ActivityRecord resumed = stack.mResumedActivity;
+                    if (resumed != null && resumed == r) Slog.e(TAG,
+                            "validateTop...: back stack has resumed activity r=" + r
+                            + " state=" + state);
+                    if (r != null && (state == INITIALIZING || state == RESUMED)) Slog.e(TAG,
+                            "validateTop...: activity in back resumed r=" + r + " state=" + state);
                 }
             }
         }
-*/
     }
 
     public void dump(PrintWriter pw, String prefix) {
@@ -3508,7 +3509,7 @@
         mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_CHANGED, displayId, 0));
     }
 
-    public void handleDisplayAddedLocked(int displayId) {
+    private void handleDisplayAdded(int displayId) {
         boolean newDisplay;
         synchronized (mService) {
             newDisplay = mActivityDisplays.get(displayId) == null;
@@ -3526,7 +3527,7 @@
         }
     }
 
-    public void handleDisplayRemovedLocked(int displayId) {
+    private void handleDisplayRemoved(int displayId) {
         synchronized (mService) {
             ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
             if (activityDisplay != null) {
@@ -3540,7 +3541,7 @@
         mWindowManager.onDisplayRemoved(displayId);
     }
 
-    public void handleDisplayChangedLocked(int displayId) {
+    private void handleDisplayChanged(int displayId) {
         synchronized (mService) {
             ActivityDisplay activityDisplay = mActivityDisplays.get(displayId);
             if (activityDisplay != null) {
@@ -3550,7 +3551,7 @@
         mWindowManager.onDisplayChanged(displayId);
     }
 
-    StackInfo getStackInfo(ActivityStack stack) {
+    private StackInfo getStackInfoLocked(ActivityStack stack) {
         StackInfo info = new StackInfo();
         mWindowManager.getStackBounds(stack.mStackId, info.bounds);
         info.displayId = Display.DEFAULT_DISPLAY;
@@ -3576,7 +3577,7 @@
     StackInfo getStackInfoLocked(int stackId) {
         ActivityStack stack = getStack(stackId);
         if (stack != null) {
-            return getStackInfo(stack);
+            return getStackInfoLocked(stack);
         }
         return null;
     }
@@ -3586,7 +3587,7 @@
         for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
             ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
             for (int ndx = stacks.size() - 1; ndx >= 0; --ndx) {
-                list.add(getStackInfo(stacks.get(ndx)));
+                list.add(getStackInfoLocked(stacks.get(ndx)));
             }
         }
         return list;
@@ -3706,13 +3707,13 @@
                     }
                 } break;
                 case HANDLE_DISPLAY_ADDED: {
-                    handleDisplayAddedLocked(msg.arg1);
+                    handleDisplayAdded(msg.arg1);
                 } break;
                 case HANDLE_DISPLAY_CHANGED: {
-                    handleDisplayChangedLocked(msg.arg1);
+                    handleDisplayChanged(msg.arg1);
                 } break;
                 case HANDLE_DISPLAY_REMOVED: {
-                    handleDisplayRemovedLocked(msg.arg1);
+                    handleDisplayRemoved(msg.arg1);
                 } break;
                 case CONTAINER_CALLBACK_VISIBILITY: {
                     final ActivityContainer container = (ActivityContainer) msg.obj;
@@ -3847,12 +3848,12 @@
                 mStackId = stackId;
                 mStack = new ActivityStack(this, mRecentTasks);
                 mIdString = "ActivtyContainer{" + mStackId + "}";
-                if (DEBUG_STACK) Slog.d(TAG, "Creating " + this);
+                if (DEBUG_STACK) Slog.d(TAG_STACK, "Creating " + this);
             }
         }
 
         void attachToDisplayLocked(ActivityDisplay activityDisplay) {
-            if (DEBUG_STACK) Slog.d(TAG, "attachToDisplayLocked: " + this
+            if (DEBUG_STACK) Slog.d(TAG_STACK, "attachToDisplayLocked: " + this
                     + " to display=" + activityDisplay);
             mActivityDisplay = activityDisplay;
             mStack.mDisplayId = activityDisplay.mDisplayId;
@@ -3930,7 +3931,7 @@
         }
 
         protected void detachLocked() {
-            if (DEBUG_STACK) Slog.d(TAG, "detachLocked: " + this + " from display="
+            if (DEBUG_STACK) Slog.d(TAG_STACK, "detachLocked: " + this + " from display="
                     + mActivityDisplay + " Callers=" + Debug.getCallers(2));
             if (mActivityDisplay != null) {
                 mActivityDisplay.detachActivitiesLocked(mStack);
@@ -4107,8 +4108,8 @@
 
             setSurfaceIfReadyLocked();
 
-            if (DEBUG_STACK) Slog.d(TAG, "setSurface: " + this + " to display="
-                    + virtualActivityDisplay);
+            if (DEBUG_STACK) Slog.d(TAG_STACK,
+                    "setSurface: " + this + " to display=" + virtualActivityDisplay);
         }
 
         @Override
@@ -4131,7 +4132,7 @@
         }
 
         private void setSurfaceIfReadyLocked() {
-            if (DEBUG_STACK) Slog.v(TAG, "setSurfaceIfReadyLocked: mDrawn=" + mDrawn +
+            if (DEBUG_STACK) Slog.v(TAG_STACK, "setSurfaceIfReadyLocked: mDrawn=" + mDrawn +
                     " mContainerState=" + mContainerState + " mSurface=" + mSurface);
             if (mDrawn && mSurface != null && mContainerState == CONTAINER_STATE_NO_SURFACE) {
                 ((VirtualActivityDisplay) mActivityDisplay).setSurface(mSurface);
@@ -4174,13 +4175,13 @@
         }
 
         void attachActivities(ActivityStack stack) {
-            if (DEBUG_STACK) Slog.v(TAG, "attachActivities: attaching " + stack + " to displayId="
-                    + mDisplayId);
+            if (DEBUG_STACK) Slog.v(TAG_STACK,
+                    "attachActivities: attaching " + stack + " to displayId=" + mDisplayId);
             mStacks.add(stack);
         }
 
         void detachActivitiesLocked(ActivityStack stack) {
-            if (DEBUG_STACK) Slog.v(TAG, "detachActivitiesLocked: detaching " + stack
+            if (DEBUG_STACK) Slog.v(TAG_STACK, "detachActivitiesLocked: detaching " + stack
                     + " from displayId=" + mDisplayId);
             mStacks.remove(stack);
         }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 197b51d..c8db3be 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -16,20 +16,26 @@
 
 package com.android.server.am;
 
+import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothHeadset;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.net.wifi.IWifiManager;
+import android.net.wifi.WifiActivityEnergyInfo;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.PowerManagerInternal;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -38,10 +44,12 @@
 import android.telephony.TelephonyManager;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.os.BatteryStatsHelper;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.PowerProfile;
+import com.android.server.FgThread;
 import com.android.server.LocalServices;
 
 import java.io.File;
@@ -59,15 +67,52 @@
     static final String TAG = "BatteryStatsService";
 
     static IBatteryStats sService;
-    
     final BatteryStatsImpl mStats;
+    final BatteryStatsHandler mHandler;
     Context mContext;
     private boolean mBluetoothPendingStats;
     private BluetoothHeadset mBluetoothHeadset;
     PowerManagerInternal mPowerManagerInternal;
 
+    class BatteryStatsHandler extends Handler implements BatteryStatsImpl.ExternalStatsSync {
+        public static final int MSG_SYNC_EXTERNAL_STATS = 1;
+        public static final int MSG_WRITE_TO_DISK = 2;
+
+        public BatteryStatsHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SYNC_EXTERNAL_STATS:
+                    updateExternalStats();
+                    break;
+
+                case MSG_WRITE_TO_DISK:
+                    updateExternalStats();
+                    synchronized (mStats) {
+                        mStats.writeAsyncLocked();
+                    }
+                    break;
+            }
+        }
+
+        @Override
+        public void scheduleSync() {
+            if (!hasMessages(MSG_SYNC_EXTERNAL_STATS)) {
+                sendEmptyMessage(MSG_SYNC_EXTERNAL_STATS);
+            }
+        }
+    }
+
     BatteryStatsService(File systemDir, Handler handler) {
-        mStats = new BatteryStatsImpl(systemDir, handler);
+        // Our handler here will be accessing the disk, use a different thread than
+        // what the ActivityManagerService gave us (no I/O on that one!).
+        mHandler = new BatteryStatsHandler(FgThread.getHandler().getLooper());
+
+        // BatteryStatsImpl expects the ActivityManagerService handler, so pass that one through.
+        mStats = new BatteryStatsImpl(systemDir, handler, mHandler);
     }
     
     public void publish(Context context) {
@@ -92,6 +137,8 @@
 
     public void shutdown() {
         Slog.w("BatteryStats", "Writing battery stats before shutdown...");
+
+        updateExternalStats();
         synchronized (mStats) {
             mStats.shutdownLocked();
         }
@@ -122,6 +169,14 @@
         return mStats;
     }
 
+    /**
+     * Schedules a write to disk to occur. This will cause the BatteryStatsImpl
+     * object to update with the latest info, then write to disk.
+     */
+    public void scheduleWriteToDisk() {
+        mHandler.sendEmptyMessage(BatteryStatsHandler.MSG_WRITE_TO_DISK);
+    }
+
     // These are for direct use by the activity manager...
 
     void addIsolatedUid(int isolatedUid, int appUid) {
@@ -174,7 +229,10 @@
         //Slog.i("foo", "SENDING BATTERY INFO:");
         //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
         Parcel out = Parcel.obtain();
-        mStats.writeToParcel(out, 0);
+        updateExternalStats();
+        synchronized (mStats) {
+            mStats.writeToParcel(out, 0);
+        }
         byte[] data = out.marshall();
         out.recycle();
         return data;
@@ -186,7 +244,10 @@
         //Slog.i("foo", "SENDING BATTERY INFO:");
         //mStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
         Parcel out = Parcel.obtain();
-        mStats.writeToParcel(out, 0);
+        updateExternalStats();
+        synchronized (mStats) {
+            mStats.writeToParcel(out, 0);
+        }
         byte[] data = out.marshall();
         out.recycle();
         try {
@@ -663,6 +724,7 @@
         }
     }
 
+    @Override
     public void noteWifiMulticastDisabledFromSource(WorkSource ws) {
         enforceCallingPermission();
         synchronized (mStats) {
@@ -671,10 +733,10 @@
     }
 
     @Override
-    public void noteNetworkInterfaceType(String iface, int type) {
+    public void noteNetworkInterfaceType(String iface, int networkType) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.noteNetworkInterfaceTypeLocked(iface, type);
+            mStats.noteNetworkInterfaceTypeLocked(iface, networkType);
         }
     }
 
@@ -715,7 +777,22 @@
     public void setBatteryState(int status, int health, int plugType, int level,
             int temp, int volt) {
         enforceCallingPermission();
-        mStats.setBatteryState(status, health, plugType, level, temp, volt);
+        synchronized (mStats) {
+            final boolean onBattery = plugType == BatteryStatsImpl.BATTERY_PLUGGED_NONE;
+            if (mStats.isOnBattery() == onBattery) {
+                // The battery state has not changed, so we don't need to sync external
+                // stats immediately.
+                mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt);
+                return;
+            }
+        }
+
+        // Sync external stats first as the battery has changed states. If we don't sync
+        // immediately here, we may not collect the relevant data later.
+        updateExternalStats();
+        synchronized (mStats) {
+            mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt);
+        }
     }
     
     public long getAwakeTimeBattery() {
@@ -772,12 +849,11 @@
 
     private void dumpHelp(PrintWriter pw) {
         pw.println("Battery stats (batterystats) dump options:");
-        pw.println("  [--checkin] [--history] [--history-start] [--unplugged] [--charged] [-c]");
+        pw.println("  [--checkin] [--history] [--history-start] [--charged] [-c]");
         pw.println("  [--daily] [--reset] [--write] [--new-daily] [--read-daily] [-h] [<package.name>]");
         pw.println("  --checkin: format output for a checkin report.");
         pw.println("  --history: show only history data.");
         pw.println("  --history-start <num>: show only history data starting at given time offset.");
-        pw.println("  --unplugged: only output data since last unplugged.");
         pw.println("  --charged: only output data since last charged.");
         pw.println("  --daily: only output full daily data.");
         pw.println("  --reset: reset the stats, clearing all current data.");
@@ -818,6 +894,7 @@
         return i;
     }
 
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -856,8 +933,6 @@
                 } else if ("-c".equals(arg)) {
                     useCheckinFormat = true;
                     flags |= BatteryStats.DUMP_INCLUDE_HISTORY;
-                } else if ("--unplugged".equals(arg)) {
-                    flags |= BatteryStats.DUMP_UNPLUGGED_ONLY;
                 } else if ("--charged".equals(arg)) {
                     flags |= BatteryStats.DUMP_CHARGED_ONLY;
                 } else if ("--daily".equals(arg)) {
@@ -868,7 +943,9 @@
                         pw.println("Battery stats reset.");
                         noOutput = true;
                     }
+                    updateExternalStats();
                 } else if ("--write".equals(arg)) {
+                    updateExternalStats();
                     synchronized (mStats) {
                         mStats.writeSyncLocked();
                         pw.println("Battery stats written.");
@@ -931,13 +1008,16 @@
         if (reqUid >= 0) {
             // By default, if the caller is only interested in a specific package, then
             // we only dump the aggregated data since charged.
-            if ((flags&(BatteryStats.DUMP_HISTORY_ONLY|BatteryStats.DUMP_UNPLUGGED_ONLY
-                    |BatteryStats.DUMP_CHARGED_ONLY)) == 0) {
+            if ((flags&(BatteryStats.DUMP_HISTORY_ONLY|BatteryStats.DUMP_CHARGED_ONLY)) == 0) {
                 flags |= BatteryStats.DUMP_CHARGED_ONLY;
                 // Also if they are doing -c, we don't want history.
                 flags &= ~BatteryStats.DUMP_INCLUDE_HISTORY;
             }
         }
+
+        // Fetch data from external sources and update the BatteryStatsImpl object with them.
+        updateExternalStats();
+
         if (useCheckinFormat) {
             List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
             if (isRealCheckin) {
@@ -952,7 +1032,7 @@
                                 in.unmarshall(raw, 0, raw.length);
                                 in.setDataPosition(0);
                                 BatteryStatsImpl checkinStats = new BatteryStatsImpl(
-                                        null, mStats.mHandler);
+                                        null, mStats.mHandler, null);
                                 checkinStats.readSummaryFromParcel(in);
                                 in.recycle();
                                 checkinStats.dumpCheckinLocked(mContext, pw, apps, flags,
@@ -982,4 +1062,85 @@
             }
         }
     }
+
+    // Objects for extracting data from external sources.
+    private final Object mExternalStatsLock = new Object();
+
+    @GuardedBy("mExternalStatsLock")
+    private IWifiManager mWifiManager;
+
+    // WiFi keeps an accumulated total of stats, unlike Bluetooth.
+    // Keep the last WiFi stats so we can compute a delta.
+    @GuardedBy("mExternalStatsLock")
+    private WifiActivityEnergyInfo mLastInfo = new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0);
+
+    @GuardedBy("mExternalStatsLock")
+    private WifiActivityEnergyInfo pullWifiEnergyInfoLocked() {
+        if (mWifiManager == null) {
+            mWifiManager = IWifiManager.Stub.asInterface(
+                    ServiceManager.getService(Context.WIFI_SERVICE));
+            if (mWifiManager == null) {
+                return null;
+            }
+        }
+
+        try {
+            // We read the data even if we are not on battery. This is so that we keep the
+            // correct delta from when we should start reading (aka when we are on battery).
+            WifiActivityEnergyInfo info = mWifiManager.reportActivityInfo();
+            if (info != null && info.isValid()) {
+                // We will modify the last info object to be the delta, and store the new
+                // WifiActivityEnergyInfo object as our last one.
+                final WifiActivityEnergyInfo result = mLastInfo;
+                result.mTimestamp = info.getTimeStamp();
+                result.mStackState = info.getStackState();
+                result.mControllerTxTimeMs =
+                        info.getControllerTxTimeMillis()- mLastInfo.mControllerTxTimeMs;
+                result.mControllerRxTimeMs =
+                        info.getControllerRxTimeMillis() - mLastInfo.mControllerRxTimeMs;
+                result.mControllerIdleTimeMs =
+                        info.getControllerIdleTimeMillis() - mLastInfo.mControllerIdleTimeMs;
+                result.mControllerEnergyUsed =
+                        info.getControllerEnergyUsed() - mLastInfo.mControllerEnergyUsed;
+                mLastInfo = info;
+                return result;
+            }
+        } catch (RemoteException e) {
+            // Nothing to report, WiFi is dead.
+        }
+        return null;
+    }
+
+    @GuardedBy("mExternalStatsLock")
+    private BluetoothActivityEnergyInfo pullBluetoothEnergyInfoLocked() {
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        if (adapter != null) {
+            BluetoothActivityEnergyInfo info = adapter.getControllerActivityEnergyInfo(
+                    BluetoothAdapter.ACTIVITY_ENERGY_INFO_REFRESHED);
+            if (info != null && info.isValid()) {
+                return info;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Fetches data from external sources (WiFi controller, bluetooth chipset) and updates
+     * batterystats with that information.
+     *
+     * We first grab a lock specific to this method, then once all the data has been collected,
+     * we grab the mStats lock and update the data.
+     */
+    void updateExternalStats() {
+        synchronized (mExternalStatsLock) {
+            final WifiActivityEnergyInfo wifiEnergyInfo = pullWifiEnergyInfoLocked();
+            final BluetoothActivityEnergyInfo bluetoothEnergyInfo = pullBluetoothEnergyInfoLocked();
+            synchronized (mStats) {
+                mStats.updateKernelWakelocksLocked();
+                mStats.updateMobileRadioStateLocked(SystemClock.elapsedRealtime());
+                mStats.updateWifiStateLocked(wifiEnergyInfo);
+                mStats.updateBluetoothStateLocked(bluetoothEnergyInfo);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 8fe1238..34c1c53 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -765,7 +765,7 @@
                 try {
                     perm = AppGlobals.getPackageManager().
                             checkPermission(r.requiredPermission,
-                                    info.activityInfo.applicationInfo.packageName);
+                                    info.activityInfo.applicationInfo.packageName, r.userId);
                 } catch (RemoteException e) {
                     perm = PackageManager.PERMISSION_DENIED;
                 }
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 7cf3b51..7c921ac 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
@@ -89,10 +90,10 @@
     int curSchedGroup;          // Currently desired scheduling class
     int setSchedGroup;          // Last set to background scheduling class
     int trimMemoryLevel;        // Last selected memory trimming level
-    int curProcState = -1;      // Currently computed process state: ActivityManager.PROCESS_STATE_*
-    int repProcState = -1;      // Last reported process state
-    int setProcState = -1;      // Last set process state in process tracker
-    int pssProcState = -1;      // The proc state we are currently requesting pss for
+    int curProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state
+    int repProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state
+    int setProcState = PROCESS_STATE_NONEXISTENT; // Last set process state in process tracker
+    int pssProcState = PROCESS_STATE_NONEXISTENT; // Currently requesting pss for
     boolean serviceb;           // Process currently is on the service B list
     boolean serviceHighRam;     // We are forcing to service B list due to its RAM use
     boolean setIsForeground;    // Running foreground UI when last set?
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 04912d0..3a20ded 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -16,10 +16,7 @@
 
 package com.android.server.am;
 
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityManagerService.DEBUG_RECENTS;
-import static com.android.server.am.ActivityManagerService.DEBUG_TASKS;
+import static com.android.server.am.ActivityManagerDebugConfig.*;
 import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
 
 import android.app.ActivityManager;
@@ -44,6 +41,8 @@
  */
 class RecentTasks extends ArrayList<TaskRecord> {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentTasks" : TAG_AM;
+    private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
+    private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
 
     // Maximum number recent bitmaps to keep in memory.
     private static final int MAX_RECENT_BITMAPS = 3;
@@ -83,8 +82,8 @@
         for (int i = size() - 1; i >= 0; --i) {
             TaskRecord tr = get(i);
             if (tr.userId == userId) {
-                if(DEBUG_TASKS) Slog.i(TAG, "remove RecentTask " + tr
-                        + " when finishing user" + userId);
+                if(DEBUG_TASKS) Slog.i(TAG_TASKS,
+                        "remove RecentTask " + tr + " when finishing user" + userId);
                 remove(i);
                 tr.removedFromRecents();
             }
@@ -170,21 +169,21 @@
                             continue;
                         } else {
                             // Otherwise just not available for now.
-                            if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG,
+                            if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS,
                                     "Making recent unavailable: " + task);
                             task.isAvailable = false;
                         }
                     } else {
                         if (!ai.enabled || !ai.applicationInfo.enabled
                                 || (ai.applicationInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
-                            if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG,
+                            if (DEBUG_RECENTS && task.isAvailable) Slog.d(TAG_RECENTS,
                                     "Making recent unavailable: " + task
                                     + " (enabled=" + ai.enabled + "/" + ai.applicationInfo.enabled
                                     + " flags=" + Integer.toHexString(ai.applicationInfo.flags)
                                     + ")");
                             task.isAvailable = false;
                         } else {
-                            if (DEBUG_RECENTS && !task.isAvailable) Slog.d(TAG,
+                            if (DEBUG_RECENTS && !task.isAvailable) Slog.d(TAG_RECENTS,
                                     "Making recent available: " + task);
                             task.isAvailable = true;
                         }
@@ -210,7 +209,7 @@
             top = top.mNextAffiliate;
             topIndex--;
         }
-        if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: adding affilliates starting at "
+        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding affilliates starting at "
                 + topIndex + " from intial " + taskIndex);
         // Find the end of the chain, doing a sanity check along the way.
         boolean sane = top.mAffiliatedTaskId == task.mAffiliatedTaskId;
@@ -218,7 +217,7 @@
         TaskRecord prev = top;
         while (endIndex < recentsCount) {
             TaskRecord cur = get(endIndex);
-            if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: looking at next chain @"
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: looking at next chain @"
                     + endIndex + " " + cur);
             if (cur == top) {
                 // Verify start of the chain.
@@ -249,7 +248,7 @@
                             + cur.mPrevAffiliate);
                     sane = false;
                 }
-                if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: end of chain @" + endIndex);
+                if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: end of chain @" + endIndex);
                 break;
             } else {
                 // Verify middle of the chain's prev points to a valid item.
@@ -290,12 +289,12 @@
             // All looks good, we can just move all of the affiliated tasks
             // to the top.
             for (int i=topIndex; i<=endIndex; i++) {
-                if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: moving affiliated " + task
+                if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving affiliated " + task
                         + " from " + i + " to " + (i-topIndex));
                 TaskRecord cur = remove(i);
                 add(i - topIndex, cur);
             }
-            if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: done moving tasks  " +  topIndex
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: done moving tasks  " +  topIndex
                     + " to " + endIndex);
             return true;
         }
@@ -312,19 +311,20 @@
         int recentsCount = size();
         // Quick case: never add voice sessions.
         if (task.voiceSession != null) {
-            if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: not adding voice interaction " + task);
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                    "addRecent: not adding voice interaction " + task);
             return;
         }
         // Another quick case: check if the top-most recent task is the same.
         if (!isAffiliated && recentsCount > 0 && get(0) == task) {
-            if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: already at top: " + task);
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: already at top: " + task);
             return;
         }
         // Another quick case: check if this is part of a set of affiliated
         // tasks that are at the top.
         if (isAffiliated && recentsCount > 0 && task.inRecents
                 && task.mAffiliatedTaskId == get(0).mAffiliatedTaskId) {
-            if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: affiliated " + get(0)
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: affiliated " + get(0)
                     + " at top when adding " + task);
             return;
         }
@@ -341,7 +341,7 @@
                     remove(taskIndex);
                     add(0, task);
                     mService.notifyTaskPersisterLocked(task, false);
-                    if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: moving to top " + task
+                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: moving to top " + task
                             + " from " + taskIndex);
                     return;
                 } else {
@@ -361,7 +361,7 @@
             }
         }
 
-        if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: trimming tasks for " + task);
+        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: trimming tasks for " + task);
         trimForTaskLocked(task, true);
 
         recentsCount = size();
@@ -376,7 +376,7 @@
             // If this is a simple non-affiliated task, or we had some failure trying to
             // handle it as part of an affilated task, then just place it at the top.
             add(0, task);
-            if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: adding " + task);
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: adding " + task);
         } else if (isAffiliated) {
             // If this is a new affiliated task, then move all of the affiliated tasks
             // to the front and insert this new one.
@@ -398,8 +398,8 @@
                         // after us in the list, so add at their position.
                         taskIndex = otherIndex;
                     }
-                    if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: new affiliated task added at "
-                            + taskIndex + ": " + task);
+                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                            "addRecent: new affiliated task added at " + taskIndex + ": " + task);
                     add(taskIndex, task);
 
                     // Now move everything to the front.
@@ -412,19 +412,19 @@
                     // everything and then go through our general path of adding a new task.
                     needAffiliationFix = true;
                 } else {
-                    if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: couldn't find other affiliation "
-                            + other);
+                    if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
+                            "addRecent: couldn't find other affiliation " + other);
                     needAffiliationFix = true;
                 }
             } else {
-                if (DEBUG_RECENTS) Slog.d(TAG,
+                if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
                         "addRecent: adding affiliated task without next/prev:" + task);
                 needAffiliationFix = true;
             }
         }
 
         if (needAffiliationFix) {
-            if (DEBUG_RECENTS) Slog.d(TAG, "addRecent: regrouping affiliations");
+            if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "addRecent: regrouping affiliations");
             cleanupLocked(task.userId);
         }
     }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index d8d361b..82e6d47 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -16,8 +16,9 @@
 
 package com.android.server.am;
 
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
+import static com.android.server.am.ActivityManagerDebugConfig.*;
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
@@ -56,6 +57,8 @@
 
 final class TaskRecord {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskRecord" : TAG_AM;
+    private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
+    private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
 
     static final String ATTR_TASKID = "task_id";
     private static final String TAG_INTENT = "intent";
@@ -310,8 +313,7 @@
                     _intent.setSourceBounds(null);
                 }
             }
-            if (ActivityManagerService.DEBUG_TASKS) Slog.v(TAG,
-                    "Setting Intent of " + this + " to " + _intent);
+            if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Setting Intent of " + this + " to " + _intent);
             intent = _intent;
             realActivity = _intent != null ? _intent.getComponent() : null;
             origActivity = null;
@@ -323,7 +325,7 @@
                 targetIntent.setComponent(targetComponent);
                 targetIntent.setSelector(null);
                 targetIntent.setSourceBounds(null);
-                if (ActivityManagerService.DEBUG_TASKS) Slog.v(TAG,
+                if (DEBUG_TASKS) Slog.v(TAG_TASKS,
                         "Setting Intent of " + this + " to target " + targetIntent);
                 intent = targetIntent;
                 realActivity = targetComponent;
@@ -346,8 +348,8 @@
         if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
             // If the activity itself has requested auto-remove, then just always do it.
             autoRemoveRecents = true;
-        } else if ((intentFlags & (Intent.FLAG_ACTIVITY_NEW_DOCUMENT
-                | Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS)) == Intent.FLAG_ACTIVITY_NEW_DOCUMENT) {
+        } else if ((intentFlags & (FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS))
+                == FLAG_ACTIVITY_NEW_DOCUMENT) {
             // If the caller has not asked for the document to be retained, then we may
             // want to turn on auto-remove, depending on whether the target has set its
             // own document launch mode.
@@ -819,7 +821,7 @@
     }
 
     void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
-        if (ActivityManagerService.DEBUG_RECENTS) Slog.i(TAG, "Saving task=" + this);
+        if (DEBUG_RECENTS) Slog.i(TAG_RECENTS, "Saving task=" + this);
 
         out.attribute(null, ATTR_TASKID, String.valueOf(taskId));
         if (realActivity != null) {
@@ -879,7 +881,8 @@
         for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
             final ActivityRecord r = activities.get(activityNdx);
             if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() ||
-                    ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) &&
+                    ((r.intent.getFlags() & FLAG_ACTIVITY_NEW_DOCUMENT
+                            | FLAG_ACTIVITY_RETAIN_IN_RECENTS) == FLAG_ACTIVITY_NEW_DOCUMENT) &&
                             activityNdx > 0) {
                 // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
                 break;
@@ -1045,7 +1048,7 @@
             activities.get(activityNdx).task = task;
         }
 
-        if (ActivityManagerService.DEBUG_RECENTS) Slog.d(TAG, "Restored task=" + task);
+        if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, "Restored task=" + task);
         return task;
     }
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 65b2ae2..1eddc8e 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -234,9 +234,6 @@
     private final Object mSoundEffectsLock = new Object();
     private static final int NUM_SOUNDPOOL_CHANNELS = 4;
 
-    // Maximum volume adjust steps allowed in a single batch call.
-    private static final int MAX_BATCH_VOLUME_ADJUST_STEPS = 4;
-
     /* Sound effect file names  */
     private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
     private static final List<String> SOUND_EFFECT_FILES = new ArrayList<String>();
@@ -988,6 +985,7 @@
         } else {
             streamType = getActiveStreamType(suggestedStreamType);
         }
+        ensureValidStreamType(streamType);
         final int resolvedStream = mStreamVolumeAlias[streamType];
 
         // Play sounds on STREAM_RING only.
@@ -1421,6 +1419,8 @@
     private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
         if (!isPlatformVoice() && (streamType == AudioSystem.STREAM_RING)) {
             streamType = AudioSystem.STREAM_NOTIFICATION;
+        } else {
+            streamType = mStreamVolumeAlias[streamType];
         }
 
         if (streamType == AudioSystem.STREAM_MUSIC) {
@@ -3131,12 +3131,6 @@
         }
     }
 
-    private void ensureValidSteps(int steps) {
-        if (Math.abs(steps) > MAX_BATCH_VOLUME_ADJUST_STEPS) {
-            throw new IllegalArgumentException("Bad volume adjust steps " + steps);
-        }
-    }
-
     private void ensureValidStreamType(int streamType) {
         if (streamType < 0 || streamType >= mStreamStates.length) {
             throw new IllegalArgumentException("Bad stream type " + streamType);
@@ -3305,7 +3299,7 @@
     }
 
     private int getDeviceForStream(int stream) {
-        int device = AudioSystem.getDevicesForStream(stream);
+        int device = getDevicesForStream(stream);
         if ((device & (device - 1)) != 0) {
             // Multiple device selection is either:
             //  - speaker + one other device: give priority to speaker in this case.
@@ -3328,6 +3322,27 @@
         return device;
     }
 
+    private int getDevicesForStream(int stream) {
+        return getDevicesForStream(stream, true /*checkOthers*/);
+    }
+
+    private int getDevicesForStream(int stream, boolean checkOthers) {
+        ensureValidStreamType(stream);
+        synchronized (VolumeStreamState.class) {
+            return mStreamStates[stream].observeDevicesForStream_syncVSS(checkOthers);
+        }
+    }
+
+    private void observeDevicesForStreams(int skipStream) {
+        synchronized (VolumeStreamState.class) {
+            for (int stream = 0; stream < mStreamStates.length; stream++) {
+                if (stream != skipStream) {
+                    mStreamStates[stream].observeDevicesForStream_syncVSS(false /*checkOthers*/);
+                }
+            }
+        }
+    }
+
     /*
      * A class just for packaging up a set of connection parameters.
      */
@@ -3406,9 +3421,11 @@
 
         private boolean mIsMuted;
         private String mVolumeIndexSettingName;
+        private int mObservedDevices;
 
         private final SparseIntArray mIndexMap = new SparseIntArray(8);
         private final Intent mVolumeChanged;
+        private final Intent mStreamDevicesChanged;
 
         private VolumeStreamState(String settingName, int streamType) {
 
@@ -3422,6 +3439,29 @@
             readSettings();
             mVolumeChanged = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
             mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mStreamType);
+            mStreamDevicesChanged = new Intent(AudioManager.STREAM_DEVICES_CHANGED_ACTION);
+            mStreamDevicesChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, mStreamType);
+        }
+
+        public int observeDevicesForStream_syncVSS(boolean checkOthers) {
+            final int devices = AudioSystem.getDevicesForStream(mStreamType);
+            if (devices == mObservedDevices) {
+                return devices;
+            }
+            final int prevDevices = mObservedDevices;
+            mObservedDevices = devices;
+            if (checkOthers) {
+                // one stream's devices have changed, check the others
+                observeDevicesForStreams(mStreamType);
+            }
+            // log base stream changes to the event log
+            if (mStreamVolumeAlias[mStreamType] == mStreamType) {
+                EventLogTags.writeStreamDevicesChanged(mStreamType, prevDevices, devices);
+            }
+            sendBroadcastToAll(mStreamDevicesChanged
+                    .putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, prevDevices)
+                    .putExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, devices));
+            return devices;
         }
 
         public String getSettingNameForDevice(int device) {
@@ -3716,7 +3756,7 @@
             }
             pw.println();
             pw.print("   Devices: ");
-            final int devices = AudioSystem.getDevicesForStream(mStreamType);
+            final int devices = getDevicesForStream(mStreamType);
             int device, i = 0, n = 0;
             // iterate all devices from 1 to DEVICE_OUT_DEFAULT exclusive
             // (the default device is not returned by getDevicesForStream)
@@ -4250,6 +4290,7 @@
                         }
                     }
                     mRoutesObservers.finishBroadcast();
+                    observeDevicesForStreams(-1);
                     break;
                 }
 
@@ -5348,7 +5389,7 @@
                                 on ? AudioSystem.FORCE_HDMI_SYSTEM_AUDIO_ENFORCED :
                                      AudioSystem.FORCE_NONE);
                     }
-                    device = AudioSystem.getDevicesForStream(AudioSystem.STREAM_MUSIC);
+                    device = getDevicesForStream(AudioSystem.STREAM_MUSIC);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/camera/CameraService.java b/services/core/java/com/android/server/camera/CameraService.java
new file mode 100644
index 0000000..f9b17ed
--- /dev/null
+++ b/services/core/java/com/android/server/camera/CameraService.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 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.server.camera;
+
+import android.content.Context;
+import android.hardware.ICameraService;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.server.SystemService;
+
+/**
+ * CameraService is the system_server analog to the camera service running in mediaserver.
+ *
+ * @hide
+ */
+public class CameraService extends SystemService {
+
+    /**
+     * This must match the ICameraService.aidl definition
+     */
+    private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
+
+    // Event arguments to use with the camera service notifySystemEvent call:
+    public static final int NO_EVENT = 0; // NOOP
+    public static final int USER_SWITCHED = 1; // User changed, argument is the new user handle
+
+    public CameraService(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {}
+
+    @Override
+    public void onSwitchUser(int userHandle) {
+        super.onSwitchUser(userHandle);
+
+        /**
+         * Forward the user switch event to the native camera service running in mediaserver.
+         */
+        IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
+        if (cameraServiceBinder == null) {
+            return; // Camera service not active, there is no need to evict user clients.
+        }
+        ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
+        try {
+            cameraServiceRaw.notifySystemEvent(USER_SWITCHED, userHandle);
+        } catch (RemoteException e) {
+            // Do nothing, if camera service is dead, there is no need to evict user clients.
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 7f47678..0b430ea 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1021,6 +1021,14 @@
     public synchronized LegacyVpnInfo getLegacyVpnInfo() {
         // Check if the caller is authorized.
         enforceControlPermission();
+        return getLegacyVpnInfoPrivileged();
+    }
+
+    /**
+     * Return the information of the current ongoing legacy VPN.
+     * Callers are responsible for checking permissions if needed.
+     */
+    public synchronized LegacyVpnInfo getLegacyVpnInfoPrivileged() {
         if (mLegacyVpnRunner == null) return null;
 
         final LegacyVpnInfo info = new LegacyVpnInfo();
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index b398f41..fd75077 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -16,25 +16,30 @@
 
 package com.android.server.fingerprint;
 
+import android.content.ContentResolver;
 import android.content.Context;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.MessageQueue;
 import android.os.RemoteException;
-import android.service.fingerprint.FingerprintManager;
 import android.util.ArrayMap;
 import android.util.Slog;
 
 import com.android.server.SystemService;
 
-import android.service.fingerprint.FingerprintUtils;
-import android.service.fingerprint.IFingerprintService;
-import android.service.fingerprint.IFingerprintServiceReceiver;
+import android.hardware.fingerprint.FingerprintUtils;
+import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintService;
+import android.hardware.fingerprint.IFingerprintServiceReceiver;
+
 import static android.Manifest.permission.MANAGE_FINGERPRINT;
 import static android.Manifest.permission.USE_FINGERPRINT;
 
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * A service to manage multiple clients that want to access the fingerprint HAL API.
@@ -50,11 +55,14 @@
 
     private static final int MSG_NOTIFY = 10;
 
+    private static final int ENROLLMENT_TIMEOUT_MS = 60 * 1000; // 1 minute
+
     Handler mHandler = new Handler() {
         public void handleMessage(android.os.Message msg) {
             switch (msg.what) {
                 case MSG_NOTIFY:
-                    handleNotify(msg.arg1, msg.arg2, (Integer) msg.obj);
+                    FpHalMsg m = (FpHalMsg) msg.obj;
+                    handleNotify(m.type, m.arg1, m.arg2, m.arg3);
                     break;
 
                 default:
@@ -66,7 +74,7 @@
     private int mHalDeviceId;
 
     private static final int STATE_IDLE = 0;
-    private static final int STATE_LISTENING = 1;
+    private static final int STATE_AUTHENTICATING = 1;
     private static final int STATE_ENROLLING = 2;
     private static final int STATE_REMOVING = 3;
     private static final long MS_PER_SEC = 1000;
@@ -76,7 +84,10 @@
         int state;
         int userId;
         public TokenWatcher tokenWatcher;
-        IBinder getToken() { return tokenWatcher.getToken(); }
+
+        IBinder getToken() {
+            return tokenWatcher.getToken();
+        }
     }
 
     private class TokenWatcher implements IBinder.DeathRecipient {
@@ -86,7 +97,10 @@
             this.token = new WeakReference<IBinder>(token);
         }
 
-        IBinder getToken() { return token.get(); }
+        IBinder getToken() {
+            return token.get();
+        }
+
         public void binderDied() {
             mClients.remove(token);
             this.token = null;
@@ -112,21 +126,42 @@
 
     // TODO: Move these into separate process
     // JNI methods to communicate from FingerprintManagerService to HAL
-    static native int nativeEnroll(int timeout);
+    static native int nativeEnroll(int timeout, int groupId);
+
+    static native int nativeAuthenticate(long sessionId, int groupId);
+
     static native int nativeEnrollCancel();
-    static native int nativeRemove(int fingerprintId);
+
+    static native int nativeRemove(int fingerId, int groupId);
+
     static native int nativeOpenHal();
+
     static native int nativeCloseHal();
+
     static native void nativeInit(MessageQueue queue, FingerprintService service);
 
-    // JNI methods for communicating from HAL to clients
-    void notify(int msg, int arg1, int arg2) {
-        mHandler.obtainMessage(MSG_NOTIFY, msg, arg1, arg2).sendToTarget();
+    static final class FpHalMsg {
+        int type; // Type of the message. One of the constants in fingerprint.h
+        int arg1; // optional arguments
+        int arg2;
+        int arg3;
+
+        FpHalMsg(int type, int arg1, int arg2, int arg3) {
+            this.type = type;
+            this.arg1 = arg1;
+            this.arg2 = arg2;
+            this.arg3 = arg3;
+        }
     }
 
-    void handleNotify(int msg, int arg1, int arg2) {
-        Slog.v(TAG, "handleNotify(msg=" + msg + ", arg1=" + arg1 + ", arg2=" + arg2 + ")"
-                + ", " + mClients.size() + " clients");
+    // JNI methods for communicating from HAL to clients
+    void notify(int type, int arg1, int arg2, int arg3) {
+        mHandler.obtainMessage(MSG_NOTIFY, new FpHalMsg(type, arg1, arg2, arg3)).sendToTarget();
+    }
+
+    void handleNotify(int type, int arg1, int arg2, int arg3) {
+        Slog.v(TAG, "handleNotify(type=" + type + ", arg1=" + arg1 + ", arg2=" + arg2 + ")" + ", "
+                + mClients.size() + " clients");
         for (int i = 0; i < mClients.size(); i++) {
             if (DEBUG) Slog.v(TAG, "Client[" + i + "] binder token: " + mClients.keyAt(i));
             ClientData clientData = mClients.valueAt(i);
@@ -134,21 +169,20 @@
                 if (DEBUG) Slog.v(TAG, "clientData is invalid!!");
                 continue;
             }
-            switch (msg) {
+            ContentResolver contentResolver = mContext.getContentResolver();
+            switch (type) {
                 case FingerprintManager.FINGERPRINT_ERROR: {
-                    final int error = arg1;
                     try {
-                        clientData.receiver.onError(error);
+                        clientData.receiver.onError(mHalDeviceId, arg1 /* error */);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "can't send message to client. Did it die?", e);
                         mClients.remove(mClients.keyAt(i));
                     }
                 }
-                break;
+                    break;
                 case FingerprintManager.FINGERPRINT_ACQUIRED: {
-                    final int acquireInfo = arg1;
                     try {
-                        clientData.receiver.onAcquired(acquireInfo);
+                        clientData.receiver.onAcquired(mHalDeviceId, arg1 /* acquireInfo */);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "can't send message to client. Did it die?", e);
                         mClients.remove(mClients.keyAt(i));
@@ -156,9 +190,9 @@
                     break;
                 }
                 case FingerprintManager.FINGERPRINT_PROCESSED: {
-                    final int fingerId = arg1;
                     try {
-                        clientData.receiver.onProcessed(fingerId);
+                        clientData.receiver
+                                .onProcessed(mHalDeviceId, arg1 /* fingerId */, arg2 /* groupId */);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "can't send message to client. Did it die?", e);
                         mClients.remove(mClients.keyAt(i));
@@ -167,11 +201,13 @@
                 }
                 case FingerprintManager.FINGERPRINT_TEMPLATE_ENROLLING: {
                     final int fingerId = arg1;
-                    final int remaining = arg2;
+                    final int groupId = arg2;
+                    final int remaining = arg3;
                     if (clientData.state == STATE_ENROLLING) {
                         // Only send enroll updates to clients that are actually enrolling
                         try {
-                            clientData.receiver.onEnrollResult(fingerId, remaining);
+                            clientData.receiver.onEnrollResult(mHalDeviceId, fingerId, groupId,
+                                    remaining);
                         } catch (RemoteException e) {
                             Slog.e(TAG, "can't send message to client. Did it die?", e);
                             mClients.remove(mClients.keyAt(i));
@@ -179,8 +215,8 @@
                         // Update the database with new finger id.
                         // TODO: move to client code (Settings)
                         if (remaining == 0) {
-                            FingerprintUtils.addFingerprintIdForUser(fingerId,
-                                    mContext.getContentResolver(), clientData.userId);
+                            FingerprintUtils.addFingerprintIdForUser(contentResolver, fingerId,
+                                    clientData.userId);
                             clientData.state = STATE_IDLE; // Nothing left to do
                         }
                     } else {
@@ -191,30 +227,50 @@
                 }
                 case FingerprintManager.FINGERPRINT_TEMPLATE_REMOVED: {
                     int fingerId = arg1;
-                    if (fingerId == 0) throw new IllegalStateException("Got illegal id from HAL");
-                    FingerprintUtils.removeFingerprintIdForUser(fingerId,
-                            mContext.getContentResolver(), clientData.userId);
+                    int groupId = arg2;
+                    if (fingerId == 0) {
+                        throw new IllegalStateException("Got illegal id from HAL");
+                    }
+                    FingerprintUtils.removeFingerprintIdForUser(fingerId, contentResolver,
+                            clientData.userId);
                     if (clientData.receiver != null) {
                         try {
-                            clientData.receiver.onRemoved(fingerId);
+                            clientData.receiver.onRemoved(mHalDeviceId, fingerId, groupId);
                         } catch (RemoteException e) {
                             Slog.e(TAG, "can't send message to client. Did it die?", e);
                             mClients.remove(mClients.keyAt(i));
                         }
                     }
-                    clientData.state = STATE_LISTENING;
+                    clientData.state = STATE_IDLE;
                 }
-                break;
+                    break;
             }
         }
     }
 
-    void startEnroll(IBinder token, long timeout, int userId) {
+    void startEnroll(IBinder token, int groupId, int flags) {
         ClientData clientData = mClients.get(token);
         if (clientData != null) {
-            if (clientData.userId != userId) throw new IllegalStateException("Bad user");
+            if (clientData.userId != groupId) {
+                throw new IllegalStateException("Bad user");
+            }
             clientData.state = STATE_ENROLLING;
-            nativeEnroll((int) (timeout / MS_PER_SEC));
+            final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
+            nativeEnroll(timeout, groupId);
+        } else {
+            Slog.w(TAG, "enroll(): No listener registered");
+        }
+    }
+
+    void startAuthenticate(IBinder token, long sessionId, int groupId, int flags) {
+        ClientData clientData = mClients.get(token);
+        if (clientData != null) {
+            if (clientData.userId != groupId) {
+                throw new IllegalStateException("Bad user");
+            }
+            clientData.state = STATE_AUTHENTICATING;
+            final int timeout = (int) (ENROLLMENT_TIMEOUT_MS / MS_PER_SEC);
+            nativeAuthenticate(sessionId, groupId);
         } else {
             Slog.w(TAG, "enroll(): No listener registered");
         }
@@ -224,7 +280,7 @@
         ClientData clientData = mClients.get(token);
         if (clientData != null) {
             if (clientData.userId != userId) throw new IllegalStateException("Bad user");
-            clientData.state = STATE_LISTENING;
+            clientData.state = STATE_IDLE;
             nativeEnrollCancel();
         } else {
             Slog.w(TAG, "enrollCancel(): No listener registered");
@@ -238,7 +294,7 @@
             if (clientData.userId != userId) throw new IllegalStateException("Bad user");
             clientData.state = STATE_REMOVING;
             // The fingerprint id will be removed when we get confirmation from the HAL
-            int result = nativeRemove(fingerId);
+            int result = nativeRemove(fingerId, userId);
             if (result != 0) {
                 Slog.w(TAG, "Error removing fingerprint with id = " + fingerId);
             }
@@ -251,7 +307,7 @@
         if (DEBUG) Slog.v(TAG, "startListening(" + receiver + ")");
         if (mClients.get(token) == null) {
             ClientData clientData = new ClientData();
-            clientData.state = STATE_LISTENING;
+            clientData.state = STATE_IDLE;
             clientData.receiver = receiver;
             clientData.userId = userId;
             clientData.tokenWatcher = new TokenWatcher(token);
@@ -266,7 +322,7 @@
         }
     }
 
-    void removeListener(IBinder token, int userId) {
+    void removeListener(IBinder token, IFingerprintServiceReceiver receiver) {
         if (DEBUG) Slog.v(TAG, "stopListening(" + token + ")");
         ClientData clientData = mClients.get(token);
         if (clientData != null) {
@@ -278,61 +334,91 @@
         mClients.remove(token);
     }
 
+    public List<Fingerprint> getEnrolledFingerprints(int groupId) {
+        ContentResolver resolver = mContext.getContentResolver();
+        int[] ids = FingerprintUtils.getFingerprintIdsForUser(resolver, groupId);
+        List<Fingerprint> result = new ArrayList<Fingerprint>();
+        for (int i = 0; i < ids.length; i++) {
+            // TODO: persist names in Settings
+            CharSequence name = "Finger" + ids[i];
+            final int group = 0; // TODO
+            final int fingerId = ids[i];
+            final long deviceId = 0; // TODO
+            Fingerprint item = new Fingerprint(name, 0, ids[i], 0);
+            result.add(item);
+        }
+        return result;
+    }
+
     void checkPermission(String permission) {
-        getContext().enforceCallingOrSelfPermission(permission, "Must have "
-                + permission + " permission.");
+        getContext().enforceCallingOrSelfPermission(permission,
+                "Must have " + permission + " permission.");
     }
 
     private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
-        @Override // Binder call
-        public void enroll(IBinder token, long timeout, int userId) {
+        @Override
+        // Binder call
+        public void enroll(IBinder token, int groupId, int flags) {
             checkPermission(MANAGE_FINGERPRINT);
-            startEnroll(token, timeout, userId);
-        }
-
-        @Override // Binder call
-        public void enrollCancel(IBinder token,int userId) {
-            checkPermission(MANAGE_FINGERPRINT);
-            startEnrollCancel(token, userId);
-        }
-
-        @Override // Binder call
-        public void remove(IBinder token, int fingerprintId, int userId) {
-            checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
-            startRemove(token, fingerprintId, userId);
-        }
-
-        @Override // Binder call
-        public void startListening(IBinder token, IFingerprintServiceReceiver receiver, int userId)
-        {
-            checkPermission(USE_FINGERPRINT);
-            addListener(token, receiver, userId);
-        }
-
-        @Override // Binder call
-        public void stopListening(IBinder token, int userId) {
-            checkPermission(USE_FINGERPRINT);
-            removeListener(token, userId);
-        }
-
-        @Override // Binder call
-        public boolean isHardwareDetected() {
-            checkPermission(USE_FINGERPRINT);
-            return mHalDeviceId != 0;
+            startEnroll(token, groupId, flags);
         }
 
         @Override
-        public void rename(int fpId, String name) {
+        // Binder call
+        public void authenticate(IBinder token, long sessionId, int groupId, int flags) {
+            checkPermission(USE_FINGERPRINT);
+            startAuthenticate(token, sessionId, groupId, flags);
+        }
+
+        @Override
+        // Binder call
+        public void remove(IBinder token, int fingerId, int groupId) {
+            checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
+            startRemove(token, fingerId, groupId);
+        }
+
+        @Override
+        // Binder call
+        public void addListener(IBinder token, IFingerprintServiceReceiver receiver, int userId) {
+            checkPermission(USE_FINGERPRINT);
+            FingerprintService.this.addListener(token, receiver, userId);
+        }
+
+        @Override
+        // Binder call
+        public void removeListener(IBinder token, IFingerprintServiceReceiver receiver) {
+            checkPermission(USE_FINGERPRINT);
+            FingerprintService.this.removeListener(token, receiver);
+        }
+
+        @Override
+        // Binder call
+        public boolean isHardwareDetected(long deviceId) {
+            checkPermission(USE_FINGERPRINT);
+            return mHalDeviceId != 0; // TODO
+        }
+
+        @Override
+        // Binder call
+        public void rename(int fingerId, int groupId, String name) {
             checkPermission(MANAGE_FINGERPRINT);
+            Slog.w(TAG, "rename id=" + fingerId + ",gid=" + groupId + ",name=" + name);
             // TODO
         }
+
+        @Override
+        // Binder call
+        public List<Fingerprint> getEnrolledFingerprints(int groupId) {
+            checkPermission(USE_FINGERPRINT);
+            return FingerprintService.this.getEnrolledFingerprints(groupId);
+        }
     }
 
     @Override
     public void onStart() {
-       publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
-       mHalDeviceId = nativeOpenHal();
-       if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + mHalDeviceId);
+        publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
+        mHalDeviceId = nativeOpenHal();
+        if (DEBUG) Slog.v(TAG, "Fingerprint HAL id: " + mHalDeviceId);
     }
 
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 70fa441..89ffe45 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -192,6 +192,13 @@
         }
     }
 
+    @ServiceThreadOnly
+    protected boolean handleUserControlPressed(HdmiCecMessage message) {
+        assertRunOnServiceThread();
+        wakeUpIfActiveSource();
+        return super.handleUserControlPressed(message);
+    }
+
     @Override
     @ServiceThreadOnly
     protected boolean handleSetStreamPath(HdmiCecMessage message) {
@@ -229,7 +236,12 @@
     }
 
     private void wakeUpIfActiveSource() {
-        if (mIsActiveSource && mService.isPowerStandbyOrTransient()) {
+        if (!mIsActiveSource) {
+            return;
+        }
+        // Wake up the device if the power is in standby mode, or its screen is off -
+        // which can happen if the device is holding a partial lock.
+        if (mService.isPowerStandbyOrTransient() || !mService.getPowerManager().isScreenOn()) {
             mService.wakeUp();
         }
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 7c93e56..d5cb5e3 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -251,7 +251,9 @@
         }
         int targetAddress = targetDevice.getLogicalAddress();
         ActiveSource active = getActiveSource();
-        if (active.isValid() && targetAddress == active.logicalAddress) {
+        if (targetDevice.getDevicePowerStatus() == HdmiControlManager.POWER_STATUS_ON
+                && active.isValid()
+                && targetAddress == active.logicalAddress) {
             invokeCallback(callback, HdmiControlManager.RESULT_SUCCESS);
             return;
         }
diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java
index cbbf91b6..75a79cb 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java
@@ -58,14 +58,16 @@
             // received without <Request ARC Initiation> or <Request ARC Termination>.
             case Constants.MESSAGE_FEATURE_ABORT:
                 int originalOpcode = cmd.getParams()[0] & 0xFF;
-                if (originalOpcode == Constants.MESSAGE_REQUEST_ARC_INITIATION
-                        || originalOpcode == Constants.MESSAGE_REQUEST_ARC_TERMINATION) {
+                if (originalOpcode == Constants.MESSAGE_REQUEST_ARC_TERMINATION) {
                     disableArcTransmission();
                     finish();
                     return true;
-                } else {
-                    return false;
+                } else if (originalOpcode == Constants.MESSAGE_REQUEST_ARC_INITIATION) {
+                    tv().setArcStatus(false);
+                    finish();
+                    return true;
                 }
+                return false;
         }
         return false;
     }
@@ -82,7 +84,7 @@
         if (mState != state || state != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE) {
             return;
         }
-        HdmiLogger.debug("[T]RequestArcAction.");
+        HdmiLogger.debug("[T] RequestArcAction.");
         disableArcTransmission();
         finish();
     }
diff --git a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
index d9e1f24..f69f975 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
@@ -35,6 +35,7 @@
 
     @Override
     boolean start() {
+        // Seq #38
         mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
         addTimer(mState, HdmiConfig.TIMEOUT_MS);
 
@@ -44,9 +45,8 @@
             @Override
             public void onSendCompleted(int error) {
                 if (error != Constants.SEND_RESULT_SUCCESS) {
-                    // If failed to send <Request ARC Initiation>, start "Disabled"
-                    // ARC transmission action.
-                    disableArcTransmission();
+                    // Turn off ARC status if <Request ARC Initiation> fails.
+                    tv().setArcStatus(false);
                     finish();
                 }
             }
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index bffa854..d200d35 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -52,6 +52,7 @@
 
     @Override
     boolean start() {
+        // Seq #37.
         if (mEnabled) {
             // Enable ARC status immediately after sending <Report Arc Initiated>.
             // If AVR responds with <Feature Abort>, disable ARC status again.
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index fe1260d..d79b5fd 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -41,6 +41,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -109,21 +110,22 @@
      * Track Services that have currently active or pending jobs. The index is provided by
      * {@link JobStatus#getServiceToken()}
      */
-    final List<JobServiceContext> mActiveServices = new ArrayList<JobServiceContext>();
+    final List<JobServiceContext> mActiveServices = new ArrayList<>();
     /** List of controllers that will notify this service of updates to jobs. */
     List<StateController> mControllers;
     /**
      * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
      * when ready to execute them.
      */
-    final ArrayList<JobStatus> mPendingJobs = new ArrayList<JobStatus>();
+    final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
 
-    final ArrayList<Integer> mStartedUsers = new ArrayList();
+    final ArrayList<Integer> mStartedUsers = new ArrayList<>();
 
     final JobHandler mHandler;
     final JobSchedulerStub mJobSchedulerStub;
 
     IBatteryStats mBatteryStats;
+    PowerManager mPowerManager;
 
     /**
      * Set to true once we are allowed to run third party apps.
@@ -131,6 +133,11 @@
     boolean mReadyToRock;
 
     /**
+     * True when in device idle mode, so we don't want to schedule any jobs.
+     */
+    boolean mDeviceIdleMode;
+
+    /**
      * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
      * still clean up. On reinstall the package will have a new uid.
      */
@@ -154,6 +161,8 @@
                     Slog.d(TAG, "Removing jobs for user: " + userId);
                 }
                 cancelJobsForUser(userId);
+            } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction())) {
+                updateIdleMode(mPowerManager != null ? mPowerManager.isDeviceIdleMode() : false);
             }
         }
     };
@@ -199,7 +208,7 @@
         return outList;
     }
 
-    private void cancelJobsForUser(int userHandle) {
+    void cancelJobsForUser(int userHandle) {
         List<JobStatus> jobsForUser;
         synchronized (mJobs) {
             jobsForUser = mJobs.getJobsByUser(userHandle);
@@ -257,6 +266,40 @@
         }
     }
 
+    void updateIdleMode(boolean enabled) {
+        boolean changed = false;
+        boolean rocking;
+        synchronized (mJobs) {
+            if (mDeviceIdleMode != enabled) {
+                changed = true;
+            }
+            rocking = mReadyToRock;
+        }
+        if (changed) {
+            if (rocking) {
+                for (int i=0; i<mControllers.size(); i++) {
+                    mControllers.get(i).deviceIdleModeChanged(enabled);
+                }
+            }
+            synchronized (mJobs) {
+                mDeviceIdleMode = enabled;
+                if (enabled) {
+                    // When becoming idle, make sure no jobs are actively running.
+                    for (int i=0; i<mActiveServices.size(); i++) {
+                        JobServiceContext jsc = mActiveServices.get(i);
+                        final JobStatus executing = jsc.getRunningJob();
+                        if (executing != null) {
+                            jsc.cancelExecutingJob();
+                        }
+                    }
+                } else {
+                    // When coming out of idle, allow thing to start back up.
+                    mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
+                }
+            }
+        }
+    }
+
     /**
      * Initializes the system service.
      * <p>
@@ -294,8 +337,10 @@
             getContext().registerReceiverAsUser(
                     mBroadcastReceiver, UserHandle.ALL, filter, null, null);
             final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
+            userFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
             getContext().registerReceiverAsUser(
                     mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
+            mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
         } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
             synchronized (mJobs) {
                 // Let's go!
@@ -313,6 +358,7 @@
                 for (int i=0; i<jobs.size(); i++) {
                     JobStatus job = jobs.valueAt(i);
                     for (int controller=0; controller<mControllers.size(); controller++) {
+                        mControllers.get(controller).deviceIdleModeChanged(mDeviceIdleMode);
                         mControllers.get(controller).maybeStartTrackingJob(job);
                     }
                 }
@@ -667,6 +713,10 @@
          */
         private void maybeRunPendingJobsH() {
             synchronized (mJobs) {
+                if (mDeviceIdleMode) {
+                    // If device is idle, we will not schedule jobs to run.
+                    return;
+                }
                 Iterator<JobStatus> it = mPendingJobs.iterator();
                 if (DEBUG) {
                     Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
@@ -878,6 +928,7 @@
             }
             pw.println();
             pw.print("mReadyToRock="); pw.println(mReadyToRock);
+            pw.print("mDeviceIdleMode="); pw.println(mDeviceIdleMode);
         }
         pw.println();
     }
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
index 7d76fc0..efd1928 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -31,12 +31,17 @@
     protected static final boolean DEBUG = false;
     protected Context mContext;
     protected StateChangedListener mStateChangedListener;
+    protected boolean mDeviceIdleMode;
 
     public StateController(StateChangedListener stateChangedListener, Context context) {
         mStateChangedListener = stateChangedListener;
         mContext = context;
     }
 
+    public void deviceIdleModeChanged(boolean enabled) {
+        mDeviceIdleMode = enabled;
+    }
+
     /**
      * Implement the logic here to decide whether a job should be tracked by this controller.
      * This logic is put here so the JobManger can be completely agnostic of Controller logic.
@@ -50,5 +55,4 @@
     public abstract void maybeStopTrackingJob(JobStatus jobStatus);
 
     public abstract void dumpControllerState(PrintWriter pw);
-
 }
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index b5036db..09d0501 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -40,6 +40,7 @@
 import android.media.session.ParcelableVolumeInfo;
 import android.media.session.PlaybackState;
 import android.media.AudioAttributes;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.DeadObjectException;
@@ -887,6 +888,14 @@
             }
         }
 
+        public void playFromUri(Uri uri, Bundle extras) {
+            try {
+                mCb.onPlayFromUri(uri, extras);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Remote failure in playFromUri.", e);
+            }
+        }
+
         public void skipToTrack(long id) {
             try {
                 mCb.onSkipToTrack(id);
@@ -1103,6 +1112,11 @@
         }
 
         @Override
+        public void playFromUri(Uri uri, Bundle extras) throws RemoteException {
+            mSessionCb.playFromUri(uri, extras);
+        }
+
+        @Override
         public void skipToQueueItem(long id) {
             mSessionCb.skipToTrack(id);
         }
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index a530dfa..65949bf 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -55,6 +55,7 @@
 import android.speech.RecognizerIntent;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.view.KeyEvent;
 
@@ -743,15 +744,23 @@
                 Log.w(TAG, "Attempted to dispatch null or non-media key event.");
                 return;
             }
+
             final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
-            if (DEBUG) {
-                Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
-                        + keyEvent);
-            }
-
             try {
+                if (DEBUG) {
+                    Log.d(TAG, "dispatchMediaKeyEvent, pid=" + pid + ", uid=" + uid + ", event="
+                            + keyEvent);
+                }
+                if (!isUserSetupComplete()) {
+                    // Global media key handling can have the side-effect of starting new
+                    // activities which is undesirable while setup is in progress.
+                    Slog.i(TAG, "Not dispatching media key event because user "
+                            + "setup is in progress.");
+                    return;
+                }
+
                 synchronized (mLock) {
                     // If we don't have a media button receiver to fall back on
                     // include non-playing sessions for dispatching
@@ -1025,6 +1034,11 @@
             return keyCode == KeyEvent.KEYCODE_HEADSETHOOK;
         }
 
+        private boolean isUserSetupComplete() {
+            return Settings.Secure.getIntForUser(getContext().getContentResolver(),
+                    Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+        }
+
         // we only handle public stream types, which are 0-5
         private boolean isValidLocalStreamType(int streamType) {
             return streamType >= AudioManager.STREAM_VOICE_CALL
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 8d46775..5de7d42 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -71,7 +71,9 @@
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
+import android.Manifest;
 import android.app.ActivityManager;
+import android.app.AppGlobals;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
 import android.app.IProcessObserver;
@@ -83,6 +85,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
@@ -2021,6 +2024,17 @@
     void updateRulesForUidLocked(int uid) {
         if (!isUidValidForRules(uid)) return;
 
+        // quick check: if this uid doesn't have INTERNET permission, it doesn't have
+        // network access anyway, so it is a waste to mess with it here.
+        final IPackageManager ipm = AppGlobals.getPackageManager();
+        try {
+            if (ipm.checkUidPermission(Manifest.permission.INTERNET, uid)
+                    != PackageManager.PERMISSION_GRANTED) {
+                return;
+            }
+        } catch (RemoteException e) {
+        }
+
         final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
         final boolean uidForeground = isUidForegroundLocked(uid);
 
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index 20f5f97..15b68c7 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -22,21 +22,28 @@
 import static android.net.NetworkStats.TAG_NONE;
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.TrafficStats.UID_REMOVED;
+import static android.net.TrafficStats.UID_TETHERING;
+import static android.text.format.DateUtils.SECOND_IN_MILLIS;
 import static android.text.format.DateUtils.WEEK_IN_MILLIS;
 
+import android.net.ConnectivityManager;
 import android.net.NetworkIdentity;
 import android.net.NetworkStats;
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.net.TrafficStats;
+import android.os.Binder;
+import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
+import android.util.IntArray;
 
 import libcore.io.IoUtils;
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FileRotator;
 import com.android.internal.util.IndentingPrintWriter;
+
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
@@ -129,6 +136,23 @@
         return mStartMillis == Long.MAX_VALUE && mEndMillis == Long.MIN_VALUE;
     }
 
+    public int[] getRelevantUids() {
+        final int callerUid = Binder.getCallingUid();
+        IntArray uids = new IntArray();
+        for (int i = 0; i < mStats.size(); i++) {
+            final Key key = mStats.keyAt(i);
+            if (isAccessibleToUser(key.uid, callerUid)) {
+                int j = uids.binarySearch(key.uid);
+
+                if (j < 0) {
+                    j = ~j;
+                    uids.add(j, key.uid);
+                }
+            }
+        }
+        return uids.toArray();
+    }
+
     /**
      * Combine all {@link NetworkStatsHistory} in this collection which match
      * the requested parameters.
@@ -144,12 +168,21 @@
      */
     public NetworkStatsHistory getHistory(
             NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end) {
+        final int callerUid = Binder.getCallingUid();
+        if (!isAccessibleToUser(uid, callerUid)) {
+            throw new SecurityException("Network stats history of uid " + uid
+                    + " is forbidden for caller " + callerUid);
+        }
+
         final NetworkStatsHistory combined = new NetworkStatsHistory(
-                mBucketDuration, estimateBuckets(), fields);
+                mBucketDuration, start == end ? 1 : estimateBuckets(), fields);
+
+        // shortcut when we know stats will be empty
+        if (start == end) return combined;
+
         for (int i = 0; i < mStats.size(); i++) {
             final Key key = mStats.keyAt(i);
-            final boolean setMatches = set == SET_ALL || key.set == set;
-            if (key.uid == uid && setMatches && key.tag == tag
+            if (key.uid == uid && NetworkStats.setMatches(set, key.set) && key.tag == tag
                     && templateMatches(template, key.ident)) {
                 final NetworkStatsHistory value = mStats.valueAt(i);
                 combined.recordHistory(value, start, end);
@@ -166,15 +199,17 @@
         final long now = System.currentTimeMillis();
 
         final NetworkStats stats = new NetworkStats(end - start, 24);
-        final NetworkStats.Entry entry = new NetworkStats.Entry();
-        NetworkStatsHistory.Entry historyEntry = null;
-
         // shortcut when we know stats will be empty
         if (start == end) return stats;
 
+        final NetworkStats.Entry entry = new NetworkStats.Entry();
+        NetworkStatsHistory.Entry historyEntry = null;
+
+        final int callerUid = Binder.getCallingUid();
         for (int i = 0; i < mStats.size(); i++) {
             final Key key = mStats.keyAt(i);
-            if (templateMatches(template, key.ident)) {
+            if (templateMatches(template, key.ident) && isAccessibleToUser(key.uid, callerUid)
+                    && key.set < NetworkStats.SET_DEBUG_START) {
                 final NetworkStatsHistory value = mStats.valueAt(i);
                 historyEntry = value.getValues(start, end, now, historyEntry);
 
@@ -507,6 +542,7 @@
             final NetworkStatsHistory value = mStats.valueAt(i);
 
             if (!templateMatches(groupTemplate, key.ident)) continue;
+            if (key.set >= NetworkStats.SET_DEBUG_START) continue;
 
             final Key groupKey = new Key(null, key.uid, key.set, key.tag);
             NetworkStatsHistory groupHistory = grouped.get(groupKey);
@@ -534,6 +570,12 @@
         }
     }
 
+    private static boolean isAccessibleToUser(int uid, int callerUid) {
+        return UserHandle.getAppId(callerUid) == android.os.Process.SYSTEM_UID ||
+                uid == android.os.Process.SYSTEM_UID || uid == UID_REMOVED || uid == UID_TETHERING
+                || UserHandle.getUserId(uid) == UserHandle.getUserId(callerUid);
+    }
+
     /**
      * Test if given {@link NetworkTemplate} matches any {@link NetworkIdentity}
      * in the given {@link NetworkIdentitySet}.
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 0b596aa..50e03a2 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -62,9 +62,13 @@
 import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
 import static com.android.server.NetworkManagementSocketTagger.setKernelCounterSet;
 
+import android.Manifest;
 import android.app.AlarmManager;
+import android.app.AppOpsManager;
 import android.app.IAlarmManager;
 import android.app.PendingIntent;
+import android.app.admin.DeviceAdminInfo;
+import android.app.admin.DevicePolicyManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -93,7 +97,9 @@
 import android.os.INetworkManagementService;
 import android.os.Message;
 import android.os.PowerManager;
+import android.os.Process;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -116,6 +122,7 @@
 import com.android.internal.util.FileRotator;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
 import com.android.server.connectivity.Tethering;
 
 import java.io.File;
@@ -429,7 +436,11 @@
 
     @Override
     public INetworkStatsSession openSession() {
-        mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
+        return openSessionForUsageStats(null);
+    }
+
+    @Override
+    public INetworkStatsSession openSessionForUsageStats(final String callingPackage) {
         assertBandwidthControlEnabled();
 
         // return an IBinder which holds strong references to any loaded stats
@@ -438,6 +449,7 @@
         return new INetworkStatsSession.Stub() {
             private NetworkStatsCollection mUidComplete;
             private NetworkStatsCollection mUidTagComplete;
+            private String mCallingPackage = callingPackage;
 
             private NetworkStatsCollection getUidComplete() {
                 synchronized (mStatsLock) {
@@ -458,8 +470,29 @@
             }
 
             @Override
+            public int[] getRelevantUids() {
+                enforcePermissionForManagedAdmin(mCallingPackage);
+                return getUidComplete().getRelevantUids();
+            }
+
+            @Override
+            public NetworkStats getDeviceSummaryForNetwork(NetworkTemplate template, long start,
+                    long end) {
+                enforcePermission(mCallingPackage);
+                NetworkStats result = new NetworkStats(end - start, 1);
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    result.combineAllValues(internalGetSummaryForNetwork(template, start, end));
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+                return result;
+            }
+
+            @Override
             public NetworkStats getSummaryForNetwork(
                     NetworkTemplate template, long start, long end) {
+                enforcePermission(mCallingPackage);
                 return internalGetSummaryForNetwork(template, start, end);
             }
 
@@ -471,6 +504,7 @@
             @Override
             public NetworkStats getSummaryForAllUid(
                     NetworkTemplate template, long start, long end, boolean includeTags) {
+                enforcePermissionForManagedAdmin(mCallingPackage);
                 final NetworkStats stats = getUidComplete().getSummary(template, start, end);
                 if (includeTags) {
                     final NetworkStats tagStats = getUidTagComplete()
@@ -483,6 +517,7 @@
             @Override
             public NetworkStatsHistory getHistoryForUid(
                     NetworkTemplate template, int uid, int set, int tag, int fields) {
+                enforcePermissionForManagedAdmin(mCallingPackage);
                 if (tag == TAG_NONE) {
                     return getUidComplete().getHistory(template, uid, set, tag, fields);
                 } else {
@@ -498,6 +533,53 @@
         };
     }
 
+    private boolean hasAppOpsPermission(String callingPackage) {
+        final int callingUid = Binder.getCallingUid();
+        boolean appOpsAllow = false;
+        if (callingPackage != null) {
+            AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(
+                    Context.APP_OPS_SERVICE);
+
+            final int mode = appOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS,
+                    callingUid, callingPackage);
+            if (mode == AppOpsManager.MODE_DEFAULT) {
+                // The default behavior here is to check if PackageManager has given the app
+                // permission.
+                final int permissionCheck = mContext.checkCallingPermission(
+                        Manifest.permission.PACKAGE_USAGE_STATS);
+                appOpsAllow = permissionCheck == PackageManager.PERMISSION_GRANTED;
+            }
+            appOpsAllow = (mode == AppOpsManager.MODE_ALLOWED);
+        }
+        return appOpsAllow;
+    }
+
+    private void enforcePermissionForManagedAdmin(String callingPackage) {
+        boolean hasPermission = hasAppOpsPermission(callingPackage);
+        if (!hasPermission) {
+            // Profile and device owners are exempt from permission checking.
+            final int callingUid = Binder.getCallingUid();
+            final DevicePolicyManagerInternal dpmi = LocalServices.getService(
+                    DevicePolicyManagerInternal.class);
+            if (dpmi.isActiveAdminWithPolicy(callingUid, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER)
+                    || dpmi.isActiveAdminWithPolicy(callingUid,
+                            DeviceAdminInfo.USES_POLICY_DEVICE_OWNER)) {
+                return;
+            }
+        }
+        if (!hasPermission) {
+            mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
+        }
+    }
+
+    private void enforcePermission(String callingPackage) {
+        boolean appOpsAllow = hasAppOpsPermission(callingPackage);
+        if (!appOpsAllow) {
+            mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
+        }
+    }
+
+
     /**
      * Return network summary, splicing between DEV and XT stats when
      * appropriate.
diff --git a/services/core/java/com/android/server/pm/BasePermission.java b/services/core/java/com/android/server/pm/BasePermission.java
index 4f27408..ec290ef3 100644
--- a/services/core/java/com/android/server/pm/BasePermission.java
+++ b/services/core/java/com/android/server/pm/BasePermission.java
@@ -18,6 +18,9 @@
 
 import android.content.pm.PackageParser;
 import android.content.pm.PermissionInfo;
+import android.os.UserHandle;
+
+import com.android.internal.util.ArrayUtils;
 
 final class BasePermission {
     final static int TYPE_NORMAL = 0;
@@ -40,9 +43,17 @@
 
     PermissionInfo pendingInfo;
 
+    /** UID that owns the definition of this permission */
     int uid;
 
-    int[] gids;
+    /** Additional GIDs given to apps granted this permission */
+    private int[] gids;
+
+    /**
+     * Flag indicating that {@link #gids} should be adjusted based on the
+     * {@link UserHandle} the granted app is running as.
+     */
+    private boolean perUser;
 
     BasePermission(String _name, String _sourcePackage, int _type) {
         name = _name;
@@ -52,8 +63,35 @@
         protectionLevel = PermissionInfo.PROTECTION_SIGNATURE;
     }
 
+    @Override
     public String toString() {
         return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name
                 + "}";
     }
+
+    public void setGids(int[] gids, boolean perUser) {
+        this.gids = gids;
+        this.perUser = perUser;
+    }
+
+    public boolean hasGids() {
+        return ArrayUtils.isEmpty(gids);
+    }
+
+    public int[] computeGids(int userId) {
+        if (perUser) {
+            final int[] userGids = new int[gids.length];
+            for (int i = 0; i < gids.length; i++) {
+                userGids[i] = UserHandle.getUid(userId, gids[i]);
+            }
+            return userGids;
+        } else {
+            return gids;
+        }
+    }
+
+    public boolean isRuntime() {
+        return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+                == PermissionInfo.PROTECTION_DANGEROUS;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/GrantedPermissions.java b/services/core/java/com/android/server/pm/GrantedPermissions.java
deleted file mode 100644
index e87546c..0000000
--- a/services/core/java/com/android/server/pm/GrantedPermissions.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.server.pm;
-
-import android.content.pm.ApplicationInfo;
-import android.util.ArraySet;
-
-class GrantedPermissions {
-    int pkgFlags;
-    int pkgPrivateFlags;
-
-    ArraySet<String> grantedPermissions = new ArraySet<String>();
-
-    int[] gids;
-
-    GrantedPermissions(int pkgFlags, int pkgPrivateFlags) {
-        setFlags(pkgFlags);
-        setPrivateFlags(pkgPrivateFlags);
-    }
-
-    @SuppressWarnings("unchecked")
-    GrantedPermissions(GrantedPermissions base) {
-        pkgFlags = base.pkgFlags;
-        grantedPermissions = new ArraySet<>(base.grantedPermissions);
-
-        if (base.gids != null) {
-            gids = base.gids.clone();
-        }
-    }
-
-    void setFlags(int pkgFlags) {
-        this.pkgFlags = pkgFlags
-                & (ApplicationInfo.FLAG_SYSTEM
-                        | ApplicationInfo.FLAG_EXTERNAL_STORAGE);
-    }
-
-    void setPrivateFlags(int pkgPrivateFlags) {
-        this.pkgPrivateFlags = pkgPrivateFlags
-                & (ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
-                        | ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK);
-    }
-}
diff --git a/services/core/java/com/android/server/pm/IntentFilterVerificationKey.java b/services/core/java/com/android/server/pm/IntentFilterVerificationKey.java
new file mode 100644
index 0000000..399b03c
--- /dev/null
+++ b/services/core/java/com/android/server/pm/IntentFilterVerificationKey.java
@@ -0,0 +1,63 @@
+/*
+ * 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.server.pm;
+
+import java.util.Arrays;
+
+/**
+ * This is the key for the map of {@link android.content.pm.IntentFilterVerificationInfo}s
+ * maintained by the  {@link com.android.server.pm.PackageManagerService}
+ */
+class IntentFilterVerificationKey {
+    public String domains;
+    public String packageName;
+    public String className;
+
+    public IntentFilterVerificationKey(String[] domains, String packageName, String className) {
+        StringBuilder sb = new StringBuilder();
+        for (String host : domains) {
+            sb.append(host);
+        }
+        this.domains = sb.toString();
+        this.packageName = packageName;
+        this.className = className;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        IntentFilterVerificationKey that = (IntentFilterVerificationKey) o;
+
+        if (domains != null ? !domains.equals(that.domains) : that.domains != null) return false;
+        if (className != null ? !className.equals(that.className) : that.className != null)
+            return false;
+        if (packageName != null ? !packageName.equals(that.packageName) : that.packageName != null)
+            return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = domains != null ? domains.hashCode() : 0;
+        result = 31 * result + (packageName != null ? packageName.hashCode() : 0);
+        result = 31 * result + (className != null ? className.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/IntentFilterVerificationResponse.java b/services/core/java/com/android/server/pm/IntentFilterVerificationResponse.java
new file mode 100644
index 0000000..ead399b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/IntentFilterVerificationResponse.java
@@ -0,0 +1,43 @@
+/*
+ * 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.server.pm;
+
+
+import java.util.List;
+
+/* package private */ class IntentFilterVerificationResponse {
+    public final int callerUid;
+    public final int code;
+    public final List<String> failedDomains;
+
+    public IntentFilterVerificationResponse(int callerUid, int code, List<String> failedDomains) {
+        this.callerUid = callerUid;
+        this.code = code;
+        this.failedDomains = failedDomains;
+    }
+
+    public String getFailedDomainsString() {
+        StringBuilder sb = new StringBuilder();
+        for (String domain : failedDomains) {
+            if (sb.length() > 0) {
+                sb.append(" ");
+            }
+            sb.append(domain);
+        }
+        return sb.toString();
+    }
+}
diff --git a/services/core/java/com/android/server/pm/IntentFilterVerificationState.java b/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
new file mode 100644
index 0000000..c09d6ae
--- /dev/null
+++ b/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
@@ -0,0 +1,125 @@
+/*
+ * 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.server.pm;
+
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+public class IntentFilterVerificationState {
+    static final String TAG = IntentFilterVerificationState.class.getName();
+
+    public final static int STATE_UNDEFINED = 0;
+    public final static int STATE_VERIFICATION_PENDING = 1;
+    public final static int STATE_VERIFICATION_SUCCESS = 2;
+    public final static int STATE_VERIFICATION_FAILURE = 3;
+
+    private int mRequiredVerifierUid = 0;
+
+    private int mState;
+
+    private ArrayList<PackageParser.ActivityIntentInfo> mFilters = new ArrayList<>();
+    private ArraySet<String> mHosts = new ArraySet<>();
+    private int mUserId;
+
+    private String mPackageName;
+    private boolean mVerificationComplete;
+
+    public IntentFilterVerificationState(int verifierUid, int userId, String packageName) {
+        mRequiredVerifierUid = verifierUid;
+        mUserId = userId;
+        mPackageName = packageName;
+        mState = STATE_UNDEFINED;
+        mVerificationComplete = false;
+    }
+
+    public void setState(int state) {
+        if (state > STATE_VERIFICATION_FAILURE || state < STATE_UNDEFINED) {
+            mState = STATE_UNDEFINED;
+        } else {
+            mState = state;
+        }
+    }
+
+    public int getState() {
+        return mState;
+    }
+
+    public void setPendingState() {
+        setState(STATE_VERIFICATION_PENDING);
+    }
+
+    public ArrayList<PackageParser.ActivityIntentInfo> getFilters() {
+        return mFilters;
+    }
+
+    public boolean isVerificationComplete() {
+        return mVerificationComplete;
+    }
+
+    public boolean isVerified() {
+        if (mVerificationComplete) {
+            return (mState == STATE_VERIFICATION_SUCCESS);
+        }
+        return false;
+    }
+
+    public int getUserId() {
+        return mUserId;
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public String getHostsString() {
+        StringBuilder sb = new StringBuilder();
+        final int count = mHosts.size();
+        for (int i=0; i<count; i++) {
+            if (i > 0) {
+                sb.append(" ");
+            }
+            sb.append(mHosts.valueAt(i));
+        }
+        return sb.toString();
+    }
+
+    public boolean setVerifierResponse(int callerUid, int code) {
+        if (mRequiredVerifierUid == callerUid) {
+            int state = STATE_UNDEFINED;
+            if (code == PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS) {
+                state = STATE_VERIFICATION_SUCCESS;
+            } else if (code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) {
+                state = STATE_VERIFICATION_FAILURE;
+            }
+            mVerificationComplete = true;
+            setState(state);
+            return true;
+        }
+        Log.d(TAG, "Cannot set verifier response with callerUid:" + callerUid + " and code:" +
+                code + " as required verifierUid is:" + mRequiredVerifierUid);
+        return false;
+    }
+
+    public void addFilter(PackageParser.ActivityIntentInfo filter) {
+        mFilters.add(filter);
+        mHosts.addAll(filter.getHostsList());
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index df2d3e5..03f90ee 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -44,6 +44,10 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
 import static android.content.pm.PackageManager.INSTALL_FORWARD_LOCK;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
 import static android.content.pm.PackageParser.isApkFile;
 import static android.os.Process.PACKAGE_INFO_GID;
 import static android.os.Process.SYSTEM_UID;
@@ -54,12 +58,13 @@
 import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
 import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
 import static com.android.internal.util.ArrayUtils.appendInt;
-import static com.android.internal.util.ArrayUtils.removeInt;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
 import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
 
+import android.Manifest;
+import android.content.pm.IntentFilterVerificationInfo;
 import android.util.ArrayMap;
 
 import com.android.internal.R;
@@ -249,6 +254,9 @@
     private static final boolean DEBUG_DEXOPT = false;
     private static final boolean DEBUG_ABI_SELECTION = false;
 
+    static final boolean RUNTIME_PERMISSIONS_ENABLED =
+            SystemProperties.getInt("ro.runtime.permissions.enabled", 0) == 1;
+
     private static final int RADIO_UID = Process.PHONE_UID;
     private static final int LOG_UID = Process.LOG_UID;
     private static final int NFC_UID = Process.NFC_UID;
@@ -273,6 +281,7 @@
     static final int SCAN_TRUSTED_OVERLAY = 1<<9;
     static final int SCAN_DELETE_DATA_ON_FAILURES = 1<<10;
     static final int SCAN_REPLACING = 1<<11;
+    static final int SCAN_REQUIRE_KNOWN = 1<<12;
 
     static final int REMOVE_CHATTY = 1<<16;
 
@@ -321,10 +330,28 @@
             DEFAULT_CONTAINER_PACKAGE,
             "com.android.defcontainer.DefaultContainerService");
 
+    private static final String KILL_APP_REASON_GIDS_CHANGED =
+            "permission grant or revoke changed gids";
+
+    private static final String KILL_APP_REASON_PERMISSIONS_REVOKED =
+            "permissions revoked";
+
     private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
 
     private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
 
+    /** Permission grant: not grant the permission. */
+    private static final int GRANT_DENIED = 1;
+
+    /** Permission grant: grant the permission as an install permission. */
+    private static final int GRANT_INSTALL = 2;
+
+    /** Permission grant: grant the permission as a runtime one. */
+    private static final int GRANT_RUNTIME = 3;
+
+    /** Permission grant: grant as runtime a permission that was granted as an install time one. */
+    private static final int GRANT_UPGRADE = 4;
+
     final ServiceThread mHandlerThread;
 
     final PackageHandler mHandler;
@@ -483,6 +510,231 @@
 
     boolean mResolverReplaced = false;
 
+    private final ComponentName mIntentFilterVerifierComponent;
+    private int mIntentFilterVerificationToken = 0;
+
+    final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
+            = new SparseArray<IntentFilterVerificationState>();
+
+    private interface IntentFilterVerifier<T extends IntentFilter> {
+        boolean addOneIntentFilterVerification(int verifierId, int userId, int verificationId,
+                                               T filter, String packageName);
+        void startVerifications(int userId);
+        void receiveVerificationResponse(int verificationId);
+    }
+
+    private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> {
+        private Context mContext;
+        private ComponentName mIntentFilterVerifierComponent;
+        private ArrayList<Integer> mCurrentIntentFilterVerifications = new ArrayList<Integer>();
+
+        public IntentVerifierProxy(Context context, ComponentName verifierComponent) {
+            mContext = context;
+            mIntentFilterVerifierComponent = verifierComponent;
+        }
+
+        private String getDefaultScheme() {
+            // TODO: replace SCHEME_HTTP with SCHEME_HTTPS
+            return IntentFilter.SCHEME_HTTP;
+        }
+
+        @Override
+        public void startVerifications(int userId) {
+            // Launch verifications requests
+            int count = mCurrentIntentFilterVerifications.size();
+            for (int n=0; n<count; n++) {
+                int verificationId = mCurrentIntentFilterVerifications.get(n);
+                final IntentFilterVerificationState ivs =
+                        mIntentFilterVerificationStates.get(verificationId);
+
+                String packageName = ivs.getPackageName();
+                boolean modified = false;
+
+                ArrayList<PackageParser.ActivityIntentInfo> filters = ivs.getFilters();
+                final int filterCount = filters.size();
+                for (int m=0; m<filterCount; m++) {
+                    PackageParser.ActivityIntentInfo filter = filters.get(m);
+                    synchronized (mPackages) {
+                        modified = mSettings.createIntentFilterVerificationIfNeededLPw(
+                                packageName, filter.getHosts());
+                    }
+                }
+                synchronized (mPackages) {
+                    if (modified) {
+                        scheduleWriteSettingsLocked();
+                    }
+                }
+                sendVerificationRequest(userId, verificationId, ivs);
+            }
+            mCurrentIntentFilterVerifications.clear();
+        }
+
+        private void sendVerificationRequest(int userId, int verificationId,
+                                             IntentFilterVerificationState ivs) {
+
+            Intent verificationIntent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
+            verificationIntent.putExtra(
+                    PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID,
+                    verificationId);
+            verificationIntent.putExtra(
+                    PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME,
+                    getDefaultScheme());
+            verificationIntent.putExtra(
+                    PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS,
+                    ivs.getHostsString());
+            verificationIntent.putExtra(
+                    PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME,
+                    ivs.getPackageName());
+            verificationIntent.setComponent(mIntentFilterVerifierComponent);
+            verificationIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+
+            UserHandle user = new UserHandle(userId);
+            mContext.sendBroadcastAsUser(verificationIntent, user);
+            Slog.d(TAG, "Sending IntenFilter verification broadcast");
+        }
+
+        public void receiveVerificationResponse(int verificationId) {
+            IntentFilterVerificationState ivs = mIntentFilterVerificationStates.get(verificationId);
+
+            final boolean verified = ivs.isVerified();
+
+            ArrayList<PackageParser.ActivityIntentInfo> filters = ivs.getFilters();
+            final int count = filters.size();
+            for (int n=0; n<count; n++) {
+                PackageParser.ActivityIntentInfo filter = filters.get(n);
+                filter.setVerified(verified);
+
+                Slog.d(TAG, "IntentFilter " + filter.toString() + " verified with result:"
+                        + verified + " and hosts:" + ivs.getHostsString());
+            }
+
+            mIntentFilterVerificationStates.remove(verificationId);
+
+            final String packageName = ivs.getPackageName();
+            IntentFilterVerificationInfo ivi = null;
+
+            synchronized (mPackages) {
+                ivi = mSettings.getIntentFilterVerificationLPr(packageName);
+            }
+            if (ivi == null) {
+                Slog.w(TAG, "IntentFilterVerificationInfo not found for verificationId:"
+                        + verificationId + " packageName:" + packageName);
+                return;
+            }
+            Slog.d(TAG, "Updating IntentFilterVerificationInfo for verificationId: "
+                    + verificationId);
+
+            synchronized (mPackages) {
+                if (verified) {
+                    ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS);
+                } else {
+                    ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK);
+                }
+                scheduleWriteSettingsLocked();
+
+                final int userId = ivs.getUserId();
+                if (userId != UserHandle.USER_ALL) {
+                    final int userStatus =
+                            mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
+
+                    int updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+                    boolean needUpdate = false;
+
+                    // We cannot override the STATUS_ALWAYS / STATUS_NEVER states if they have
+                    // already been set by the User thru the Disambiguation dialog
+                    switch (userStatus) {
+                        case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
+                            if (verified) {
+                                updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+                            } else {
+                                updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+                            }
+                            needUpdate = true;
+                            break;
+
+                        case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
+                            if (verified) {
+                                updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+                                needUpdate = true;
+                            }
+                            break;
+
+                        default:
+                            // Nothing to do
+                    }
+
+                    if (needUpdate) {
+                        mSettings.updateIntentFilterVerificationStatusLPw(
+                                packageName, updatedStatus, userId);
+                        scheduleWritePackageRestrictionsLocked(userId);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public boolean addOneIntentFilterVerification(int verifierId, int userId, int verificationId,
+                    ActivityIntentInfo filter, String packageName) {
+            if (!(filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
+                    filter.hasDataScheme(IntentFilter.SCHEME_HTTPS))) {
+                Slog.d(TAG, "IntentFilter does not contain HTTP nor HTTPS data scheme");
+                return false;
+            }
+            IntentFilterVerificationState ivs = mIntentFilterVerificationStates.get(verificationId);
+            if (ivs == null) {
+                ivs = createDomainVerificationState(verifierId, userId, verificationId,
+                        packageName);
+            }
+            ArrayList<String> hosts = filter.getHostsList();
+            if (!hasValidHosts(hosts)) {
+                return false;
+            }
+            ivs.addFilter(filter);
+            return true;
+        }
+
+        private IntentFilterVerificationState createDomainVerificationState(int verifierId,
+                int userId, int verificationId, String packageName) {
+            IntentFilterVerificationState ivs = new IntentFilterVerificationState(
+                    verifierId, userId, packageName);
+            ivs.setPendingState();
+            synchronized (mPackages) {
+                mIntentFilterVerificationStates.append(verificationId, ivs);
+                mCurrentIntentFilterVerifications.add(verificationId);
+            }
+            return ivs;
+        }
+
+        private boolean hasValidHosts(ArrayList<String> hosts) {
+            if (hosts.size() == 0) {
+                Slog.d(TAG, "IntentFilter does not contain any data hosts");
+                return false;
+            }
+            String hostEndBase = null;
+            for (String host : hosts) {
+                String[] hostParts = host.split("\\.");
+                // Should be at minimum a host like "example.com"
+                if (hostParts.length < 2) {
+                    Slog.d(TAG, "IntentFilter does not contain a valid data host name: " + host);
+                    return false;
+                }
+                // Verify that we have the same ending domain
+                int length = hostParts.length;
+                String hostEnd = hostParts[length - 1] + hostParts[length - 2];
+                if (hostEndBase == null) {
+                    hostEndBase = hostEnd;
+                }
+                if (!hostEnd.equalsIgnoreCase(hostEndBase)) {
+                    Slog.d(TAG, "IntentFilter does not contain the same data domains");
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    private IntentFilterVerifier mIntentFilterVerifier;
+
     // Set of pending broadcasts for aggregating enable/disable of components.
     static class PendingPackageBroadcasts {
         // for each user id, a map of <package name -> components within that package>
@@ -569,6 +821,8 @@
     static final int WRITE_PACKAGE_RESTRICTIONS = 14;
     static final int PACKAGE_VERIFIED = 15;
     static final int CHECK_PENDING_VERIFICATION = 16;
+    static final int START_INTENT_FILTER_VERIFICATIONS = 17;
+    static final int INTENT_FILTER_VERIFIED = 18;
 
     static final int WRITE_SETTINGS_DELAY = 10*1000;  // 10 seconds
 
@@ -994,6 +1248,15 @@
                             res.removedInfo.sendBroadcast(false, true, false);
                             Bundle extras = new Bundle(1);
                             extras.putInt(Intent.EXTRA_UID, res.uid);
+
+                            // Now that we successfully installed the package, grant runtime
+                            // permissions if requested before broadcasting the install.
+                            if ((args.installFlags
+                                    & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0) {
+                                grantRequestedRuntimePermissions(res.pkg,
+                                        args.user.getIdentifier());
+                            }
+
                             // Determine the set of users who are adding this
                             // package for the first time vs. those who are seeing
                             // an update.
@@ -1210,6 +1473,80 @@
 
                     break;
                 }
+                case START_INTENT_FILTER_VERIFICATIONS: {
+                    int userId = msg.arg1;
+                    int verifierUid = msg.arg2;
+                    PackageParser.Package pkg = (PackageParser.Package)msg.obj;
+
+                    verifyIntentFiltersIfNeeded(userId, verifierUid, pkg);
+                    break;
+                }
+                case INTENT_FILTER_VERIFIED: {
+                    final int verificationId = msg.arg1;
+
+                    final IntentFilterVerificationState state = mIntentFilterVerificationStates.get(
+                            verificationId);
+                    if (state == null) {
+                        Slog.w(TAG, "Invalid IntentFilter verification token "
+                                + verificationId + " received");
+                        break;
+                    }
+
+                    final int userId = state.getUserId();
+
+                    Slog.d(TAG, "Processing IntentFilter verification with token:"
+                            + verificationId + " and userId:" + userId);
+
+                    final IntentFilterVerificationResponse response =
+                            (IntentFilterVerificationResponse) msg.obj;
+
+                    state.setVerifierResponse(response.callerUid, response.code);
+
+                    Slog.d(TAG, "IntentFilter verification with token:" + verificationId
+                            + " and userId:" + userId
+                            + " is settings verifier response with response code:"
+                            + response.code);
+
+                    if (response.code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) {
+                        Slog.d(TAG, "Domains failing verification: "
+                                + response.getFailedDomainsString());
+                    }
+
+                    if (state.isVerificationComplete()) {
+                        mIntentFilterVerifier.receiveVerificationResponse(verificationId);
+                    } else {
+                        Slog.d(TAG, "IntentFilter verification with token:" + verificationId
+                                + " was not said to be complete");
+                    }
+
+                    break;
+                }
+            }
+        }
+    }
+
+    private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int userId) {
+        if (userId >= UserHandle.USER_OWNER) {
+            grantRequestedRuntimePermissionsForUser(pkg, userId);
+        } else if (userId == UserHandle.USER_ALL) {
+            for (int someUserId : UserManagerService.getInstance().getUserIds()) {
+                grantRequestedRuntimePermissionsForUser(pkg, someUserId);
+            }
+        }
+    }
+
+    private void grantRequestedRuntimePermissionsForUser(PackageParser.Package pkg, int userId) {
+        SettingBase sb = (SettingBase) pkg.mExtras;
+        if (sb == null) {
+            return;
+        }
+
+        PermissionsState permissionsState = sb.getPermissionsState();
+
+        for (String permission : pkg.requestedPermissions) {
+            BasePermission bp = mSettings.mPermissions.get(permission);
+            if (bp != null && bp.isRuntime()) {
+                permissionsState.grantRuntimePermission(bp, userId);
             }
         }
     }
@@ -1243,7 +1580,7 @@
         }
     }
 
-    public static final PackageManagerService main(Context context, Installer installer,
+    public static PackageManagerService main(Context context, Installer installer,
             boolean factoryTest, boolean onlyCore) {
         PackageManagerService m = new PackageManagerService(context, installer,
                 factoryTest, onlyCore);
@@ -1293,7 +1630,7 @@
         mOnlyCore = onlyCore;
         mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
         mMetrics = new DisplayMetrics();
-        mSettings = new Settings(context);
+        mSettings = new Settings(mPackages);
         mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
         mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
@@ -1374,7 +1711,7 @@
                     mSettings.mPermissions.put(perm.name, bp);
                 }
                 if (perm.gids != null) {
-                    bp.gids = appendInts(bp.gids, perm.gids);
+                    bp.setGids(perm.gids, perm.perUser);
                 }
             }
 
@@ -1644,10 +1981,10 @@
             if (!mOnlyCore) {
                 EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                         SystemClock.uptimeMillis());
-                scanDirLI(mAppInstallDir, 0, scanFlags, 0);
+                scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
 
                 scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
-                        scanFlags, 0);
+                        scanFlags | SCAN_REQUIRE_KNOWN, 0);
 
                 /**
                  * Remove disable package settings for any updated system
@@ -1755,7 +2092,14 @@
                     + mSettings.mInternalSdkPlatform + " to " + mSdkVersion
                     + "; regranting permissions for internal storage");
             mSettings.mInternalSdkPlatform = mSdkVersion;
-            
+
+            // For now runtime permissions are toggled via a system property.
+            if (!RUNTIME_PERMISSIONS_ENABLED) {
+                // Remove the runtime permissions state if the feature
+                // was disabled by flipping the system property.
+                mSettings.deleteRuntimePermissionsFiles();
+            }
+
             updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
                     | (regrantPermissions
                             ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
@@ -1787,13 +2131,17 @@
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
                     SystemClock.uptimeMillis());
 
-
             mRequiredVerifierPackage = getRequiredVerifierLPr();
+
+            mInstallerService = new PackageInstallerService(context, this, mAppInstallDir);
+
+            mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
+            mIntentFilterVerifier = new IntentVerifierProxy(mContext,
+                    mIntentFilterVerifierComponent);
+
         } // synchronized (mPackages)
         } // synchronized (mInstallLock)
 
-        mInstallerService = new PackageInstallerService(context, this, mAppInstallDir);
-
         // Now after opening every single application zip, make sure they
         // are all flushed.  Not really needed, but keeps things nice and
         // tidy.
@@ -1832,14 +2180,8 @@
 
             final String packageName = info.activityInfo.packageName;
 
-            final PackageSetting ps = mSettings.mPackages.get(packageName);
-            if (ps == null) {
-                continue;
-            }
-
-            final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
-            if (!gp.grantedPermissions
-                    .contains(android.Manifest.permission.PACKAGE_VERIFICATION_AGENT)) {
+            if (checkPermission(android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
+                    packageName, UserHandle.USER_OWNER) != PackageManager.PERMISSION_GRANTED) {
                 continue;
             }
 
@@ -1853,6 +2195,46 @@
         return requiredVerifier;
     }
 
+    private ComponentName getIntentFilterVerifierComponentNameLPr() {
+        final Intent verification = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
+        final List<ResolveInfo> receivers = queryIntentReceivers(verification, PACKAGE_MIME_TYPE,
+                PackageManager.GET_DISABLED_COMPONENTS, 0 /* userId */);
+
+        ComponentName verifierComponentName = null;
+
+        int priority = -1000;
+        final int N = receivers.size();
+        for (int i = 0; i < N; i++) {
+            final ResolveInfo info = receivers.get(i);
+
+            if (info.activityInfo == null) {
+                continue;
+            }
+
+            final String packageName = info.activityInfo.packageName;
+
+            final PackageSetting ps = mSettings.mPackages.get(packageName);
+            if (ps == null) {
+                continue;
+            }
+
+            if (checkPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
+                    packageName, UserHandle.USER_OWNER) != PackageManager.PERMISSION_GRANTED) {
+                continue;
+            }
+
+            // Select the IntentFilterVerifier with the highest priority
+            if (priority < info.priority) {
+                priority = info.priority;
+                verifierComponentName = new ComponentName(packageName, info.activityInfo.name);
+                Slog.d(TAG, "Selecting IntentFilterVerifier: " + verifierComponentName +
+                        " with priority: " + info.priority);
+            }
+        }
+
+        return verifierComponentName;
+    }
+
     @Override
     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
             throws RemoteException {
@@ -1895,27 +2277,21 @@
         return cur;
     }
 
-    static int[] removeInts(int[] cur, int[] rem) {
-        if (rem == null) return cur;
-        if (cur == null) return cur;
-        final int N = rem.length;
-        for (int i=0; i<N; i++) {
-            cur = removeInt(cur, rem[i]);
-        }
-        return cur;
-    }
-
     PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
         final PackageSetting ps = (PackageSetting) p.mExtras;
         if (ps == null) {
             return null;
         }
-        final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+
+        final PermissionsState permissionsState = ps.getPermissionsState();
+
+        final int[] gids = permissionsState.computeGids(userId);
+        final Set<String> permissions = permissionsState.getPermissions(userId);
         final PackageUserState state = ps.readUserState(userId);
-        return PackageParser.generatePackageInfo(p, gp.gids, flags,
-                ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions,
-                state, userId);
+
+        return PackageParser.generatePackageInfo(p, gids, flags,
+                ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId);
     }
 
     @Override
@@ -1986,6 +2362,7 @@
     public int getPackageUid(String packageName, int userId) {
         if (!sUserManager.exists(userId)) return -1;
         enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get package uid");
+
         // reader
         synchronized (mPackages) {
             PackageParser.Package p = mPackages.get(packageName);
@@ -2002,22 +2379,30 @@
     }
 
     @Override
-    public int[] getPackageGids(String packageName) {
+    public int[] getPackageGids(String packageName, int userId) throws RemoteException {
+        if (!sUserManager.exists(userId)) {
+            return null;
+        }
+
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false,
+                "getPackageGids");
+
         // reader
         synchronized (mPackages) {
             PackageParser.Package p = mPackages.get(packageName);
-            if (DEBUG_PACKAGE_INFO)
+            if (DEBUG_PACKAGE_INFO) {
                 Log.v(TAG, "getPackageGids" + packageName + ": " + p);
+            }
             if (p != null) {
-                final PackageSetting ps = (PackageSetting)p.mExtras;
-                return ps.getGids();
+                PackageSetting ps = (PackageSetting) p.mExtras;
+                return ps.getPermissionsState().computeGids(userId);
             }
         }
-        // stupid thing to indicate an error.
-        return new int[0];
+
+        return null;
     }
 
-    static final PermissionInfo generatePermissionInfo(
+    static PermissionInfo generatePermissionInfo(
             BasePermission bp, int flags) {
         if (bp.perm != null) {
             return PackageParser.generatePermissionInfo(bp.perm, flags);
@@ -2381,30 +2766,37 @@
     }
 
     @Override
-    public int checkPermission(String permName, String pkgName) {
+    public int checkPermission(String permName, String pkgName, int userId) {
+        if (!sUserManager.exists(userId)) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+
         synchronized (mPackages) {
-            PackageParser.Package p = mPackages.get(pkgName);
+            final PackageParser.Package p = mPackages.get(pkgName);
             if (p != null && p.mExtras != null) {
-                PackageSetting ps = (PackageSetting)p.mExtras;
-                if (ps.sharedUser != null) {
-                    if (ps.sharedUser.grantedPermissions.contains(permName)) {
-                        return PackageManager.PERMISSION_GRANTED;
-                    }
-                } else if (ps.grantedPermissions.contains(permName)) {
+                final PackageSetting ps = (PackageSetting) p.mExtras;
+                if (ps.getPermissionsState().hasPermission(permName, userId)) {
                     return PackageManager.PERMISSION_GRANTED;
                 }
             }
         }
+
         return PackageManager.PERMISSION_DENIED;
     }
 
     @Override
     public int checkUidPermission(String permName, int uid) {
+        final int userId = UserHandle.getUserId(uid);
+
+        if (!sUserManager.exists(userId)) {
+            return PackageManager.PERMISSION_DENIED;
+        }
+
         synchronized (mPackages) {
             Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
             if (obj != null) {
-                GrantedPermissions gp = (GrantedPermissions)obj;
-                if (gp.grantedPermissions.contains(permName)) {
+                final SettingBase ps = (SettingBase) obj;
+                if (ps.getPermissionsState().hasPermission(permName, userId)) {
                     return PackageManager.PERMISSION_GRANTED;
                 }
             } else {
@@ -2414,6 +2806,7 @@
                 }
             }
         }
+
         return PackageManager.PERMISSION_DENIED;
     }
 
@@ -2620,121 +3013,132 @@
         }
     }
 
-    private static void checkGrantRevokePermissions(PackageParser.Package pkg, BasePermission bp) {
+    private static void enforceDeclaredAsUsedAndRuntimePermission(PackageParser.Package pkg,
+            BasePermission bp) {
         int index = pkg.requestedPermissions.indexOf(bp.name);
         if (index == -1) {
             throw new SecurityException("Package " + pkg.packageName
                     + " has not requested permission " + bp.name);
         }
-        boolean isNormal =
-                ((bp.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE)
-                        == PermissionInfo.PROTECTION_NORMAL);
-        boolean isDangerous =
-                ((bp.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE)
-                        == PermissionInfo.PROTECTION_DANGEROUS);
-        boolean isDevelopment =
-                ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0);
-
-        if (!isNormal && !isDangerous && !isDevelopment) {
+        if (!bp.isRuntime()) {
             throw new SecurityException("Permission " + bp.name
                     + " is not a changeable permission type");
         }
-
-        if (isNormal || isDangerous) {
-            if (pkg.requestedPermissionsRequired.get(index)) {
-                throw new SecurityException("Can't change " + bp.name
-                        + ". It is required by the application");
-            }
-        }
     }
 
     @Override
-    public void grantPermission(String packageName, String permissionName) {
+    public boolean grantPermission(String packageName, String name, int userId) {
+        if (!RUNTIME_PERMISSIONS_ENABLED) {
+            return false;
+        }
+
+        if (!sUserManager.exists(userId)) {
+            return false;
+        }
+
         mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null);
+                android.Manifest.permission.GRANT_REVOKE_PERMISSIONS,
+                "grantPermission");
+
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+                "grantPermission");
+
+        boolean gidsChanged = false;
+        final SettingBase sb;
+
         synchronized (mPackages) {
             final PackageParser.Package pkg = mPackages.get(packageName);
             if (pkg == null) {
                 throw new IllegalArgumentException("Unknown package: " + packageName);
             }
-            final BasePermission bp = mSettings.mPermissions.get(permissionName);
+
+            final BasePermission bp = mSettings.mPermissions.get(name);
             if (bp == null) {
-                throw new IllegalArgumentException("Unknown permission: " + permissionName);
+                throw new IllegalArgumentException("Unknown permission: " + name);
             }
 
-            checkGrantRevokePermissions(pkg, bp);
+            enforceDeclaredAsUsedAndRuntimePermission(pkg, bp);
 
-            final PackageSetting ps = (PackageSetting) pkg.mExtras;
-            if (ps == null) {
-                return;
+            sb = (SettingBase) pkg.mExtras;
+            if (sb == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
             }
-            final GrantedPermissions gp = (ps.sharedUser != null) ? ps.sharedUser : ps;
-            if (gp.grantedPermissions.add(permissionName)) {
-                if (ps.haveGids) {
-                    gp.gids = appendInts(gp.gids, bp.gids);
+
+            final PermissionsState permissionsState = sb.getPermissionsState();
+
+            final int result = permissionsState.grantRuntimePermission(bp, userId);
+            switch (result) {
+                case PermissionsState.PERMISSION_OPERATION_FAILURE: {
+                    return false;
                 }
-                mSettings.writeLPr();
+
+                case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
+                    gidsChanged = true;
+                } break;
             }
+
+            // Not critical if that is lost - app has to request again.
+            mSettings.writeRuntimePermissionsForUserLPr(userId, false);
         }
+
+        if (gidsChanged) {
+            killSettingPackagesForUser(sb, userId, KILL_APP_REASON_GIDS_CHANGED);
+        }
+
+        return true;
     }
 
     @Override
-    public void revokePermission(String packageName, String permissionName) {
-        int changedAppId = -1;
+    public boolean revokePermission(String packageName, String name, int userId) {
+        if (!RUNTIME_PERMISSIONS_ENABLED) {
+            return false;
+        }
+
+        if (!sUserManager.exists(userId)) {
+            return false;
+        }
+
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.GRANT_REVOKE_PERMISSIONS,
+                "revokePermission");
+
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+                "revokePermission");
+
+        final SettingBase sb;
 
         synchronized (mPackages) {
             final PackageParser.Package pkg = mPackages.get(packageName);
             if (pkg == null) {
                 throw new IllegalArgumentException("Unknown package: " + packageName);
             }
-            if (pkg.applicationInfo.uid != Binder.getCallingUid()) {
-                mContext.enforceCallingOrSelfPermission(
-                        android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null);
-            }
-            final BasePermission bp = mSettings.mPermissions.get(permissionName);
+
+            final BasePermission bp = mSettings.mPermissions.get(name);
             if (bp == null) {
-                throw new IllegalArgumentException("Unknown permission: " + permissionName);
+                throw new IllegalArgumentException("Unknown permission: " + name);
             }
 
-            checkGrantRevokePermissions(pkg, bp);
+            enforceDeclaredAsUsedAndRuntimePermission(pkg, bp);
 
-            final PackageSetting ps = (PackageSetting) pkg.mExtras;
-            if (ps == null) {
-                return;
+            sb = (SettingBase) pkg.mExtras;
+            if (sb == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
             }
-            final GrantedPermissions gp = (ps.sharedUser != null) ? ps.sharedUser : ps;
-            if (gp.grantedPermissions.remove(permissionName)) {
-                gp.grantedPermissions.remove(permissionName);
-                if (ps.haveGids) {
-                    gp.gids = removeInts(gp.gids, bp.gids);
-                }
-                mSettings.writeLPr();
-                changedAppId = ps.appId;
+
+            final PermissionsState permissionsState = sb.getPermissionsState();
+
+            if (permissionsState.revokeRuntimePermission(bp, userId) ==
+                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                return false;
             }
+
+            // Critical, after this call all should never have the permission.
+            mSettings.writeRuntimePermissionsForUserLPr(userId, true);
         }
 
-        if (changedAppId >= 0) {
-            // We changed the perm on someone, kill its processes.
-            IActivityManager am = ActivityManagerNative.getDefault();
-            if (am != null) {
-                final int callingUserId = UserHandle.getCallingUserId();
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    //XXX we should only revoke for the calling user's app permissions,
-                    // but for now we impact all users.
-                    //am.killUid(UserHandle.getUid(callingUserId, changedAppId),
-                    //        "revoke " + permissionName);
-                    int[] users = sUserManager.getUserIds();
-                    for (int user : users) {
-                        am.killUid(UserHandle.getUid(user, changedAppId),
-                                "revoke " + permissionName);
-                    }
-                } catch (RemoteException e) {
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-            }
-        }
+        killSettingPackagesForUser(sb, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
+
+        return true;
     }
 
     @Override
@@ -2794,6 +3198,46 @@
         }
     }
 
+    private void killSettingPackagesForUser(SettingBase sb, int userId, String reason) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            if (sb instanceof SharedUserSetting) {
+                SharedUserSetting sus = (SharedUserSetting) sb;
+                final int packageCount = sus.packages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    PackageSetting susPs = sus.packages.valueAt(i);
+                    if (userId == UserHandle.USER_ALL) {
+                        killApplication(susPs.pkg.packageName, susPs.appId, reason);
+                    } else {
+                        final int uid = UserHandle.getUid(userId, susPs.appId);
+                        killUid(uid, reason);
+                    }
+                }
+            } else if (sb instanceof PackageSetting) {
+                PackageSetting ps = (PackageSetting) sb;
+                if (userId == UserHandle.USER_ALL) {
+                    killApplication(ps.pkg.packageName, ps.appId, reason);
+                } else {
+                    final int uid = UserHandle.getUid(userId, ps.appId);
+                    killUid(uid, reason);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private static void killUid(int uid, String reason) {
+        IActivityManager am = ActivityManagerNative.getDefault();
+        if (am != null) {
+            try {
+                am.killUid(uid, reason);
+            } catch (RemoteException e) {
+                /* ignore - same process */
+            }
+        }
+    }
+
     /**
      * Compares two sets of signatures. Returns:
      * <br />
@@ -3414,14 +3858,20 @@
                 resolveInfo = queryCrossProfileIntents(
                         matchingFilters, intent, resolvedType, flags, userId);
 
-                // Check for results in the current profile.
+                // Check for results in the current profile. Adding GET_RESOLVED_FILTER flags
+                // as we need it later
                 List<ResolveInfo> result = mActivities.queryIntent(
                         intent, resolvedType, flags, userId);
                 if (resolveInfo != null) {
                     result.add(resolveInfo);
                     Collections.sort(result, mResolvePrioritySorter);
                 }
-                return filterIfNotPrimaryUser(result, userId);
+                result = filterIfNotPrimaryUser(result, userId);
+                if (result.size() > 1) {
+                    return filterCandidatesWithDomainPreferedActivitiesLPw(result);
+                }
+
+                return result;
             }
             final PackageParser.Package pkg = mPackages.get(pkgName);
             if (pkg != null) {
@@ -3452,6 +3902,49 @@
         return resolveInfos;
     }
 
+    private List<ResolveInfo> filterCandidatesWithDomainPreferedActivitiesLPw(
+            List<ResolveInfo> candidates) {
+        if (DEBUG_PREFERRED) {
+            Slog.v("TAG", "Filtering results with prefered activities. Candidates count: " +
+                    candidates.size());
+        }
+        final int userId = UserHandle.getCallingUserId();
+        ArrayList<ResolveInfo> result = new ArrayList<ResolveInfo>(candidates);
+        synchronized (mPackages) {
+            final int count = result.size();
+            for (int n = count-1; n >= 0; n--) {
+                ResolveInfo info = result.get(n);
+                if (!info.filterNeedsVerification) {
+                    continue;
+                }
+                String packageName = info.activityInfo.packageName;
+                PackageSetting ps = mSettings.mPackages.get(packageName);
+                if (ps != null) {
+                    // Try to get the status from User settings first
+                    int status = ps.getDomainVerificationStatusForUser(userId);
+                    // if none available, get the master status
+                    if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+                        if (ps.getIntentFilterVerificationInfo() != null) {
+                            status = ps.getIntentFilterVerificationInfo().getStatus();
+                        }
+                    }
+                    if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
+                        result.clear();
+                        result.add(info);
+                        // We break the for loop as we are good to go
+                        break;
+                    } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
+                        result.remove(n);
+                    }
+                }
+            }
+        }
+        if (DEBUG_PREFERRED) {
+            Slog.v("TAG", "Filtered results with prefered activities. New candidates count: " +
+                    result.size());
+        }
+        return result;
+    }
 
     private ResolveInfo querySkipCurrentProfileIntents(
             List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
@@ -3875,9 +4368,10 @@
     private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageSetting ps,
             String[] permissions, boolean[] tmp, int flags, int userId) {
         int numMatch = 0;
-        final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+        final PermissionsState permissionsState = ps.getPermissionsState();
         for (int i=0; i<permissions.length; i++) {
-            if (gp.grantedPermissions.contains(permissions[i])) {
+            final String permission = permissions[i];
+            if (permissionsState.hasPermission(permission, userId)) {
                 tmp[i] = true;
                 numMatch++;
             } else {
@@ -5164,6 +5658,28 @@
                     + " already installed.  Skipping duplicate.");
         }
 
+        // If we're only installing presumed-existing packages, require that the
+        // scanned APK is both already known and at the path previously established
+        // for it.  Previously unknown packages we pick up normally, but if we have an
+        // a priori expectation about this package's install presence, enforce it.
+        if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
+            PackageSetting known = mSettings.peekPackageLPr(pkg.packageName);
+            if (known != null) {
+                if (DEBUG_PACKAGE_SCANNING) {
+                    Log.d(TAG, "Examining " + pkg.codePath
+                            + " and requiring known paths " + known.codePathString
+                            + " & " + known.resourcePathString);
+                }
+                if (!pkg.applicationInfo.getCodePath().equals(known.codePathString)
+                        || !pkg.applicationInfo.getResourcePath().equals(known.resourcePathString)) {
+                    throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
+                            "Application package " + pkg.packageName
+                            + " found at " + pkg.applicationInfo.getCodePath()
+                            + " but expected at " + known.codePathString + "; ignoring.");
+                }
+            }
+        }
+
         // Initialize package source and resource directories
         File destCodeFile = new File(pkg.applicationInfo.getCodePath());
         File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
@@ -6843,36 +7359,49 @@
 
     private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace,
             String packageOfInterest) {
+        // IMPORTANT: There are two types of permissions: install and runtime.
+        // Install time permissions are granted when the app is installed to
+        // all device users and users added in the future. Runtime permissions
+        // are granted at runtime explicitly to specific users. Normal and signature
+        // protected permissions are install time permissions. Dangerous permissions
+        // are install permissions if the app's target SDK is Lollipop MR1 or older,
+        // otherwise they are runtime permissions. This function does not manage
+        // runtime permissions except for the case an app targeting Lollipop MR1
+        // being upgraded to target a newer SDK, in which case dangerous permissions
+        // are transformed from install time to runtime ones.
+
         final PackageSetting ps = (PackageSetting) pkg.mExtras;
         if (ps == null) {
             return;
         }
-        final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
-        ArraySet<String> origPermissions = gp.grantedPermissions;
-        boolean changedPermission = false;
+
+        PermissionsState permissionsState = ps.getPermissionsState();
+        PermissionsState origPermissions = permissionsState;
+
+        final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
+
+        int[] upgradeUserIds = PermissionsState.USERS_NONE;
+        int[] changedRuntimePermissionUserIds = PermissionsState.USERS_NONE;
+
+        boolean changedInstallPermission = false;
 
         if (replace) {
-            ps.permissionsFixed = false;
-            if (gp == ps) {
-                origPermissions = new ArraySet<String>(gp.grantedPermissions);
-                gp.grantedPermissions.clear();
-                gp.gids = mGlobalGids;
+            ps.installPermissionsFixed = false;
+            if (!ps.isSharedUser()) {
+                origPermissions = new PermissionsState(permissionsState);
+                permissionsState.reset();
             }
         }
 
-        if (gp.gids == null) {
-            gp.gids = mGlobalGids;
-        }
+        permissionsState.setGlobalGids(mGlobalGids);
 
         final int N = pkg.requestedPermissions.size();
         for (int i=0; i<N; i++) {
             final String name = pkg.requestedPermissions.get(i);
-            final boolean required = pkg.requestedPermissionsRequired.get(i);
             final BasePermission bp = mSettings.mPermissions.get(name);
+
             if (DEBUG_INSTALL) {
-                if (gp != ps) {
-                    Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp);
-                }
+                Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp);
             }
 
             if (bp == null || bp.packageSetting == null) {
@@ -6884,10 +7413,11 @@
             }
 
             final String perm = bp.name;
-            boolean allowed;
             boolean allowedSig = false;
-            if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
-                // Keep track of app op permissions.
+            int grant = GRANT_DENIED;
+
+            // Keep track of app op permissions.
+            if ((bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
                 ArraySet<String> pkgs = mAppOpPermissionPackages.get(bp.name);
                 if (pkgs == null) {
                     pkgs = new ArraySet<>();
@@ -6895,65 +7425,126 @@
                 }
                 pkgs.add(pkg.packageName);
             }
+
             final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
-            if (level == PermissionInfo.PROTECTION_NORMAL
-                    || level == PermissionInfo.PROTECTION_DANGEROUS) {
-                // We grant a normal or dangerous permission if any of the following
-                // are true:
-                // 1) The permission is required
-                // 2) The permission is optional, but was granted in the past
-                // 3) The permission is optional, but was requested by an
-                //    app in /system (not /data)
-                //
-                // Otherwise, reject the permission.
-                allowed = (required || origPermissions.contains(perm)
-                        || (isSystemApp(ps) && !isUpdatedSystemApp(ps)));
-            } else if (bp.packageSetting == null) {
-                // This permission is invalid; skip it.
-                allowed = false;
-            } else if (level == PermissionInfo.PROTECTION_SIGNATURE) {
-                allowed = grantSignaturePermission(perm, pkg, bp, origPermissions);
-                if (allowed) {
-                    allowedSig = true;
-                }
-            } else {
-                allowed = false;
+            switch (level) {
+                case PermissionInfo.PROTECTION_NORMAL: {
+                    // For all apps normal permissions are install time ones.
+                    grant = GRANT_INSTALL;
+                } break;
+
+                case PermissionInfo.PROTECTION_DANGEROUS: {
+                    if (!RUNTIME_PERMISSIONS_ENABLED
+                            || pkg.applicationInfo.targetSdkVersion
+                                    <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+                        // For legacy apps dangerous permissions are install time ones.
+                        grant = GRANT_INSTALL;
+                    } else if (ps.isSystem()) {
+                        final int[] updatedUserIds = ps.getPermissionsUpdatedForUserIds();
+                        if (origPermissions.hasInstallPermission(bp.name)) {
+                            // If a system app had an install permission, then the app was
+                            // upgraded and we grant the permissions as runtime to all users.
+                            grant = GRANT_UPGRADE;
+                            upgradeUserIds = currentUserIds;
+                        } else if (!Arrays.equals(updatedUserIds, currentUserIds)) {
+                            // If users changed since the last permissions update for a
+                            // system app, we grant the permission as runtime to the new users.
+                            grant = GRANT_UPGRADE;
+                            upgradeUserIds = currentUserIds;
+                            for (int userId : updatedUserIds) {
+                                upgradeUserIds = ArrayUtils.removeInt(upgradeUserIds, userId);
+                            }
+                        } else {
+                            // Otherwise, we grant the permission as runtime if the app
+                            // already had it, i.e. we preserve runtime permissions.
+                            grant = GRANT_RUNTIME;
+                        }
+                    } else if (origPermissions.hasInstallPermission(bp.name)) {
+                        // For legacy apps that became modern, install becomes runtime.
+                        grant = GRANT_UPGRADE;
+                        upgradeUserIds = currentUserIds;
+                    } else if (replace) {
+                        // For upgraded modern apps keep runtime permissions unchanged.
+                        grant = GRANT_RUNTIME;
+                    }
+                } break;
+
+                case PermissionInfo.PROTECTION_SIGNATURE: {
+                    // For all apps signature permissions are install time ones.
+                    allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);
+                    if (allowedSig) {
+                        grant = GRANT_INSTALL;
+                    }
+                } break;
             }
+
             if (DEBUG_INSTALL) {
-                if (gp != ps) {
-                    Log.i(TAG, "Package " + pkg.packageName + " granting " + perm);
-                }
+                Log.i(TAG, "Package " + pkg.packageName + " granting " + perm);
             }
-            if (allowed) {
-                if (!isSystemApp(ps) && ps.permissionsFixed) {
+
+            if (grant != GRANT_DENIED) {
+                if (!isSystemApp(ps) && ps.installPermissionsFixed) {
                     // If this is an existing, non-system package, then
                     // we can't add any new permissions to it.
-                    if (!allowedSig && !gp.grantedPermissions.contains(perm)) {
+                    if (!allowedSig && !origPermissions.hasInstallPermission(perm)) {
                         // Except...  if this is a permission that was added
                         // to the platform (note: need to only do this when
                         // updating the platform).
-                        allowed = isNewPlatformPermissionForPackage(perm, pkg);
+                        if (!isNewPlatformPermissionForPackage(perm, pkg)) {
+                            grant = GRANT_DENIED;
+                        }
                     }
                 }
-                if (allowed) {
-                    if (!gp.grantedPermissions.contains(perm)) {
-                        changedPermission = true;
-                        gp.grantedPermissions.add(perm);
-                        gp.gids = appendInts(gp.gids, bp.gids);
-                    } else if (!ps.haveGids) {
-                        gp.gids = appendInts(gp.gids, bp.gids);
-                    }
-                } else {
-                    if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
-                        Slog.w(TAG, "Not granting permission " + perm
-                                + " to package " + pkg.packageName
-                                + " because it was previously installed without");
-                    }
+
+                switch (grant) {
+                    case GRANT_INSTALL: {
+                        // Grant an install permission.
+                        if (permissionsState.grantInstallPermission(bp) !=
+                                PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                            changedInstallPermission = true;
+                        }
+                    } break;
+
+                    case GRANT_RUNTIME: {
+                        // Grant previously granted runtime permissions.
+                        for (int userId : UserManagerService.getInstance().getUserIds()) {
+                            if (origPermissions.hasRuntimePermission(bp.name, userId)) {
+                                if (permissionsState.grantRuntimePermission(bp, userId) ==
+                                        PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                                    // If we cannot put the permission as it was, we have to write.
+                                    changedRuntimePermissionUserIds = ArrayUtils.appendInt(
+                                            changedRuntimePermissionUserIds, userId);
+                                }
+                            }
+                        }
+                    } break;
+
+                    case GRANT_UPGRADE: {
+                        // Grant runtime permissions for a previously held install permission.
+                        permissionsState.revokeInstallPermission(bp);
+                        for (int userId : upgradeUserIds) {
+                            if (permissionsState.grantRuntimePermission(bp, userId) !=
+                                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                                // If we granted the permission, we have to write.
+                                changedRuntimePermissionUserIds = ArrayUtils.appendInt(
+                                        changedRuntimePermissionUserIds, userId);
+                            }
+                        }
+                    } break;
+
+                    default: {
+                        if (packageOfInterest == null
+                                || packageOfInterest.equals(pkg.packageName)) {
+                            Slog.w(TAG, "Not granting permission " + perm
+                                    + " to package " + pkg.packageName
+                                    + " because it was previously installed without");
+                        }
+                    } break;
                 }
             } else {
-                if (gp.grantedPermissions.remove(perm)) {
-                    changedPermission = true;
-                    gp.gids = removeInts(gp.gids, bp.gids);
+                if (permissionsState.revokeInstallPermission(bp) !=
+                        PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                    changedInstallPermission = true;
                     Slog.i(TAG, "Un-granting permission " + perm
                             + " from package " + pkg.packageName
                             + " (protectionLevel=" + bp.protectionLevel
@@ -6973,14 +7564,22 @@
             }
         }
 
-        if ((changedPermission || replace) && !ps.permissionsFixed &&
+        if ((changedInstallPermission || replace) && !ps.installPermissionsFixed &&
                 !isSystemApp(ps) || isUpdatedSystemApp(ps)){
             // This is the first that we have heard about this package, so the
             // permissions we have now selected are fixed until explicitly
             // changed.
-            ps.permissionsFixed = true;
+            ps.installPermissionsFixed = true;
         }
-        ps.haveGids = true;
+
+        ps.setPermissionsUpdatedForUserIds(currentUserIds);
+
+        // Persist the runtime permissions state for users with changes.
+        if (RUNTIME_PERMISSIONS_ENABLED) {
+            for (int userId : changedRuntimePermissionUserIds) {
+                mSettings.writeRuntimePermissionsForUserLPr(userId, true);
+            }
+        }
     }
 
     private boolean isNewPlatformPermissionForPackage(String perm, PackageParser.Package pkg) {
@@ -7001,7 +7600,7 @@
     }
 
     private boolean grantSignaturePermission(String perm, PackageParser.Package pkg,
-                                          BasePermission bp, ArraySet<String> origPermissions) {
+            BasePermission bp, PermissionsState origPermissions) {
         boolean allowed;
         allowed = (compareSignatures(
                 bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
@@ -7016,10 +7615,7 @@
                 if (isUpdatedSystemApp(pkg)) {
                     final PackageSetting sysPs = mSettings
                             .getDisabledSystemPkgLPr(pkg.packageName);
-                    final GrantedPermissions origGp = sysPs.sharedUser != null
-                            ? sysPs.sharedUser : sysPs;
-
-                    if (origGp.grantedPermissions.contains(perm)) {
+                    if (sysPs.getPermissionsState().hasInstallPermission(perm)) {
                         // If the original was granted this permission, we take
                         // that grant decision as read and propagate it to the
                         // update.
@@ -7053,7 +7649,7 @@
                 & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
             // For development permissions, a development permission
             // is granted only if it was already granted.
-            allowed = origPermissions.contains(perm);
+            allowed = origPermissions.hasInstallPermission(perm);
         }
         return allowed;
     }
@@ -7215,6 +7811,9 @@
             if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = info;
             }
+            if (info != null) {
+                res.filterNeedsVerification = info.needsVerification();
+            }
             res.priority = info.getPriority();
             res.preferredOrder = activity.owner.mPreferredOrder;
             //System.out.println("Result: " + res.activityInfo.className +
@@ -7437,8 +8036,6 @@
             }
             res.priority = info.getPriority();
             res.preferredOrder = service.owner.mPreferredOrder;
-            //System.out.println("Result: " + res.activityInfo.className +
-            //                   " = " + res.priority);
             res.match = match;
             res.isDefault = info.hasDefault;
             res.labelRes = info.labelRes;
@@ -8315,6 +8912,45 @@
                 android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) == 1;
     }
 
+    @Override
+    public void verifyIntentFilter(int id, int verificationCode, List<String> outFailedDomains)
+            throws RemoteException {
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
+                "Only intentfilter verification agents can verify applications");
+
+        final Message msg = mHandler.obtainMessage(INTENT_FILTER_VERIFIED);
+        final IntentFilterVerificationResponse response = new IntentFilterVerificationResponse(
+                Binder.getCallingUid(), verificationCode, outFailedDomains);
+        msg.arg1 = id;
+        msg.obj = response;
+        mHandler.sendMessage(msg);
+    }
+
+    @Override
+    public int getIntentVerificationStatus(String packageName, int userId) {
+        synchronized (mPackages) {
+            return mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
+        }
+    }
+
+    @Override
+    public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
+        boolean result = false;
+        synchronized (mPackages) {
+            result = mSettings.updateIntentFilterVerificationStatusLPw(packageName, status, userId);
+        }
+        scheduleWritePackageRestrictionsLocked(userId);
+        return result;
+    }
+
+    @Override
+    public List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) {
+        synchronized (mPackages) {
+            return mSettings.getIntentFilterVerificationsLPr(packageName);
+        }
+    }
+
     /**
      * Get the "allow unknown sources" setting.
      *
@@ -10479,6 +11115,8 @@
             return;
         }
 
+        startIntentFilterVerifications(args.user.getIdentifier(), pkg);
+
         if (replace) {
             replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                     installerPackageName, res);
@@ -10494,6 +11132,86 @@
         }
     }
 
+    private void startIntentFilterVerifications(int userId, PackageParser.Package pkg) {
+        if (mIntentFilterVerifierComponent == null) {
+            Slog.d(TAG, "No IntentFilter verification will not be done as "
+                    + "there is no IntentFilterVerifier available!");
+            return;
+        }
+
+        final int verifierUid = getPackageUid(
+                mIntentFilterVerifierComponent.getPackageName(),
+                (userId == UserHandle.USER_ALL) ? UserHandle.USER_OWNER : userId);
+
+        mHandler.removeMessages(START_INTENT_FILTER_VERIFICATIONS);
+        final Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
+        msg.obj = pkg;
+        msg.arg1 = userId;
+        msg.arg2 = verifierUid;
+
+        mHandler.sendMessage(msg);
+    }
+
+    private void verifyIntentFiltersIfNeeded(int userId, int verifierUid,
+                                             PackageParser.Package pkg) {
+        int size = pkg.activities.size();
+        if (size == 0) {
+            Slog.d(TAG, "No activity, so no need to verify any IntentFilter!");
+            return;
+        }
+
+        Slog.d(TAG, "Checking for userId:" + userId + " if any IntentFilter from the " + size
+                + " Activities needs verification ...");
+
+        final int verificationId = mIntentFilterVerificationToken++;
+        int count = 0;
+        synchronized (mPackages) {
+            for (PackageParser.Activity a : pkg.activities) {
+                for (ActivityIntentInfo filter : a.intents) {
+                    boolean needFilterVerification = filter.needsVerification() &&
+                            !filter.isVerified();
+                    if (needFilterVerification && needNetworkVerificationLPr(filter)) {
+                        Slog.d(TAG, "Verification needed for IntentFilter:" + filter.toString());
+                        mIntentFilterVerifier.addOneIntentFilterVerification(
+                                verifierUid, userId, verificationId, filter, pkg.packageName);
+                        count++;
+                    } else {
+                        Slog.d(TAG, "No verification needed for IntentFilter:" + filter.toString());
+                    }
+                }
+            }
+        }
+
+        if (count > 0) {
+            mIntentFilterVerifier.startVerifications(userId);
+            Slog.d(TAG, "Started " + count + " IntentFilter verification"
+                    + (count > 1 ? "s" : "") +  " for userId:" + userId + "!");
+        } else {
+            Slog.d(TAG, "No need to start any IntentFilter verification!");
+        }
+    }
+
+    private boolean needNetworkVerificationLPr(ActivityIntentInfo filter) {
+        final ComponentName cn  = filter.activity.getComponentName();
+        final String packageName = cn.getPackageName();
+
+        IntentFilterVerificationInfo ivi = mSettings.getIntentFilterVerificationLPr(
+                packageName);
+        if (ivi == null) {
+            return true;
+        }
+        int status = ivi.getStatus();
+        switch (status) {
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
+                return true;
+
+            default:
+                // Nothing to do
+                return false;
+        }
+    }
+
     private static boolean isMultiArch(PackageSetting ps) {
         return (ps.pkgFlags & ApplicationInfo.FLAG_MULTIARCH) != 0;
     }
@@ -10811,14 +11529,33 @@
                         mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName);
                         outInfo.removedAppId = mSettings.removePackageLPw(packageName);
                     }
-                    if (deletedPs != null) {
-                        updatePermissionsLPw(deletedPs.name, null, 0);
-                        if (deletedPs.sharedUser != null) {
-                            // remove permissions associated with package
-                            mSettings.updateSharedUserPermsLPw(deletedPs, mGlobalGids);
+                    updatePermissionsLPw(deletedPs.name, null, 0);
+                    if (deletedPs.sharedUser != null) {
+                        // Remove permissions associated with package. Since runtime
+                        // permissions are per user we have to kill the removed package
+                        // or packages running under the shared user of the removed
+                        // package if revoking the permissions requested only by the removed
+                        // package is successful and this causes a change in gids.
+                        for (int userId : UserManagerService.getInstance().getUserIds()) {
+                            final int userIdToKill = mSettings.updateSharedUserPermsLPw(deletedPs,
+                                    userId);
+                            if (userIdToKill == UserHandle.USER_ALL
+                                    || userIdToKill >= UserHandle.USER_OWNER) {
+                                // If gids changed for this user, kill all affected packages.
+                                mHandler.post(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        // This has to happen with no lock held.
+                                        killSettingPackagesForUser(deletedPs, userIdToKill,
+                                                KILL_APP_REASON_GIDS_CHANGED);
+                                    }
+                                });
+                            break;
+                            }
                         }
                     }
                     clearPackagePreferredActivitiesLPw(deletedPs.name, UserHandle.USER_ALL);
+                    clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL);
                 }
                 // make sure to preserve per-user disabled state if this removal was just
                 // a downgrade of a system app to the factory package
@@ -11047,8 +11784,8 @@
                         true,  //notLaunched
                         false, //hidden
                         null, null, null,
-                        false // blockUninstall
-                        );
+                        false, // blockUninstall
+                        INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
                 if (!isSystemApp(ps)) {
                     if (ps.isAnyInstalled(sUserManager.getUserIds())) {
                         // Other user still have this package installed, so all
@@ -11671,6 +12408,19 @@
         return changed;
     }
 
+    /** This method takes a specific user id as well as UserHandle.USER_ALL. */
+    void clearIntentFilterVerificationsLPw(String packageName, int userId) {
+        if (userId == UserHandle.USER_ALL) {
+            mSettings.removeIntentFilterVerificationLPw(packageName, sUserManager.getUserIds());
+            for (int oneUserId : sUserManager.getUserIds()) {
+                scheduleWritePackageRestrictionsLocked(oneUserId);
+            }
+        } else {
+            mSettings.removeIntentFilterVerificationLPw(packageName, userId);
+            scheduleWritePackageRestrictionsLocked(userId);
+        }
+    }
+
     @Override
     public void resetPreferredActivities(int userId) {
         /* TODO: Actually use userId. Why is it being passed in? */
@@ -12181,6 +12931,8 @@
         public static final int DUMP_KEYSETS = 1 << 11;
         public static final int DUMP_VERSION = 1 << 12;
         public static final int DUMP_INSTALLS = 1 << 13;
+        public static final int DUMP_INTENT_FILTER_VERIFIERS = 1 << 14;
+        public static final int DUMP_DOMAIN_PREFERRED = 1 << 15;
 
         public static final int OPTION_SHOW_FILTERS = 1 << 0;
 
@@ -12286,6 +13038,8 @@
                 pw.println("    write: write current settings now");
                 pw.println("    <package.name>: info about given package");
                 pw.println("    installs: details about install sessions");
+                pw.println("    d[omain-preferred-apps]: print domains preferred apps");
+                pw.println("    i[ntent-filter-verifiers]|ifv: print intent filter verifier info");
                 return;
             } else if ("--checkin".equals(opt)) {
                 checkin = true;
@@ -12322,6 +13076,8 @@
                     fullPreferred = true;
                     opti++;
                 }
+            } else if ("d".equals(cmd) || "domain-preferred-apps".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_DOMAIN_PREFERRED);
             } else if ("p".equals(cmd) || "packages".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_PACKAGES);
             } else if ("s".equals(cmd) || "shared-users".equals(cmd)) {
@@ -12332,6 +13088,9 @@
                 dumpState.setDump(DumpState.DUMP_MESSAGES);
             } else if ("v".equals(cmd) || "verifiers".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_VERIFIERS);
+            } else if ("i".equals(cmd) || "ifv".equals(cmd)
+                    || "intent-filter-verifiers".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_INTENT_FILTER_VERIFIERS);
             } else if ("version".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_VERSION);
             } else if ("k".equals(cmd) || "keysets".equals(cmd)) {
@@ -12387,6 +13146,29 @@
                 }
             }
 
+            if (dumpState.isDumping(DumpState.DUMP_INTENT_FILTER_VERIFIERS) &&
+                    packageName == null) {
+                if (mIntentFilterVerifierComponent != null) {
+                    String verifierPackageName = mIntentFilterVerifierComponent.getPackageName();
+                    if (!checkin) {
+                        if (dumpState.onTitlePrinted())
+                            pw.println();
+                        pw.println("Intent Filter Verifier:");
+                        pw.print("  Using: ");
+                        pw.print(verifierPackageName);
+                        pw.print(" (uid=");
+                        pw.print(getPackageUid(verifierPackageName, 0));
+                        pw.println(")");
+                    } else if (verifierPackageName != null) {
+                        pw.print("ifv,"); pw.print(verifierPackageName);
+                        pw.print(","); pw.println(getPackageUid(verifierPackageName, 0));
+                    }
+                } else {
+                    pw.println();
+                    pw.println("No Intent Filter Verifier available!");
+                }
+            }
+
             if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
                 boolean printedHeader = false;
                 final Iterator<String> it = mSharedLibraries.keySet().iterator();
@@ -12506,6 +13288,65 @@
                 }
             }
 
+            if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
+                pw.println();
+                int count = mSettings.mPackages.size();
+                if (count == 0) {
+                    pw.println("No domain preferred apps!");
+                    pw.println();
+                } else {
+                    final String prefix = "  ";
+                    Collection<PackageSetting> allPackageSettings = mSettings.mPackages.values();
+                    if (allPackageSettings.size() == 0) {
+                        pw.println("No domain preferred apps!");
+                        pw.println();
+                    } else {
+                        pw.println("Domain preferred apps status:");
+                        pw.println();
+                        count = 0;
+                        for (PackageSetting ps : allPackageSettings) {
+                            IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
+                            if (ivi == null || ivi.getPackageName() == null) continue;
+                            pw.println(prefix + "Package Name: " + ivi.getPackageName());
+                            pw.println(prefix + "Domains: " + ivi.getDomainsString());
+                            pw.println(prefix + "Status: " + ivi.getStatusString());
+                            pw.println();
+                            count++;
+                        }
+                        if (count == 0) {
+                            pw.println(prefix + "No domain preferred app status!");
+                            pw.println();
+                        }
+                        for (int userId : sUserManager.getUserIds()) {
+                            pw.println("Domain preferred apps for User " + userId + ":");
+                            pw.println();
+                            count = 0;
+                            for (PackageSetting ps : allPackageSettings) {
+                                IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
+                                if (ivi == null || ivi.getPackageName() == null) {
+                                    continue;
+                                }
+                                final int status = ps.getDomainVerificationStatusForUser(userId);
+                                if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+                                    continue;
+                                }
+                                pw.println(prefix + "Package Name: " + ivi.getPackageName());
+                                pw.println(prefix + "Domains: " + ivi.getDomainsString());
+                                String statusStr = IntentFilterVerificationInfo.
+                                        getStatusStringFromValue(status);
+                                pw.println(prefix + "Status: " + statusStr);
+                                pw.println();
+                                count++;
+                            }
+                            if (count == 0) {
+                                pw.println(prefix + "No domain preferred apps!");
+                                pw.println();
+                            }
+                        }
+                    }
+                }
+            }
+
             if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
                 mSettings.dumpPermissionsLPr(pw, packageName, dumpState);
                 if (packageName == null) {
@@ -13194,6 +14035,11 @@
         }
     }
 
+    void newUserCreatedLILPw(int userHandle) {
+        // Adding a user requires updating runtime permissions for system apps.
+        updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL);
+    }
+
     @Override
     public VerifierDeviceIdentity getVerifierDeviceIdentity() throws RemoteException {
         mContext.enforceCallingOrSelfPermission(
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 06d842a..e7c0ef7 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -57,8 +57,10 @@
             + " " + name + "/" + appId + "}";
     }
 
-    public int[] getGids() {
-        return sharedUser != null ? sharedUser.gids : gids;
+    public PermissionsState getPermissionsState() {
+        return (sharedUser != null)
+                ? sharedUser.getPermissionsState()
+                : super.getPermissionsState();
     }
 
     public boolean isPrivileged() {
@@ -68,4 +70,12 @@
     public boolean isForwardLocked() {
         return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
     }
+
+    public boolean isSystem() {
+        return (pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
+    }
+
+    public boolean isSharedUser() {
+        return sharedUser != null;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 4b8ca42..daa6d64 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -20,6 +20,8 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 
+import android.content.pm.IntentFilterVerificationInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageUserState;
 import android.util.ArraySet;
 import android.util.SparseArray;
@@ -29,7 +31,7 @@
 /**
  * Settings base class for pending and resolved classes.
  */
-class PackageSettingBase extends GrantedPermissions {
+abstract class PackageSettingBase extends SettingBase {
     /**
      * Indicates the state of installation. Used by PackageManager to figure out
      * incomplete installations. Say a package is being installed (the state is
@@ -92,8 +94,7 @@
 
     PackageSignatures signatures = new PackageSignatures();
 
-    boolean permissionsFixed;
-    boolean haveGids;
+    boolean installPermissionsFixed;
 
     PackageKeySetData keySetData = new PackageKeySetData();
 
@@ -109,6 +110,9 @@
 
     /* package name of the app that installed this package */
     String installerPackageName;
+
+    IntentFilterVerificationInfo verificationInfo;
+
     PackageSettingBase(String name, String realName, File codePath, File resourcePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString,
@@ -146,8 +150,7 @@
 
         signatures = new PackageSignatures(base.signatures);
 
-        permissionsFixed = base.permissionsFixed;
-        haveGids = base.haveGids;
+        installPermissionsFixed = base.installPermissionsFixed;
         userState.clear();
         for (int i=0; i<base.userState.size(); i++) {
             userState.put(base.userState.keyAt(i),
@@ -160,7 +163,6 @@
         installerPackageName = base.installerPackageName;
 
         keySetData = new PackageKeySetData(base.keySetData);
-
     }
 
     void init(File codePath, File resourcePath, String legacyNativeLibraryPathString,
@@ -201,9 +203,8 @@
      * Make a shallow copy of this package settings.
      */
     public void copyFrom(PackageSettingBase base) {
-        grantedPermissions = base.grantedPermissions;
-        gids = base.gids;
-
+        setPermissionsUpdatedForUserIds(base.getPermissionsUpdatedForUserIds());
+        mPermissionsState.copyFrom(base.mPermissionsState);
         primaryCpuAbiString = base.primaryCpuAbiString;
         secondaryCpuAbiString = base.secondaryCpuAbiString;
         cpuAbiOverrideString = base.cpuAbiOverrideString;
@@ -211,14 +212,14 @@
         firstInstallTime = base.firstInstallTime;
         lastUpdateTime = base.lastUpdateTime;
         signatures = base.signatures;
-        permissionsFixed = base.permissionsFixed;
-        haveGids = base.haveGids;
+        installPermissionsFixed = base.installPermissionsFixed;
         userState.clear();
         for (int i=0; i<base.userState.size(); i++) {
             userState.put(base.userState.keyAt(i), base.userState.valueAt(i));
         }
         installStatus = base.installStatus;
         keySetData = base.keySetData;
+        verificationInfo = base.verificationInfo;
     }
 
     private PackageUserState modifyUserState(int userId) {
@@ -322,7 +323,7 @@
     void setUserState(int userId, int enabled, boolean installed, boolean stopped,
             boolean notLaunched, boolean hidden,
             String lastDisableAppCaller, ArraySet<String> enabledComponents,
-            ArraySet<String> disabledComponents, boolean blockUninstall) {
+            ArraySet<String> disabledComponents, boolean blockUninstall, int domainVerifState) {
         PackageUserState state = modifyUserState(userId);
         state.enabled = enabled;
         state.installed = installed;
@@ -333,6 +334,7 @@
         state.enabledComponents = enabledComponents;
         state.disabledComponents = disabledComponents;
         state.blockUninstall = blockUninstall;
+        state.domainVerificationStatus = domainVerifState;
     }
 
     ArraySet<String> getEnabledComponents(int userId) {
@@ -420,4 +422,25 @@
     void removeUser(int userId) {
         userState.delete(userId);
     }
+
+    public IntentFilterVerificationInfo getIntentFilterVerificationInfo() {
+        return verificationInfo;
+    }
+
+    public void setIntentFilterVerificationInfo(IntentFilterVerificationInfo info) {
+        verificationInfo = info;
+    }
+
+    public int getDomainVerificationStatusForUser(int userId) {
+        return readUserState(userId).domainVerificationStatus;
+    }
+
+    public void setDomainVerificationStatusForUser(int status, int userId) {
+        modifyUserState(userId).domainVerificationStatus = status;
+    }
+
+    public void clearDomainVerificationStatusForUser(int userId) {
+        modifyUserState(userId).domainVerificationStatus =
+                PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/PermissionsState.java
new file mode 100644
index 0000000..705abf8
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PermissionsState.java
@@ -0,0 +1,532 @@
+/*
+ * 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.server.pm;
+
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.util.ArrayUtils;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * This class encapsulates the permissions for a package or a shared user.
+ * <p>
+ * There are two types of permissions: install (granted at installation)
+ * and runtime (granted at runtime). Install permissions are granted to
+ * all device users while runtime permissions are granted explicitly to
+ * specific users.
+ * </p>
+ * <p>
+ * The permissions are kept on a per device user basis. For example, an
+ * application may have some runtime permissions granted under the device
+ * owner but not granted under the secondary user.
+ * <p>
+ * This class is also responsible for keeping track of the Linux gids per
+ * user for a package or a shared user. The gids are computed as a set of
+ * the gids for all granted permissions' gids on a per user basis.
+ * </p>
+ */
+public final class PermissionsState {
+
+    /** The permission operation succeeded and no gids changed. */
+    public static final int PERMISSION_OPERATION_SUCCESS = 1;
+
+    /** The permission operation succeeded and gids changed. */
+    public static final int PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED = 2;
+
+    /** The permission operation failed. */
+    public static final int PERMISSION_OPERATION_FAILURE = 3;
+
+    public static final int[] USERS_ALL = {UserHandle.USER_ALL};
+
+    public static final int[] USERS_NONE = {};
+
+    private static final int[] NO_GIDS = {};
+
+    private ArrayMap<String, PermissionData> mPermissions;
+
+    private int[] mGlobalGids = NO_GIDS;
+
+    public PermissionsState() {
+        /* do nothing */
+    }
+
+    public PermissionsState(PermissionsState prototype) {
+        copyFrom(prototype);
+    }
+
+    /**
+     * Sets the global gids, applicable to all users.
+     *
+     * @param globalGids The global gids.
+     */
+    public void setGlobalGids(int[] globalGids) {
+        if (!ArrayUtils.isEmpty(globalGids)) {
+            mGlobalGids = Arrays.copyOf(globalGids, globalGids.length);
+        }
+    }
+
+    /**
+     * Initialized this instance from another one.
+     *
+     * @param other The other instance.
+     */
+    public void copyFrom(PermissionsState other) {
+        if (mPermissions != null) {
+            if (other.mPermissions == null) {
+                mPermissions = null;
+            } else {
+                mPermissions.clear();
+            }
+        }
+        if (other.mPermissions != null) {
+            if (mPermissions == null) {
+                mPermissions = new ArrayMap<>();
+            }
+            final int permissionCount = other.mPermissions.size();
+            for (int i = 0; i < permissionCount; i++) {
+                String name = other.mPermissions.keyAt(i);
+                PermissionData permissionData = other.mPermissions.valueAt(i);
+                mPermissions.put(name, new PermissionData(permissionData));
+            }
+        }
+
+        mGlobalGids = NO_GIDS;
+        if (other.mGlobalGids != NO_GIDS) {
+            mGlobalGids = Arrays.copyOf(other.mGlobalGids,
+                    other.mGlobalGids.length);
+        }
+    }
+
+    /**
+     * Grant an install permission.
+     *
+     * @param permission The permission to grant.
+     * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+     *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
+     *     #PERMISSION_OPERATION_FAILURE}.
+     */
+    public int grantInstallPermission(BasePermission permission) {
+        return grantPermission(permission, UserHandle.USER_ALL);
+    }
+
+    /**
+     * Revoke an install permission.
+     *
+     * @param permission The permission to revoke.
+     * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+     *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
+     *     #PERMISSION_OPERATION_FAILURE}.
+     */
+    public int revokeInstallPermission(BasePermission permission) {
+        return revokePermission(permission, UserHandle.USER_ALL);
+    }
+
+    /**
+     * Grant a runtime permission.
+     *
+     * @param permission The permission to grant.
+     * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+     *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
+     *     #PERMISSION_OPERATION_FAILURE}.
+     */
+    public int grantRuntimePermission(BasePermission permission, int userId) {
+        if (userId == UserHandle.USER_ALL) {
+            return PERMISSION_OPERATION_FAILURE;
+        }
+        return grantPermission(permission, userId);
+    }
+
+    /**
+     * Revoke a runtime permission for a given device user.
+     *
+     * @param permission The permission to revoke.
+     * @param userId The device user id.
+     * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+     *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
+     *     #PERMISSION_OPERATION_FAILURE}.
+     */
+    public int revokeRuntimePermission(BasePermission permission, int userId) {
+        if (userId == UserHandle.USER_ALL) {
+            return PERMISSION_OPERATION_FAILURE;
+        }
+        return revokePermission(permission, userId);
+    }
+
+    /**
+     * Gets whether this state has a given permission, regardless if
+     * it is install time or runtime one.
+     *
+     * @param name The permission name.
+     * @return Whether this state has the permission.
+     */
+    public boolean hasPermission(String name) {
+        return mPermissions != null && mPermissions.get(name) != null;
+    }
+
+    /**
+     * Gets whether this state has a given runtime permission for a
+     * given device user id.
+     *
+     * @param name The permission name.
+     * @param userId The device user id.
+     * @return Whether this state has the permission.
+     */
+    public boolean hasRuntimePermission(String name, int userId) {
+        return !hasInstallPermission(name) && hasPermission(name, userId);
+    }
+
+    /**
+     * Gets whether this state has a given install permission.
+     *
+     * @param name The permission name.
+     * @return Whether this state has the permission.
+     */
+    public boolean hasInstallPermission(String name) {
+        return hasPermission(name, UserHandle.USER_ALL);
+    }
+
+    /**
+     * Revokes a permission for all users regardless if it is an install or
+     * a runtime permission.
+     *
+     * @param permission The permission to revoke.
+     * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS},
+     *     or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link
+     *     #PERMISSION_OPERATION_FAILURE}.
+     */
+    public int revokePermission(BasePermission permission) {
+        if (!hasPermission(permission.name)) {
+            return PERMISSION_OPERATION_FAILURE;
+        }
+
+        int result = PERMISSION_OPERATION_SUCCESS;
+
+        PermissionData permissionData = mPermissions.get(permission.name);
+        if (permissionData.hasGids()) {
+            for (int userId : permissionData.getUserIds()) {
+                if (revokePermission(permission, userId)
+                        == PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
+                    result = PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+                    break;
+                }
+            }
+        }
+
+        mPermissions.remove(permission.name);
+
+        return result;
+    }
+
+    /**
+     * Gets whether the state has a given permission for the specified
+     * user, regardless if this is an install or a runtime permission.
+     *
+     * @param name The permission name.
+     * @param userId The device user id.
+     * @return Whether the user has the permission.
+     */
+    public boolean hasPermission(String name, int userId) {
+        enforceValidUserId(userId);
+
+        if (mPermissions == null) {
+            return false;
+        }
+
+        PermissionData permissionData = mPermissions.get(name);
+        return permissionData != null && permissionData.hasUserId(userId);
+    }
+
+    /**
+     * Gets all permissions regardless if they are install or runtime.
+     *
+     * @return The permissions or an empty set.
+     */
+    public Set<String> getPermissions() {
+        if (mPermissions != null) {
+            return mPermissions.keySet();
+        }
+
+        return Collections.emptySet();
+    }
+
+    /**
+     * Gets all permissions for a given device user id regardless if they
+     * are install time or runtime permissions.
+     *
+     * @param userId The device user id.
+     * @return The permissions or an empty set.
+     */
+    public Set<String> getPermissions(int userId) {
+        enforceValidUserId(userId);
+
+        if (mPermissions == null) {
+            return Collections.emptySet();
+        }
+
+        Set<String> permissions = new ArraySet<>();
+
+        final int permissionCount = mPermissions.size();
+        for (int i = 0; i < permissionCount; i++) {
+            String permission = mPermissions.keyAt(i);
+            if (userId == UserHandle.USER_ALL) {
+                if (hasInstallPermission(permission)) {
+                    permissions.add(permission);
+                }
+            } else {
+                if (hasRuntimePermission(permission, userId)) {
+                    permissions.add(permission);
+                }
+            }
+        }
+
+        return  permissions;
+    }
+
+    /**
+     * Gets all runtime permissions.
+     *
+     * @return The permissions or an empty set.
+     */
+    public Set<String> getRuntimePermissions(int userId) {
+        return getPermissions(userId);
+    }
+
+    /**
+     * Gets all install permissions.
+     *
+     * @return The permissions or an empty set.
+     */
+    public Set<String> getInstallPermissions() {
+        return getPermissions(UserHandle.USER_ALL);
+    }
+
+    /**
+     * Compute the Linux gids for a given device user from the permissions
+     * granted to this user. Note that these are computed to avoid additional
+     * state as they are rarely accessed.
+     *
+     * @param userId The device user id.
+     * @return The gids for the device user.
+     */
+    public int[] computeGids(int userId) {
+        enforceValidUserId(userId);
+
+        int[] gids = mGlobalGids;
+
+        if (mPermissions != null) {
+            final int permissionCount = mPermissions.size();
+            for (int i = 0; i < permissionCount; i++) {
+                String permission = mPermissions.keyAt(i);
+                if (!hasPermission(permission, userId)) {
+                    continue;
+                }
+                PermissionData permissionData = mPermissions.valueAt(i);
+                final int[] permGids = permissionData.computeGids(userId);
+                if (permGids != NO_GIDS) {
+                    gids = appendInts(gids, permGids);
+                }
+            }
+        }
+
+        return gids;
+    }
+
+    /**
+     * Compute the Linux gids for all device users from the permissions
+     * granted to these users.
+     *
+     * @return The gids for all device users.
+     */
+    public int[] computeGids() {
+        int[] gids = mGlobalGids;
+
+        for (int userId : UserManagerService.getInstance().getUserIds()) {
+            final int[] userGids = computeGids(userId);
+            gids = appendInts(gids, userGids);
+        }
+
+        return gids;
+    }
+
+    /**
+     * Resets the internal state of this object.
+     */
+    public void reset() {
+        mGlobalGids = NO_GIDS;
+        mPermissions = null;
+    }
+
+    private int grantPermission(BasePermission permission, int userId) {
+        if (hasPermission(permission.name, userId)) {
+            return PERMISSION_OPERATION_FAILURE;
+        }
+
+        final boolean hasGids = permission.hasGids();
+        final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
+
+        if (mPermissions == null) {
+            mPermissions = new ArrayMap<>();
+        }
+
+        PermissionData permissionData = mPermissions.get(permission.name);
+        if (permissionData == null) {
+            permissionData = new PermissionData(permission);
+            mPermissions.put(permission.name, permissionData);
+        }
+
+        if (!permissionData.addUserId(userId)) {
+            return PERMISSION_OPERATION_FAILURE;
+        }
+
+        if (hasGids) {
+            final int[] newGids = computeGids(userId);
+            if (oldGids.length != newGids.length) {
+                return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+            }
+        }
+
+        return PERMISSION_OPERATION_SUCCESS;
+    }
+
+    private int revokePermission(BasePermission permission, int userId) {
+        if (!hasPermission(permission.name, userId)) {
+            return PERMISSION_OPERATION_FAILURE;
+        }
+
+        final boolean hasGids = permission.hasGids();
+        final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;
+
+        PermissionData permissionData = mPermissions.get(permission.name);
+
+        if (!permissionData.removeUserId(userId)) {
+            return PERMISSION_OPERATION_FAILURE;
+        }
+
+        if (permissionData.getUserIds() == USERS_NONE) {
+            mPermissions.remove(permission.name);
+        }
+
+        if (mPermissions.isEmpty()) {
+            mPermissions = null;
+        }
+
+        if (hasGids) {
+            final int[] newGids = computeGids(userId);
+            if (oldGids.length != newGids.length) {
+                return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+            }
+        }
+
+        return PERMISSION_OPERATION_SUCCESS;
+    }
+
+    private static int[] appendInts(int[] current, int[] added) {
+        if (current != null && added != null) {
+            for (int guid : added) {
+                current = ArrayUtils.appendInt(current, guid);
+            }
+        }
+        return current;
+    }
+
+    private static void enforceValidUserId(int userId) {
+        if (userId != UserHandle.USER_ALL && userId < 0) {
+            throw new IllegalArgumentException("Invalid userId:" + userId);
+        }
+    }
+
+    private static final class PermissionData {
+        private final BasePermission mPerm;
+        private int[] mUserIds = USERS_NONE;
+
+        public PermissionData(BasePermission perm) {
+            mPerm = perm;
+        }
+
+        public PermissionData(PermissionData other) {
+            this(other.mPerm);
+
+            if (other.mUserIds == USERS_ALL || other.mUserIds == USERS_NONE) {
+                mUserIds = other.mUserIds;
+            } else {
+                mUserIds = Arrays.copyOf(other.mUserIds, other.mUserIds.length);
+            }
+        }
+
+        public boolean hasGids() {
+            return mPerm.hasGids();
+        }
+
+        public int[] computeGids(int userId) {
+            return mPerm.computeGids(userId);
+        }
+
+        public int[] getUserIds() {
+            return mUserIds;
+        }
+
+        public boolean hasUserId(int userId) {
+            if (mUserIds == USERS_ALL) {
+                return true;
+            }
+
+            if (userId != UserHandle.USER_ALL) {
+                return ArrayUtils.contains(mUserIds, userId);
+            }
+
+            return false;
+        }
+
+        public boolean addUserId(int userId) {
+            if (hasUserId(userId)) {
+                return false;
+            }
+
+            if (userId == UserHandle.USER_ALL) {
+                mUserIds = USERS_ALL;
+                return true;
+            }
+
+            mUserIds = ArrayUtils.appendInt(mUserIds, userId);
+
+            return true;
+        }
+
+        public boolean removeUserId(int userId) {
+            if (!hasUserId(userId)) {
+                return false;
+            }
+
+            if (mUserIds == USERS_ALL) {
+                mUserIds = UserManagerService.getInstance().getUserIds();
+            }
+
+            mUserIds = ArrayUtils.removeInt(mUserIds, userId);
+
+            if (mUserIds.length == 0) {
+                mUserIds = USERS_NONE;
+            }
+
+            return true;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
new file mode 100644
index 0000000..0c7f79d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package com.android.server.pm;
+
+import android.content.pm.ApplicationInfo;
+
+import java.util.Arrays;
+
+abstract class SettingBase {
+    int pkgFlags;
+    int pkgPrivateFlags;
+
+    protected final PermissionsState mPermissionsState;
+    private int[] mPermissionsUpdatedForUserIds = PermissionsState.USERS_NONE;
+
+    SettingBase(int pkgFlags, int pkgPrivateFlags) {
+        setFlags(pkgFlags);
+        setPrivateFlags(pkgPrivateFlags);
+        mPermissionsState = new PermissionsState();
+    }
+
+    SettingBase(SettingBase base) {
+        pkgFlags = base.pkgFlags;
+        pkgPrivateFlags = base.pkgPrivateFlags;
+        mPermissionsState = new PermissionsState(base.mPermissionsState);
+        setPermissionsUpdatedForUserIds(base.mPermissionsUpdatedForUserIds);
+    }
+
+    public PermissionsState getPermissionsState() {
+        return mPermissionsState;
+    }
+
+    public int[] getPermissionsUpdatedForUserIds() {
+        return mPermissionsUpdatedForUserIds;
+    }
+
+    public void setPermissionsUpdatedForUserIds(int[] userIds) {
+        if (Arrays.equals(mPermissionsUpdatedForUserIds, userIds)) {
+            return;
+        }
+
+        if (userIds == PermissionsState.USERS_NONE || userIds == PermissionsState.USERS_ALL) {
+            mPermissionsUpdatedForUserIds = userIds;
+        } else {
+            mPermissionsUpdatedForUserIds = Arrays.copyOf(userIds, userIds.length);
+        }
+    }
+
+    void setFlags(int pkgFlags) {
+        this.pkgFlags = pkgFlags
+                & (ApplicationInfo.FLAG_SYSTEM
+                        | ApplicationInfo.FLAG_EXTERNAL_STORAGE);
+    }
+
+    void setPrivateFlags(int pkgPrivateFlags) {
+        this.pkgPrivateFlags = pkgPrivateFlags
+                & (ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
+                        | ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 3776953..0d2ef89 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -22,28 +22,41 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.PACKAGE_INFO_GID;
 
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
+import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Environment;
 import android.os.FileUtils;
+import android.os.Handler;
+import android.os.Message;
 import android.os.PatternMatcher;
 import android.os.Process;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.AtomicFile;
+import android.text.TextUtils;
 import android.util.LogPrinter;
 
+import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.XmlUtils;
 import com.android.server.pm.PackageManagerService.DumpState;
 
+import java.io.FileNotFoundException;
 import java.util.Collection;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -51,7 +64,6 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ComponentInfo;
@@ -79,6 +91,7 @@
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
@@ -134,6 +147,8 @@
     private static final boolean DEBUG_STOPPED = false;
     private static final boolean DEBUG_MU = false;
 
+    private static final String RUNTIME_PERMISSIONS_FILE_NAME = "runtime-permissions.xml";
+
     private static final String TAG_READ_EXTERNAL_STORAGE = "read-external-storage";
     private static final String ATTR_ENFORCEMENT = "enforcement";
 
@@ -142,10 +157,14 @@
     private static final String TAG_ENABLED_COMPONENTS = "enabled-components";
     private static final String TAG_PACKAGE_RESTRICTIONS = "package-restrictions";
     private static final String TAG_PACKAGE = "pkg";
+    private static final String TAG_SHARED_USER = "shared-user";
+    private static final String TAG_RUNTIME_PERMISSIONS = "runtime-permissions";
+    private static final String TAG_PERMISSIONS = "perms";
     private static final String TAG_PERSISTENT_PREFERRED_ACTIVITIES =
             "persistent-preferred-activities";
     static final String TAG_CROSS_PROFILE_INTENT_FILTERS =
             "crossProfile-intent-filters";
+    public static final String TAG_DOMAIN_VERIFICATION = "domain-verification";
 
     private static final String ATTR_NAME = "name";
     private static final String ATTR_USER = "user";
@@ -160,6 +179,11 @@
     private static final String ATTR_HIDDEN = "hidden";
     private static final String ATTR_INSTALLED = "inst";
     private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
+    private static final String ATTR_DOMAIN_VERIFICATON_STATE = "domainVerificationStatus";
+
+    private final Object mLock;
+
+    private final RuntimePermissionPersistence mRuntimePermissionsPersistence;
 
     private final File mSettingsFilename;
     private final File mBackupSettingsFilename;
@@ -259,11 +283,15 @@
 
     public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages);
 
-    Settings(Context context) {
-        this(context, Environment.getDataDirectory());
+    Settings(Object lock) {
+        this(Environment.getDataDirectory(), lock);
     }
 
-    Settings(Context context, File dataDir) {
+    Settings(File dataDir, Object lock) {
+        mLock = lock;
+
+        mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
+
         mSystemDir = new File(dataDir, "system");
         mSystemDir.mkdirs();
         FileUtils.setPermissions(mSystemDir.toString(),
@@ -462,7 +490,7 @@
                         bp.pendingInfo.packageName = newPkg;
                     }
                     bp.uid = 0;
-                    bp.gids = null;
+                    bp.setGids(null, false);
                 }
             }
         }
@@ -470,9 +498,9 @@
 
     private PackageSetting getPackageLPw(String name, PackageSetting origPackage,
             String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
-            String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString,
-            int vc, int pkgFlags, int pkgPrivateFlags, UserHandle installUser, boolean add,
-            boolean allowInstall) {
+            String legacyNativeLibraryPathString, String primaryCpuAbiString,
+            String secondaryCpuAbiString, int vc, int pkgFlags, int pkgPrivateFlags,
+            UserHandle installUser, boolean add, boolean allowInstall) {
         PackageSetting p = mPackages.get(name);
         UserManagerService userManager = UserManagerService.getInstance();
         if (p != null) {
@@ -570,8 +598,8 @@
                                     true, // notLaunched
                                     false, // hidden
                                     null, null, null,
-                                    false // blockUninstall
-                                    );
+                                    false, // blockUninstall
+                                    INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
                             writePackageRestrictionsLPr(user.id);
                         }
                     }
@@ -591,7 +619,7 @@
                         }
                         p.appId = dis.appId;
                         // Clone permissions
-                        p.grantedPermissions = new ArraySet<String>(dis.grantedPermissions);
+                        p.getPermissionsState().copyFrom(dis.getPermissionsState());
                         // Clone component info
                         List<UserInfo> users = getAllUsers();
                         if (users != null) {
@@ -734,45 +762,60 @@
      * not in use by other permissions of packages in the
      * shared user setting.
      */
-    void updateSharedUserPermsLPw(PackageSetting deletedPs, int[] globalGids) {
+    int updateSharedUserPermsLPw(PackageSetting deletedPs, int userId) {
         if ((deletedPs == null) || (deletedPs.pkg == null)) {
             Slog.i(PackageManagerService.TAG,
                     "Trying to update info for null package. Just ignoring");
-            return;
+            return UserHandle.USER_NULL;
         }
+
         // No sharedUserId
         if (deletedPs.sharedUser == null) {
-            return;
+            return UserHandle.USER_NULL;
         }
+
         SharedUserSetting sus = deletedPs.sharedUser;
+
         // Update permissions
         for (String eachPerm : deletedPs.pkg.requestedPermissions) {
-            boolean used = false;
-            if (!sus.grantedPermissions.contains(eachPerm)) {
+            BasePermission bp = mPermissions.get(eachPerm);
+            if (bp == null) {
                 continue;
             }
-            for (PackageSetting pkg:sus.packages) {
-                if (pkg.pkg != null &&
-                        !pkg.pkg.packageName.equals(deletedPs.pkg.packageName) &&
-                        pkg.pkg.requestedPermissions.contains(eachPerm)) {
+
+            // If no user has the permission, nothing to remove.
+            if (!sus.getPermissionsState().hasPermission(bp.name, userId)) {
+                 continue;
+            }
+
+            boolean used = false;
+
+            // Check if another package in the shared user needs the permission.
+            for (PackageSetting pkg : sus.packages) {
+                if (pkg.pkg != null
+                        && !pkg.pkg.packageName.equals(deletedPs.pkg.packageName)
+                        && pkg.pkg.requestedPermissions.contains(eachPerm)) {
                     used = true;
                     break;
                 }
             }
+
             if (!used) {
-                // can safely delete this permission from list
-                sus.grantedPermissions.remove(eachPerm);
+                // Try to revoke as an install permission which is for all users.
+                if (sus.getPermissionsState().revokeInstallPermission(bp) ==
+                        PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
+                    return UserHandle.USER_ALL;
+                }
+
+                // Try to revoke as an install permission which is per user.
+                if (sus.getPermissionsState().revokeRuntimePermission(bp, userId) ==
+                        PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) {
+                    return userId;
+                }
             }
         }
-        // Update gids
-        int newGids[] = globalGids;
-        for (String eachPerm : sus.grantedPermissions) {
-            BasePermission bp = mPermissions.get(eachPerm);
-            if (bp != null) {
-                newGids = PackageManagerService.appendInts(newGids, bp.gids);
-            }
-        }
-        sus.gids = newGids;
+
+        return UserHandle.USER_NULL;
     }
 
     int removePackageLPw(String name) {
@@ -830,7 +873,7 @@
             if (mOtherUserIds.get(uid) != null) {
                 PackageManagerService.reportSettingsProblem(Log.ERROR,
                         "Adding duplicate shared id: " + uid
-                        + " name=" + name);
+                                + " name=" + name);
                 return false;
             }
             mOtherUserIds.put(uid, obj);
@@ -896,8 +939,119 @@
         return cpir;
     }
 
+    /**
+     * The following functions suppose that you have a lock for managing access to the
+     * mIntentFiltersVerifications map.
+     */
+
+    /* package protected */
+    IntentFilterVerificationInfo getIntentFilterVerificationLPr(String packageName) {
+        PackageSetting ps = mPackages.get(packageName);
+        if (ps == null) {
+            Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+            return null;
+        }
+        return ps.getIntentFilterVerificationInfo();
+    }
+
+    /* package protected */
+    boolean createIntentFilterVerificationIfNeededLPw(String packageName, String[] domains) {
+        PackageSetting ps = mPackages.get(packageName);
+        if (ps == null) {
+            Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+            return false;
+        }
+        if (ps.getIntentFilterVerificationInfo() == null) {
+            IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(packageName, domains);
+            ps.setIntentFilterVerificationInfo(ivi);
+            return false;
+        }
+        return true;
+    }
+
+    int getIntentFilterVerificationStatusLPr(String packageName, int userId) {
+        PackageSetting ps = mPackages.get(packageName);
+        if (ps == null) {
+            Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+            return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+        }
+        int status = ps.getDomainVerificationStatusForUser(userId);
+        if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+            if (ps.getIntentFilterVerificationInfo() != null) {
+                status = ps.getIntentFilterVerificationInfo().getStatus();
+            }
+        }
+        return status;
+    }
+
+    boolean updateIntentFilterVerificationStatusLPw(String packageName, int status, int userId) {
+        PackageSetting ps = mPackages.get(packageName);
+        if (ps == null) {
+            Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+            return false;
+        }
+        ps.setDomainVerificationStatusForUser(status, userId);
+        return true;
+    }
+
+    /**
+     * Used for dump. Should be read only.
+     */
+    List<IntentFilterVerificationInfo> getIntentFilterVerificationsLPr(
+            String packageName) {
+        if (packageName == null) {
+            return Collections.<IntentFilterVerificationInfo>emptyList();
+        }
+        ArrayList<IntentFilterVerificationInfo> result = new ArrayList<>();
+        for (PackageSetting ps : mPackages.values()) {
+            IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
+            if (ivi == null || TextUtils.isEmpty(ivi.getPackageName()) ||
+                    !ivi.getPackageName().equalsIgnoreCase(packageName)) {
+                continue;
+            }
+            result.add(ivi);
+        }
+        return result;
+    }
+
+    void removeIntentFilterVerificationLPw(String packageName, int userId) {
+        PackageSetting ps = mPackages.get(packageName);
+        if (ps == null) {
+            Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+            return;
+        }
+        ps.clearDomainVerificationStatusForUser(userId);
+    }
+
+    void removeIntentFilterVerificationLPw(String packageName, int[] userIds) {
+        for (int userId : userIds) {
+            removeIntentFilterVerificationLPw(packageName, userId);
+        }
+    }
+
     private File getUserPackagesStateFile(int userId) {
-        return new File(Environment.getUserSystemDirectory(userId), "package-restrictions.xml");
+        // TODO: Implement a cleaner solution when adding tests.
+        // This instead of Environment.getUserSystemDirectory(userId) to support testing.
+        File userDir = new File(new File(mSystemDir, "users"), Integer.toString(userId));
+        return new File(userDir, "package-restrictions.xml");
+    }
+
+    private File getUserRuntimePermissionsFile(int userId) {
+        // TODO: Implement a cleaner solution when adding tests.
+        // This instead of Environment.getUserSystemDirectory(userId) to support testing.
+        File userDir = new File(new File(mSystemDir, "users"), Integer.toString(userId));
+        return new File(userDir, RUNTIME_PERMISSIONS_FILE_NAME);
+    }
+
+    boolean isFirstRuntimePermissionsBoot() {
+        return !getUserRuntimePermissionsFile(UserHandle.USER_OWNER).exists();
+    }
+
+    void deleteRuntimePermissionsFiles() {
+        for (int userId : UserManagerService.getInstance().getUserIds()) {
+            File file = getUserRuntimePermissionsFile(userId);
+            file.delete();
+        }
     }
 
     private File getUserPackagesStateBackupFile(int userId) {
@@ -914,15 +1068,9 @@
         }
     }
 
-    void readAllUsersPackageRestrictionsLPr() {
-        List<UserInfo> users = getAllUsers();
-        if (users == null) {
-            readPackageRestrictionsLPr(0);
-            return;
-        }
-
-        for (UserInfo user : users) {
-            readPackageRestrictionsLPr(user.id);
+    void writeAllRuntimePermissionsLPr() {
+        for (int userId : UserManagerService.getInstance().getUserIds()) {
+            mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId);
         }
     }
 
@@ -1033,6 +1181,25 @@
         }
     }
 
+    private void readDomainVerificationLPw(XmlPullParser parser, PackageSettingBase packageSetting)
+            throws XmlPullParserException, IOException {
+        IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser);
+        packageSetting.setIntentFilterVerificationInfo(ivi);
+        Log.d(TAG, "Read domain verification for package:" + ivi.getPackageName());
+    }
+
+    void writeDomainVerificationsLPr(XmlSerializer serializer, String packageName,
+                                     IntentFilterVerificationInfo verificationInfo)
+            throws IllegalArgumentException, IllegalStateException, IOException {
+        if (verificationInfo != null && verificationInfo.getPackageName() != null) {
+            serializer.startTag(null, TAG_DOMAIN_VERIFICATION);
+            verificationInfo.writeToXml(serializer);
+            Log.d(TAG, "Wrote domain verification for package: "
+                    + verificationInfo.getPackageName());
+            serializer.endTag(null, TAG_DOMAIN_VERIFICATION);
+        }
+    }
+
     void readPackageRestrictionsLPr(int userId) {
         if (DEBUG_MU) {
             Log.i(TAG, "Reading package restrictions for user=" + userId);
@@ -1077,8 +1244,8 @@
                                 false,  // notLaunched
                                 false,  // hidden
                                 null, null, null,
-                                false // blockUninstall
-                                );
+                                false, // blockUninstall
+                                INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
                     }
                     return;
                 }
@@ -1147,6 +1314,12 @@
                     final boolean blockUninstall = blockUninstallStr == null
                             ? false : Boolean.parseBoolean(blockUninstallStr);
 
+                    final String verifStateStr =
+                            parser.getAttributeValue(null, ATTR_DOMAIN_VERIFICATON_STATE);
+                    final int verifState = (verifStateStr == null) ?
+                            PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED :
+                            Integer.parseInt(verifStateStr);
+
                     ArraySet<String> enabledComponents = null;
                     ArraySet<String> disabledComponents = null;
 
@@ -1167,7 +1340,8 @@
                     }
 
                     ps.setUserState(userId, enabled, installed, stopped, notLaunched, hidden,
-                            enabledCaller, enabledComponents, disabledComponents, blockUninstall);
+                            enabledCaller, enabledComponents, disabledComponents, blockUninstall,
+                            verifState);
                 } else if (tagName.equals("preferred-activities")) {
                     readPreferredActivitiesLPw(parser, userId);
                 } else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
@@ -1314,7 +1488,9 @@
                                 && ustate.enabledComponents.size() > 0)
                         || (ustate.disabledComponents != null
                                 && ustate.disabledComponents.size() > 0)
-                        || ustate.blockUninstall) {
+                        || ustate.blockUninstall
+                        || (ustate.domainVerificationStatus !=
+                            PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED)) {
                     serializer.startTag(null, TAG_PACKAGE);
                     serializer.attribute(null, ATTR_NAME, pkg.name);
                     if (DEBUG_MU) Log.i(TAG, "  pkg=" + pkg.name + ", state=" + ustate.enabled);
@@ -1342,6 +1518,11 @@
                                     ustate.lastDisableAppCaller);
                         }
                     }
+                    if (ustate.domainVerificationStatus !=
+                            PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+                        serializer.attribute(null, ATTR_DOMAIN_VERIFICATON_STATE,
+                                Integer.toString(ustate.domainVerificationStatus));
+                    }
                     if (ustate.enabledComponents != null
                             && ustate.enabledComponents.size() > 0) {
                         serializer.startTag(null, TAG_ENABLED_COMPONENTS);
@@ -1362,14 +1543,13 @@
                         }
                         serializer.endTag(null, TAG_DISABLED_COMPONENTS);
                     }
+
                     serializer.endTag(null, TAG_PACKAGE);
                 }
             }
 
             writePreferredActivitiesLPr(serializer, userId, true);
-
             writePersistentPreferredActivitiesLPr(serializer, userId);
-
             writeCrossProfileIntentFiltersLPr(serializer, userId);
 
             serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS);
@@ -1405,6 +1585,58 @@
         }
     }
 
+    void readInstallPermissionsLPr(XmlPullParser parser,
+            PermissionsState permissionsState) throws IOException, XmlPullParserException {
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG
+                    || type == XmlPullParser.TEXT) {
+                continue;
+            }
+            String tagName = parser.getName();
+            if (tagName.equals(TAG_ITEM)) {
+                String name = parser.getAttributeValue(null, ATTR_NAME);
+
+                BasePermission bp = mPermissions.get(name);
+                if (bp == null) {
+                    Slog.w(PackageManagerService.TAG, "Unknown permission: " + name);
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                }
+
+                if (permissionsState.grantInstallPermission(bp) ==
+                        PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                    Slog.w(PackageManagerService.TAG, "Permission already added: " + name);
+                    XmlUtils.skipCurrentTag(parser);
+                }
+            } else {
+                Slog.w(PackageManagerService.TAG, "Unknown element under <permissions>: "
+                        + parser.getName());
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+    }
+
+    void writePermissionsLPr(XmlSerializer serializer, Set<String> permissions)
+            throws IOException {
+        if (permissions.isEmpty()) {
+            return;
+        }
+
+        serializer.startTag(null, TAG_PERMISSIONS);
+
+        for (String permission : permissions) {
+            serializer.startTag(null, TAG_ITEM);
+            serializer.attribute(null, ATTR_NAME, permission);
+            serializer.endTag(null, TAG_ITEM);
+        }
+
+        serializer.endTag(null, TAG_PERMISSIONS);
+    }
+
     // Note: assumed "stopped" field is already cleared in all packages.
     // Legacy reader, used to read in the old file format after an upgrade. Not used after that.
     void readStoppedLPw() {
@@ -1596,13 +1828,7 @@
                 serializer.attribute(null, "userId",
                         Integer.toString(usr.userId));
                 usr.signatures.writeXml(serializer, "sigs", mPastSignatures);
-                serializer.startTag(null, "perms");
-                for (String name : usr.grantedPermissions) {
-                    serializer.startTag(null, TAG_ITEM);
-                    serializer.attribute(null, ATTR_NAME, name);
-                    serializer.endTag(null, TAG_ITEM);
-                }
-                serializer.endTag(null, "perms");
+                writePermissionsLPr(serializer, usr.getPermissionsState().getInstallPermissions());
                 serializer.endTag(null, "shared-user");
             }
 
@@ -1616,7 +1842,7 @@
                     serializer.endTag(null, "cleaning-package");
                 }
             }
-            
+
             if (mRenamedPackages.size() > 0) {
                 for (Map.Entry<String, String> e : mRenamedPackages.entrySet()) {
                     serializer.startTag(null, "renamed-package");
@@ -1625,7 +1851,7 @@
                     serializer.endTag(null, "renamed-package");
                 }
             }
-            
+
             mKeySetManagerService.writeKeySetManagerServiceLPr(serializer);
 
             serializer.endTag(null, "packages");
@@ -1664,7 +1890,7 @@
                     final ApplicationInfo ai = pkg.pkg.applicationInfo;
                     final String dataPath = ai.dataDir;
                     final boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
-                    final int[] gids = pkg.getGids();
+                    final int[] gids = pkg.getPermissionsState().computeGids();
 
                     // Avoid any application that has a space in its path.
                     if (dataPath.indexOf(" ") >= 0)
@@ -1720,6 +1946,8 @@
             }
 
             writeAllUsersPackageRestrictionsLPr();
+
+            writeAllRuntimePermissionsLPr();
             return;
 
         } catch(XmlPullParserException e) {
@@ -1772,26 +2000,12 @@
         } else {
             serializer.attribute(null, "sharedUserId", Integer.toString(pkg.appId));
         }
-        serializer.startTag(null, "perms");
+
+        // If this is a shared user, the permissions will be written there.
         if (pkg.sharedUser == null) {
-            // If this is a shared user, the permissions will
-            // be written there. We still need to write an
-            // empty permissions list so permissionsFixed will
-            // be set.
-            for (final String name : pkg.grantedPermissions) {
-                BasePermission bp = mPermissions.get(name);
-                if (bp != null) {
-                    // We only need to write signature or system permissions but
-                    // this wont
-                    // match the semantics of grantedPermissions. So write all
-                    // permissions.
-                    serializer.startTag(null, TAG_ITEM);
-                    serializer.attribute(null, ATTR_NAME, name);
-                    serializer.endTag(null, TAG_ITEM);
-                }
-            }
+            writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissions());
         }
-        serializer.endTag(null, "perms");
+
         serializer.endTag(null, "updated-package");
     }
 
@@ -1842,24 +2056,13 @@
         }
         pkg.signatures.writeXml(serializer, "sigs", mPastSignatures);
         if ((pkg.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
-            serializer.startTag(null, "perms");
-            if (pkg.sharedUser == null) {
-                // If this is a shared user, the permissions will
-                // be written there. We still need to write an
-                // empty permissions list so permissionsFixed will
-                // be set.
-                for (final String name : pkg.grantedPermissions) {
-                    serializer.startTag(null, TAG_ITEM);
-                    serializer.attribute(null, ATTR_NAME, name);
-                    serializer.endTag(null, TAG_ITEM);
-                }
-            }
-            serializer.endTag(null, "perms");
+            writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissions());
         }
 
         writeSigningKeySetLPr(serializer, pkg.keySetData);
         writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
         writeKeySetAliasesLPr(serializer, pkg.keySetData);
+        writeDomainVerificationsLPr(serializer, pkg.name, pkg.verificationInfo);
 
         serializer.endTag(null, "package");
     }
@@ -2028,7 +2231,8 @@
                     // TODO: check whether this is okay! as it is very
                     // similar to how preferred-activities are treated
                     readCrossProfileIntentFiltersLPw(parser, 0);
-                } else if (tagName.equals("updated-package")) {
+                }
+                else if (tagName.equals("updated-package")) {
                     readDisabledSysPackageLPw(parser);
                 } else if (tagName.equals("cleaning-package")) {
                     String name = parser.getAttributeValue(null, ATTR_NAME);
@@ -2156,9 +2360,11 @@
         } else {
             if (users == null) {
                 readPackageRestrictionsLPr(0);
+                mRuntimePermissionsPersistence.readStateForUserSyncLPr(UserHandle.USER_OWNER);
             } else {
                 for (UserInfo user : users) {
                     readPackageRestrictionsLPr(user.id);
+                    mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id);
                 }
             }
         }
@@ -2532,7 +2738,7 @@
                 final String ptype = parser.getAttributeValue(null, "type");
                 if (name != null && sourcePackage != null) {
                     final boolean dynamic = "dynamic".equals(ptype);
-                    final BasePermission bp = new BasePermission(name, sourcePackage,
+                    final BasePermission bp = new BasePermission(name.intern(), sourcePackage,
                             dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL);
                     bp.protectionLevel = readInt(parser, null, "protection",
                             PermissionInfo.PROTECTION_NORMAL);
@@ -2638,6 +2844,7 @@
             String sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
             ps.appId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0;
         }
+
         int outerDepth = parser.getDepth();
         int type;
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -2646,9 +2853,8 @@
                 continue;
             }
 
-            String tagName = parser.getName();
-            if (tagName.equals("perms")) {
-                readGrantedPermissionsLPw(parser, ps.grantedPermissions);
+            if (parser.getName().equals(TAG_PERMISSIONS)) {
+                readInstallPermissionsLPr(parser, ps.getPermissionsState());
             } else {
                 PackageManagerService.reportSettingsProblem(Log.WARN,
                         "Unknown element under <updated-package>: " + parser.getName());
@@ -2656,6 +2862,18 @@
             }
         }
 
+        // We keep track for which users we granted permissions to be able
+        // to grant runtime permissions to system apps for newly appeared
+        // users or newly appeared system apps. If we supported runtime
+        // permissions during the previous boot, then we already granted
+        // permissions for all device users. In such a case we set the users
+        // for which we granted permissions to avoid clobbering of runtime
+        // permissions we granted to system apps but the user revoked later.
+        if (!isFirstRuntimePermissionsBoot()) {
+            final int[] userIds = UserManagerService.getInstance().getUserIds();
+            ps.setPermissionsUpdatedForUserIds(userIds);
+        }
+
         mDisabledSysPackages.put(name, ps);
     }
 
@@ -2706,7 +2924,7 @@
             if (primaryCpuAbiString == null && legacyCpuAbiString != null) {
                 primaryCpuAbiString = legacyCpuAbiString;
             }
-;
+
             version = parser.getAttributeValue(null, "version");
             if (version != null) {
                 try {
@@ -2897,7 +3115,6 @@
                     packageSetting.installStatus = PackageSettingBase.PKG_INSTALL_COMPLETE;
                 }
             }
-
             int outerDepth = parser.getDepth();
             int type;
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -2914,9 +3131,10 @@
                     readEnabledComponentsLPw(packageSetting, parser, 0);
                 } else if (tagName.equals("sigs")) {
                     packageSetting.signatures.readXml(parser, mPastSignatures);
-                } else if (tagName.equals("perms")) {
-                    readGrantedPermissionsLPw(parser, packageSetting.grantedPermissions);
-                    packageSetting.permissionsFixed = true;
+                } else if (tagName.equals(TAG_PERMISSIONS)) {
+                    readInstallPermissionsLPr(parser,
+                            packageSetting.getPermissionsState());
+                    packageSetting.installPermissionsFixed = true;
                 } else if (tagName.equals("proper-signing-keyset")) {
                     long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
                     Integer refCt = mKeySetRefs.get(id);
@@ -2941,6 +3159,8 @@
                         mKeySetRefs.put(id, 1);
                     }
                     packageSetting.keySetData.addDefinedKeySet(id, alias);
+                } else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
+                    readDomainVerificationLPw(parser, packageSetting);
                 } else {
                     PackageManagerService.reportSettingsProblem(Log.WARN,
                             "Unknown element under <package>: " + parser.getName());
@@ -2948,7 +3168,17 @@
                 }
             }
 
-
+            // We keep track for which users we granted permissions to be able
+            // to grant runtime permissions to system apps for newly appeared
+            // users or newly appeared system apps. If we supported runtime
+            // permissions during the previous boot, then we already granted
+            // permissions for all device users. In such a case we set the users
+            // for which we granted permissions to avoid clobbering of runtime
+            // permissions we granted to system apps but the user revoked later.
+            if (!isFirstRuntimePermissionsBoot()) {
+                final int[] userIds = UserManagerService.getInstance().getUserIds();
+                packageSetting.setPermissionsUpdatedForUserIds(userIds);
+            }
         } else {
             XmlUtils.skipCurrentTag(parser);
         }
@@ -3045,7 +3275,6 @@
                     "Error in package manager settings: package " + name + " has bad userId "
                             + idStr + " at " + parser.getPositionDescription());
         }
-        ;
 
         if (su != null) {
             int outerDepth = parser.getDepth();
@@ -3060,7 +3289,7 @@
                 if (tagName.equals("sigs")) {
                     su.signatures.readXml(parser, mPastSignatures);
                 } else if (tagName.equals("perms")) {
-                    readGrantedPermissionsLPw(parser, su.grantedPermissions);
+                    readInstallPermissionsLPr(parser, su.getPermissionsState());
                 } else {
                     PackageManagerService.reportSettingsProblem(Log.WARN,
                             "Unknown element under <shared-user>: " + parser.getName());
@@ -3068,39 +3297,22 @@
                 }
             }
 
+            // We keep track for which users we granted permissions to be able
+            // to grant runtime permissions to system apps for newly appeared
+            // users or newly appeared system apps. If we supported runtime
+            // permissions during the previous boot, then we already granted
+            // permissions for all device users. In such a case we set the users
+            // for which we granted permissions to avoid clobbering of runtime
+            // permissions we granted to system apps but the user revoked later.
+            if (!isFirstRuntimePermissionsBoot()) {
+                final int[] userIds = UserManagerService.getInstance().getUserIds();
+                su.setPermissionsUpdatedForUserIds(userIds);
+            }
         } else {
             XmlUtils.skipCurrentTag(parser);
         }
     }
 
-    private void readGrantedPermissionsLPw(XmlPullParser parser, ArraySet<String> outPerms)
-            throws IOException, XmlPullParserException {
-        int outerDepth = parser.getDepth();
-        int type;
-        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
-            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                continue;
-            }
-
-            String tagName = parser.getName();
-            if (tagName.equals(TAG_ITEM)) {
-                String name = parser.getAttributeValue(null, ATTR_NAME);
-                if (name != null) {
-                    outPerms.add(name.intern());
-                } else {
-                    PackageManagerService.reportSettingsProblem(Log.WARN,
-                            "Error in package manager settings: <perms> has" + " no name at "
-                                    + parser.getPositionDescription());
-                }
-            } else {
-                PackageManagerService.reportSettingsProblem(Log.WARN,
-                        "Unknown element under <perms>: " + parser.getName());
-            }
-            XmlUtils.skipCurrentTag(parser);
-        }
-    }
-
     void createNewUserLILPw(PackageManagerService service, Installer installer,
             int userHandle, File path) {
         path.mkdir();
@@ -3132,6 +3344,8 @@
         file = getUserPackagesStateBackupFile(userId);
         file.delete();
         removeCrossProfileIntentFiltersLPw(userId);
+
+        mRuntimePermissionsPersistence.onUserRemoved(userId);
     }
 
     void removeCrossProfileIntentFiltersLPw(int userId) {
@@ -3311,7 +3525,7 @@
         return false;
     }
 
-    private List<UserInfo> getAllUsers() {
+    List<UserInfo> getAllUsers() {
         long id = Binder.clearCallingIdentity();
         try {
             return UserManagerService.getInstance().getUsers(false);
@@ -3323,7 +3537,7 @@
         return null;
     }
 
-    static final void printFlags(PrintWriter pw, int val, Object[] spec) {
+    static void printFlags(PrintWriter pw, int val, Object[] spec) {
         pw.print("[ ");
         for (int i=0; i<spec.length; i+=2) {
             int mask = (Integer)spec[i];
@@ -3420,8 +3634,8 @@
             pw.println(ps.name);
         }
 
-        pw.print(prefix); pw.print("  userId="); pw.print(ps.appId);
-                pw.print(" gids="); pw.println(PackageManagerService.arrayToString(ps.gids));
+        pw.print(prefix); pw.print("  userId="); pw.println(ps.appId);
+
         if (ps.sharedUser != null) {
             pw.print(prefix); pw.print("  sharedUser="); pw.println(ps.sharedUser);
         }
@@ -3530,11 +3744,17 @@
                     pw.println(ps.installerPackageName);
         }
         pw.print(prefix); pw.print("  signatures="); pw.println(ps.signatures);
-        pw.print(prefix); pw.print("  permissionsFixed="); pw.print(ps.permissionsFixed);
-                pw.print(" haveGids="); pw.print(ps.haveGids);
+        pw.print(prefix); pw.print("  installPermissionsFixed=");
+                pw.print(ps.installPermissionsFixed);
                 pw.print(" installStatus="); pw.println(ps.installStatus);
         pw.print(prefix); pw.print("  pkgFlags="); printFlags(pw, ps.pkgFlags, FLAG_DUMP_SPEC);
                 pw.println();
+
+        if (ps.sharedUser == null) {
+            PermissionsState permissionsState = ps.getPermissionsState();
+            dumpInstallPermissionsLPr(pw, prefix + "  ", permissionsState);
+        }
+
         for (UserInfo user : users) {
             pw.print(prefix); pw.print("  User "); pw.print(user.id); pw.print(": ");
             pw.print(" installed=");
@@ -3552,6 +3772,14 @@
                 pw.print(prefix); pw.print("    lastDisabledCaller: ");
                         pw.println(lastDisabledAppCaller);
             }
+
+            if (ps.sharedUser == null) {
+                PermissionsState permissionsState = ps.getPermissionsState();
+                dumpGidsLPr(pw, prefix + "    ", permissionsState.computeGids(user.id));
+                dumpRuntimePermissionsLPr(pw, prefix + "    ", permissionsState
+                        .getRuntimePermissions(user.id));
+            }
+
             ArraySet<String> cmp = ps.getDisabledComponents(user.id);
             if (cmp != null && cmp.size() > 0) {
                 pw.print(prefix); pw.println("    disabledComponents:");
@@ -3567,12 +3795,6 @@
                 }
             }
         }
-        if (ps.grantedPermissions.size() > 0) {
-            pw.print(prefix); pw.println("  grantedPermissions:");
-            for (String s : ps.grantedPermissions) {
-                pw.print(prefix); pw.print("    "); pw.println(s);
-            }
-        }
     }
 
     void dumpPackagesLPr(PrintWriter pw, String packageName, DumpState dumpState, boolean checkin) {
@@ -3658,7 +3880,8 @@
                     pw.println("):");
             pw.print("    sourcePackage="); pw.println(p.sourcePackage);
             pw.print("    uid="); pw.print(p.uid);
-                    pw.print(" gids="); pw.print(PackageManagerService.arrayToString(p.gids));
+                    pw.print(" gids="); pw.print(Arrays.toString(
+                            p.computeGids(UserHandle.USER_OWNER)));
                     pw.print(" type="); pw.print(p.type);
                     pw.print(" prot=");
                     pw.println(PermissionInfo.protectionToString(p.protectionLevel));
@@ -3694,14 +3917,21 @@
                 pw.print("] (");
                 pw.print(Integer.toHexString(System.identityHashCode(su)));
                         pw.println("):");
-                pw.print("    userId=");
-                pw.print(su.userId);
-                pw.print(" gids=");
-                pw.println(PackageManagerService.arrayToString(su.gids));
-                pw.println("    grantedPermissions:");
-                for (String s : su.grantedPermissions) {
-                    pw.print("      ");
-                    pw.println(s);
+
+                String prefix = "    ";
+                pw.print(prefix); pw.print("userId="); pw.println(su.userId);
+
+                PermissionsState permissionsState = su.getPermissionsState();
+                dumpInstallPermissionsLPr(pw, prefix, permissionsState);
+
+                for (int userId : UserManagerService.getInstance().getUserIds()) {
+                    final int[] gids = permissionsState.computeGids(userId);
+                    Set<String> permissions = permissionsState.getRuntimePermissions(userId);
+                    if (!ArrayUtils.isEmpty(gids) || !permissions.isEmpty()) {
+                        pw.print(prefix); pw.print("User "); pw.print(userId); pw.println(": ");
+                        dumpGidsLPr(pw, prefix + "  ", gids);
+                        dumpRuntimePermissionsLPr(pw, prefix + "  ", permissions);
+                    }
                 }
             } else {
                 pw.print("suid,"); pw.print(su.userId); pw.print(","); pw.println(su.name);
@@ -3736,4 +3966,320 @@
             pw.print("]");
         }
     }
+
+    void dumpGidsLPr(PrintWriter pw, String prefix, int[] gids) {
+        if (!ArrayUtils.isEmpty(gids)) {
+            pw.print(prefix); pw.print("gids="); pw.println(
+                    PackageManagerService.arrayToString(gids));
+        }
+    }
+
+    void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix, Set<String> permissions) {
+        if (!permissions.isEmpty()) {
+            pw.print(prefix); pw.println("runtime permissions:");
+            for (String permission : permissions) {
+                pw.print(prefix); pw.print("  "); pw.println(permission);
+            }
+        }
+    }
+
+    void dumpInstallPermissionsLPr(PrintWriter pw, String prefix,
+            PermissionsState permissionsState) {
+        Set<String> permissions = permissionsState.getInstallPermissions();
+        if (!permissions.isEmpty()) {
+            pw.print(prefix); pw.println("install permissions:");
+            for (String permission : permissions) {
+                pw.print(prefix); pw.print("  "); pw.println(permission);
+            }
+        }
+    }
+
+    public void writeRuntimePermissionsForUserLPr(int userId, boolean sync) {
+        if (sync) {
+            mRuntimePermissionsPersistence.writePermissionsForUserSyncLPr(userId);
+        } else {
+            mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId);
+        }
+    }
+
+    private final class RuntimePermissionPersistence {
+        private static final long WRITE_PERMISSIONS_DELAY_MILLIS = 200;
+
+        private static final long MAX_WRITE_PERMISSIONS_DELAY_MILLIS = 2000;
+
+        private final Handler mHandler = new MyHandler();
+
+        private final Object mLock;
+
+        @GuardedBy("mLock")
+        private SparseBooleanArray mWriteScheduled = new SparseBooleanArray();
+
+        @GuardedBy("mLock")
+        private SparseLongArray mLastNotWrittenMutationTimesMillis = new SparseLongArray();
+
+        public RuntimePermissionPersistence(Object lock) {
+            mLock = lock;
+        }
+
+        public void writePermissionsForUserSyncLPr(int userId) {
+            if (!PackageManagerService.RUNTIME_PERMISSIONS_ENABLED) {
+                return;
+            }
+
+            mHandler.removeMessages(userId);
+            writePermissionsSync(userId);
+        }
+
+        public void writePermissionsForUserAsyncLPr(int userId) {
+            if (!PackageManagerService.RUNTIME_PERMISSIONS_ENABLED) {
+                return;
+            }
+
+            final long currentTimeMillis = SystemClock.uptimeMillis();
+
+            if (mWriteScheduled.get(userId)) {
+                mHandler.removeMessages(userId);
+
+                // If enough time passed, write without holding off anymore.
+                final long lastNotWrittenMutationTimeMillis = mLastNotWrittenMutationTimesMillis
+                        .get(userId);
+                final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis
+                        - lastNotWrittenMutationTimeMillis;
+                if (timeSinceLastNotWrittenMutationMillis >= MAX_WRITE_PERMISSIONS_DELAY_MILLIS) {
+                    mHandler.obtainMessage(userId).sendToTarget();
+                    return;
+                }
+
+                // Hold off a bit more as settings are frequently changing.
+                final long maxDelayMillis = Math.max(lastNotWrittenMutationTimeMillis
+                        + MAX_WRITE_PERMISSIONS_DELAY_MILLIS - currentTimeMillis, 0);
+                final long writeDelayMillis = Math.min(WRITE_PERMISSIONS_DELAY_MILLIS,
+                        maxDelayMillis);
+
+                Message message = mHandler.obtainMessage(userId);
+                mHandler.sendMessageDelayed(message, writeDelayMillis);
+            } else {
+                mLastNotWrittenMutationTimesMillis.put(userId, currentTimeMillis);
+                Message message = mHandler.obtainMessage(userId);
+                mHandler.sendMessageDelayed(message, WRITE_PERMISSIONS_DELAY_MILLIS);
+                mWriteScheduled.put(userId, true);
+            }
+        }
+
+        private void writePermissionsSync(int userId) {
+            AtomicFile destination = new AtomicFile(getUserRuntimePermissionsFile(userId));
+
+            ArrayMap<String, Set<String>> permissionsForPackage = new ArrayMap<>();
+            ArrayMap<String, Set<String>> permissionsForSharedUser = new ArrayMap<>();
+
+            synchronized (mLock) {
+                mWriteScheduled.delete(userId);
+
+                final int packageCount = mPackages.size();
+                for (int i = 0; i < packageCount; i++) {
+                    String packageName = mPackages.keyAt(i);
+                    PackageSetting packageSetting = mPackages.valueAt(i);
+                    if (packageSetting.sharedUser == null) {
+                        PermissionsState permissionsState = packageSetting.getPermissionsState();
+                        Set<String> permissions = permissionsState.getRuntimePermissions(userId);
+                        if (!permissions.isEmpty()) {
+                            permissionsForPackage.put(packageName, permissions);
+                        }
+                    }
+                }
+
+                final int sharedUserCount = mSharedUsers.size();
+                for (int i = 0; i < sharedUserCount; i++) {
+                    String sharedUserName = mSharedUsers.keyAt(i);
+                    SharedUserSetting sharedUser = mSharedUsers.valueAt(i);
+                    PermissionsState permissionsState = sharedUser.getPermissionsState();
+                    Set<String> permissions = permissionsState.getRuntimePermissions(userId);
+                    if (!permissions.isEmpty()) {
+                        permissionsForSharedUser.put(sharedUserName, permissions);
+                    }
+                }
+            }
+
+            FileOutputStream out = null;
+            try {
+                out = destination.startWrite();
+
+                XmlSerializer serializer = Xml.newSerializer();
+                serializer.setOutput(out, "utf-8");
+                serializer.setFeature(
+                        "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+                serializer.startDocument(null, true);
+                serializer.startTag(null, TAG_RUNTIME_PERMISSIONS);
+
+                final int packageCount = permissionsForPackage.size();
+                for (int i = 0; i < packageCount; i++) {
+                    String packageName = permissionsForPackage.keyAt(i);
+                    Set<String> permissions = permissionsForPackage.valueAt(i);
+                    serializer.startTag(null, TAG_PACKAGE);
+                    serializer.attribute(null, ATTR_NAME, packageName);
+                    writePermissions(serializer, permissions);
+                    serializer.endTag(null, TAG_PACKAGE);
+                }
+
+                final int sharedUserCount = permissionsForSharedUser.size();
+                for (int i = 0; i < sharedUserCount; i++) {
+                    String packageName = permissionsForSharedUser.keyAt(i);
+                    Set<String> permissions = permissionsForSharedUser.valueAt(i);
+                    serializer.startTag(null, TAG_SHARED_USER);
+                    serializer.attribute(null, ATTR_NAME, packageName);
+                    writePermissions(serializer, permissions);
+                    serializer.endTag(null, TAG_SHARED_USER);
+                }
+
+                serializer.endTag(null, TAG_RUNTIME_PERMISSIONS);
+                serializer.endDocument();
+                destination.finishWrite(out);
+            } catch (IOException e) {
+                Slog.wtf(PackageManagerService.TAG,
+                        "Failed to write settings, restoring backup", e);
+                destination.failWrite(out);
+            } finally {
+                IoUtils.closeQuietly(out);
+            }
+        }
+
+        private void onUserRemoved(int userId) {
+            // Make sure we do not
+            mHandler.removeMessages(userId);
+
+            for (SettingBase sb : mPackages.values()) {
+                revokeRuntimePermissions(sb, userId);
+            }
+
+            for (SettingBase sb : mSharedUsers.values()) {
+                revokeRuntimePermissions(sb, userId);
+            }
+        }
+
+        private void revokeRuntimePermissions(SettingBase sb, int userId) {
+            PermissionsState permissionsState = sb.getPermissionsState();
+            for (String permission : permissionsState.getRuntimePermissions(userId)) {
+                BasePermission bp = mPermissions.get(permission);
+                if (bp != null) {
+                    permissionsState.revokeRuntimePermission(bp, userId);
+                }
+            }
+        }
+
+        public void readStateForUserSyncLPr(int userId) {
+            File permissionsFile = getUserRuntimePermissionsFile(userId);
+            if (!permissionsFile.exists()) {
+                return;
+            }
+
+            FileInputStream in;
+            try {
+                in = new FileInputStream(permissionsFile);
+            } catch (FileNotFoundException fnfe) {
+                Slog.i(PackageManagerService.TAG, "No permissions state");
+                return;
+            }
+
+            try {
+                XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(in, null);
+                parseRuntimePermissionsLPr(parser, userId);
+            } catch (XmlPullParserException | IOException ise) {
+                throw new IllegalStateException("Failed parsing permissions file: "
+                        + permissionsFile , ise);
+            } finally {
+                IoUtils.closeQuietly(in);
+            }
+        }
+
+        private void parseRuntimePermissionsLPr(XmlPullParser parser, int userId)
+                throws IOException, XmlPullParserException {
+            final int outerDepth = parser.getDepth();
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                    continue;
+                }
+
+                switch (parser.getName()) {
+                    case TAG_PACKAGE: {
+                        String name = parser.getAttributeValue(null, ATTR_NAME);
+                        PackageSetting ps = mPackages.get(name);
+                        if (ps == null) {
+                            Slog.w(PackageManagerService.TAG, "Unknown package:" + name);
+                            XmlUtils.skipCurrentTag(parser);
+                            continue;
+                        }
+                        parsePermissionsLPr(parser, ps.getPermissionsState(), userId);
+                    } break;
+
+                    case TAG_SHARED_USER: {
+                        String name = parser.getAttributeValue(null, ATTR_NAME);
+                        SharedUserSetting sus = mSharedUsers.get(name);
+                        if (sus == null) {
+                            Slog.w(PackageManagerService.TAG, "Unknown shared user:" + name);
+                            XmlUtils.skipCurrentTag(parser);
+                            continue;
+                        }
+                        parsePermissionsLPr(parser, sus.getPermissionsState(), userId);
+                    } break;
+                }
+            }
+        }
+
+        private void parsePermissionsLPr(XmlPullParser parser, PermissionsState permissionsState,
+                int userId) throws IOException, XmlPullParserException {
+            final int outerDepth = parser.getDepth();
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                    continue;
+                }
+
+                switch (parser.getName()) {
+                    case TAG_ITEM: {
+                        String name = parser.getAttributeValue(null, ATTR_NAME);
+                        BasePermission bp = mPermissions.get(name);
+                        if (bp == null) {
+                            Slog.w(PackageManagerService.TAG, "Unknown permission:" + name);
+                            XmlUtils.skipCurrentTag(parser);
+                            continue;
+                        }
+
+                        if (permissionsState.grantRuntimePermission(bp, userId) ==
+                                PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                            Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name);
+                        }
+                    } break;
+                }
+            }
+        }
+
+        private void writePermissions(XmlSerializer serializer, Set<String> permissions)
+                throws IOException {
+            for (String permission : permissions) {
+                serializer.startTag(null, TAG_ITEM);
+                serializer.attribute(null, ATTR_NAME, permission);
+                serializer.endTag(null, TAG_ITEM);
+            }
+        }
+
+        private final class MyHandler extends Handler {
+            public MyHandler() {
+                super(BackgroundThread.getHandler().getLooper());
+            }
+
+            @Override
+            public void handleMessage(Message message) {
+                final int userId = message.what;
+                Runnable callback = (Runnable) message.obj;
+                writePermissionsSync(userId);
+                if (callback != null) {
+                    callback.run();
+                }
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index d95739c..06e020a 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -21,7 +21,7 @@
 /**
  * Settings data for a particular shared user ID we know about.
  */
-final class SharedUserSetting extends GrantedPermissions {
+final class SharedUserSetting extends SettingBase {
     final String name;
 
     int userId;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 26ecb72..5e58cd9 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -36,6 +36,7 @@
 import android.os.IUserManager;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -49,9 +50,11 @@
 import android.util.TimeUtils;
 import android.util.Xml;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -71,6 +74,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import libcore.io.IoUtils;
+
 public class UserManagerService extends IUserManager.Stub {
 
     private static final String LOG_TAG = "UserManagerService";
@@ -107,6 +112,8 @@
     private static final String ATTR_TYPE_STRING = "s";
     private static final String ATTR_TYPE_BOOLEAN = "b";
     private static final String ATTR_TYPE_INTEGER = "i";
+    private static final String ATTR_TYPE_BUNDLE = "B";
+    private static final String ATTR_TYPE_BUNDLE_ARRAY = "BA";
 
     private static final String USER_INFO_DIR = "system" + File.separator + "users";
     private static final String USER_LIST_FILENAME = "userlist.xml";
@@ -1219,6 +1226,7 @@
                     updateUserIdsLocked();
                     Bundle restrictions = new Bundle();
                     mUserRestrictions.append(userId, restrictions);
+                    mPm.newUserCreatedLILPw(userId);
                 }
             }
             if (userInfo != null) {
@@ -1671,124 +1679,168 @@
 
     private Bundle readApplicationRestrictionsLocked(String packageName,
             int userId) {
+        AtomicFile restrictionsFile =
+                new AtomicFile(new File(Environment.getUserSystemDirectory(userId),
+                        packageToRestrictionsFileName(packageName)));
+        return readApplicationRestrictionsLocked(restrictionsFile);
+    }
+
+    @VisibleForTesting
+    static Bundle readApplicationRestrictionsLocked(AtomicFile restrictionsFile) {
         final Bundle restrictions = new Bundle();
-        final ArrayList<String> values = new ArrayList<String>();
+        final ArrayList<String> values = new ArrayList<>();
 
         FileInputStream fis = null;
         try {
-            AtomicFile restrictionsFile =
-                    new AtomicFile(new File(Environment.getUserSystemDirectory(userId),
-                            packageToRestrictionsFileName(packageName)));
             fis = restrictionsFile.openRead();
             XmlPullParser parser = Xml.newPullParser();
             parser.setInput(fis, null);
-            int type;
-            while ((type = parser.next()) != XmlPullParser.START_TAG
-                    && type != XmlPullParser.END_DOCUMENT) {
-                ;
-            }
-
-            if (type != XmlPullParser.START_TAG) {
+            XmlUtils.nextElement(parser);
+            if (parser.getEventType() != XmlPullParser.START_TAG) {
                 Slog.e(LOG_TAG, "Unable to read restrictions file "
                         + restrictionsFile.getBaseFile());
                 return restrictions;
             }
-
-            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
-                if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) {
-                    String key = parser.getAttributeValue(null, ATTR_KEY);
-                    String valType = parser.getAttributeValue(null, ATTR_VALUE_TYPE);
-                    String multiple = parser.getAttributeValue(null, ATTR_MULTIPLE);
-                    if (multiple != null) {
-                        values.clear();
-                        int count = Integer.parseInt(multiple);
-                        while (count > 0 && (type = parser.next()) != XmlPullParser.END_DOCUMENT) {
-                            if (type == XmlPullParser.START_TAG
-                                    && parser.getName().equals(TAG_VALUE)) {
-                                values.add(parser.nextText().trim());
-                                count--;
-                            }
-                        }
-                        String [] valueStrings = new String[values.size()];
-                        values.toArray(valueStrings);
-                        restrictions.putStringArray(key, valueStrings);
-                    } else {
-                        String value = parser.nextText().trim();
-                        if (ATTR_TYPE_BOOLEAN.equals(valType)) {
-                            restrictions.putBoolean(key, Boolean.parseBoolean(value));
-                        } else if (ATTR_TYPE_INTEGER.equals(valType)) {
-                            restrictions.putInt(key, Integer.parseInt(value));
-                        } else {
-                            restrictions.putString(key, value);
-                        }
-                    }
-                }
+            while (parser.next() != XmlPullParser.END_DOCUMENT) {
+                readEntry(restrictions, values, parser);
             }
-        } catch (IOException ioe) {
-        } catch (XmlPullParserException pe) {
+        } catch (IOException|XmlPullParserException e) {
+            Log.w(LOG_TAG, "Error parsing " + restrictionsFile.getBaseFile(), e);
         } finally {
-            if (fis != null) {
-                try {
-                    fis.close();
-                } catch (IOException e) {
-                }
-            }
+            IoUtils.closeQuietly(fis);
         }
         return restrictions;
     }
 
+    private static void readEntry(Bundle restrictions, ArrayList<String> values,
+            XmlPullParser parser) throws XmlPullParserException, IOException {
+        int type = parser.getEventType();
+        if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) {
+            String key = parser.getAttributeValue(null, ATTR_KEY);
+            String valType = parser.getAttributeValue(null, ATTR_VALUE_TYPE);
+            String multiple = parser.getAttributeValue(null, ATTR_MULTIPLE);
+            if (multiple != null) {
+                values.clear();
+                int count = Integer.parseInt(multiple);
+                while (count > 0 && (type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+                    if (type == XmlPullParser.START_TAG
+                            && parser.getName().equals(TAG_VALUE)) {
+                        values.add(parser.nextText().trim());
+                        count--;
+                    }
+                }
+                String [] valueStrings = new String[values.size()];
+                values.toArray(valueStrings);
+                restrictions.putStringArray(key, valueStrings);
+            } else if (ATTR_TYPE_BUNDLE.equals(valType)) {
+                restrictions.putBundle(key, readBundleEntry(parser, values));
+            } else if (ATTR_TYPE_BUNDLE_ARRAY.equals(valType)) {
+                final int outerDepth = parser.getDepth();
+                ArrayList<Bundle> bundleList = new ArrayList<>();
+                while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+                    Bundle childBundle = readBundleEntry(parser, values);
+                    bundleList.add(childBundle);
+                }
+                restrictions.putParcelableArray(key,
+                        bundleList.toArray(new Bundle[bundleList.size()]));
+            } else {
+                String value = parser.nextText().trim();
+                if (ATTR_TYPE_BOOLEAN.equals(valType)) {
+                    restrictions.putBoolean(key, Boolean.parseBoolean(value));
+                } else if (ATTR_TYPE_INTEGER.equals(valType)) {
+                    restrictions.putInt(key, Integer.parseInt(value));
+                } else {
+                    restrictions.putString(key, value);
+                }
+            }
+        }
+    }
+
+    private static Bundle readBundleEntry(XmlPullParser parser, ArrayList<String> values)
+            throws IOException, XmlPullParserException {
+        Bundle childBundle = new Bundle();
+        final int outerDepth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+            readEntry(childBundle, values, parser);
+        }
+        return childBundle;
+    }
+
     private void writeApplicationRestrictionsLocked(String packageName,
             Bundle restrictions, int userId) {
-        FileOutputStream fos = null;
         AtomicFile restrictionsFile = new AtomicFile(
                 new File(Environment.getUserSystemDirectory(userId),
                         packageToRestrictionsFileName(packageName)));
+        writeApplicationRestrictionsLocked(restrictions, restrictionsFile);
+    }
+
+    @VisibleForTesting
+    static void writeApplicationRestrictionsLocked(Bundle restrictions,
+            AtomicFile restrictionsFile) {
+        FileOutputStream fos = null;
         try {
             fos = restrictionsFile.startWrite();
             final BufferedOutputStream bos = new BufferedOutputStream(fos);
 
-            // XmlSerializer serializer = XmlUtils.serializerInstance();
             final XmlSerializer serializer = new FastXmlSerializer();
             serializer.setOutput(bos, "utf-8");
             serializer.startDocument(null, true);
             serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
 
             serializer.startTag(null, TAG_RESTRICTIONS);
-
-            for (String key : restrictions.keySet()) {
-                Object value = restrictions.get(key);
-                serializer.startTag(null, TAG_ENTRY);
-                serializer.attribute(null, ATTR_KEY, key);
-
-                if (value instanceof Boolean) {
-                    serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BOOLEAN);
-                    serializer.text(value.toString());
-                } else if (value instanceof Integer) {
-                    serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_INTEGER);
-                    serializer.text(value.toString());
-                } else if (value == null || value instanceof String) {
-                    serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING);
-                    serializer.text(value != null ? (String) value : "");
-                } else {
-                    serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING_ARRAY);
-                    String[] values = (String[]) value;
-                    serializer.attribute(null, ATTR_MULTIPLE, Integer.toString(values.length));
-                    for (String choice : values) {
-                        serializer.startTag(null, TAG_VALUE);
-                        serializer.text(choice != null ? choice : "");
-                        serializer.endTag(null, TAG_VALUE);
-                    }
-                }
-                serializer.endTag(null, TAG_ENTRY);
-            }
-
+            writeBundle(restrictions, serializer);
             serializer.endTag(null, TAG_RESTRICTIONS);
 
             serializer.endDocument();
             restrictionsFile.finishWrite(fos);
         } catch (Exception e) {
             restrictionsFile.failWrite(fos);
-            Slog.e(LOG_TAG, "Error writing application restrictions list");
+            Slog.e(LOG_TAG, "Error writing application restrictions list", e);
+        }
+    }
+
+    private static void writeBundle(Bundle restrictions, XmlSerializer serializer)
+            throws IOException {
+        for (String key : restrictions.keySet()) {
+            Object value = restrictions.get(key);
+            serializer.startTag(null, TAG_ENTRY);
+            serializer.attribute(null, ATTR_KEY, key);
+
+            if (value instanceof Boolean) {
+                serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BOOLEAN);
+                serializer.text(value.toString());
+            } else if (value instanceof Integer) {
+                serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_INTEGER);
+                serializer.text(value.toString());
+            } else if (value == null || value instanceof String) {
+                serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING);
+                serializer.text(value != null ? (String) value : "");
+            } else if (value instanceof Bundle) {
+                serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BUNDLE);
+                writeBundle((Bundle) value, serializer);
+            } else if (value instanceof Parcelable[]) {
+                serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BUNDLE_ARRAY);
+                Parcelable[] array = (Parcelable[]) value;
+                for (Parcelable parcelable : array) {
+                    if (!(parcelable instanceof Bundle)) {
+                        throw new IllegalArgumentException("bundle-array can only hold Bundles");
+                    }
+                    serializer.startTag(null, TAG_ENTRY);
+                    serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BUNDLE);
+                    writeBundle((Bundle) parcelable, serializer);
+                    serializer.endTag(null, TAG_ENTRY);
+                }
+            } else {
+                serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING_ARRAY);
+                String[] values = (String[]) value;
+                serializer.attribute(null, ATTR_MULTIPLE, Integer.toString(values.length));
+                for (String choice : values) {
+                    serializer.startTag(null, TAG_VALUE);
+                    serializer.text(choice != null ? choice : "");
+                    serializer.endTag(null, TAG_VALUE);
+                }
+            }
+            serializer.endTag(null, TAG_ENTRY);
         }
     }
 
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 930ef9a..958caea 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1398,6 +1398,14 @@
                     public void onDebug() {
                         // no-op
                     }
+                    @Override
+                    public void onDown() {
+                        mOrientationListener.onTouchStart();
+                    }
+                    @Override
+                    public void onUpOrCancel() {
+                        mOrientationListener.onTouchEnd();
+                    }
                 });
         mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext);
         mWindowManagerFuncs.registerPointerEventListener(mSystemGestures);
@@ -3501,8 +3509,14 @@
     /** {@inheritDoc} */
     @Override
     public int getSystemDecorLayerLw() {
-        if (mStatusBar != null) return mStatusBar.getSurfaceLayer();
-        if (mNavigationBar != null) return mNavigationBar.getSurfaceLayer();
+        if (mStatusBar != null && mStatusBar.isVisibleLw()) {
+            return mStatusBar.getSurfaceLayer();
+        }
+
+        if (mNavigationBar != null && mNavigationBar.isVisibleLw()) {
+            return mNavigationBar.getSurfaceLayer();
+        }
+
         return 0;
     }
 
@@ -4025,7 +4039,7 @@
     }
 
     private void offsetInputMethodWindowLw(WindowState win) {
-        int top = win.getDisplayFrameLw().top;
+        int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
         top += win.getGivenContentInsetsLw().top;
         if (mContentBottom > top) {
             mContentBottom = top;
@@ -4044,7 +4058,7 @@
     }
 
     private void offsetVoiceInputWindowLw(WindowState win) {
-        int top = win.getDisplayFrameLw().top;
+        int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
         top += win.getGivenContentInsetsLw().top;
         if (mVoiceContentBottom > top) {
             mVoiceContentBottom = top;
diff --git a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
index cfa631f..627b328 100644
--- a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
@@ -75,6 +75,7 @@
                 mDebugFireable = true;
                 mDownPointers = 0;
                 captureDown(event, 0);
+                mCallbacks.onDown();
                 break;
             case MotionEvent.ACTION_POINTER_DOWN:
                 captureDown(event, event.getActionIndex());
@@ -106,6 +107,7 @@
             case MotionEvent.ACTION_CANCEL:
                 mSwipeFireable = false;
                 mDebugFireable = false;
+                mCallbacks.onUpOrCancel();
                 break;
             default:
                 if (DEBUG) Slog.d(TAG, "Ignoring " + event);
@@ -192,6 +194,8 @@
         void onSwipeFromTop();
         void onSwipeFromBottom();
         void onSwipeFromRight();
+        void onDown();
+        void onUpOrCancel();
         void onDebug();
     }
 }
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java
index 0118127..c8fd82e 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java
@@ -22,6 +22,7 @@
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.util.Log;
 import android.util.Slog;
@@ -133,6 +134,24 @@
         }
     }
 
+    public void onTouchStart() {
+        synchronized (mLock) {
+            if (mSensorEventListener != null) {
+                mSensorEventListener.onTouchStartLocked();
+            }
+        }
+    }
+
+    public void onTouchEnd() {
+        long whenElapsedNanos = SystemClock.elapsedRealtimeNanos();
+
+        synchronized (mLock) {
+            if (mSensorEventListener != null) {
+                mSensorEventListener.onTouchEndLocked(whenElapsedNanos);
+            }
+        }
+    }
+
     /**
      * Sets the current rotation.
      *
@@ -269,6 +288,11 @@
         private static final long PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS =
                 500 * NANOS_PER_MS;
 
+        // The minimum amount of time that must have elapsed since the screen was last touched
+        // before the proposed rotation can change.
+        private static final long PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS =
+                500 * NANOS_PER_MS;
+
         // If the tilt angle remains greater than the specified angle for a minimum of
         // the specified time, then the device is deemed to be lying flat
         // (just chillin' on a table).
@@ -398,6 +422,10 @@
         private long mAccelerationTimestampNanos;
         private boolean mAccelerating;
 
+        // Timestamp when the last touch to the touch screen ended
+        private long mTouchEndedTimestampNanos = Long.MIN_VALUE;
+        private boolean mTouched;
+
         // Whether we are locked into an overhead usage mode.
         private boolean mOverhead;
 
@@ -422,6 +450,7 @@
             pw.println(prefix + "mSwinging=" + mSwinging);
             pw.println(prefix + "mAccelerating=" + mAccelerating);
             pw.println(prefix + "mOverhead=" + mOverhead);
+            pw.println(prefix + "mTouched=" + mTouched);
         }
 
         @Override
@@ -601,6 +630,7 @@
                             + ", isFlat=" + isFlat
                             + ", isSwinging=" + isSwinging
                             + ", isOverhead=" + mOverhead
+                            + ", isTouched=" + mTouched
                             + ", timeUntilSettledMS=" + remainingMS(now,
                                     mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS)
                             + ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now,
@@ -608,7 +638,9 @@
                             + ", timeUntilFlatDelayExpiredMS=" + remainingMS(now,
                                     mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS)
                             + ", timeUntilSwingDelayExpiredMS=" + remainingMS(now,
-                                    mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS));
+                                    mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS)
+                            + ", timeUntilTouchDelayExpiredMS=" + remainingMS(now,
+                                    mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS));
                 }
             }
 
@@ -710,6 +742,12 @@
                 return false;
             }
 
+            // The last touch must have ended sufficiently long ago.
+            if (mTouched || now < mTouchEndedTimestampNanos
+                    + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS) {
+                return false;
+            }
+
             // Looks good!
             return true;
         }
@@ -796,5 +834,14 @@
         private float remainingMS(long now, long until) {
             return now >= until ? 0 : (until - now) * 0.000001f;
         }
+
+        private void onTouchStartLocked() {
+            mTouched = true;
+        }
+
+        private void onTouchEndLocked(long whenElapsedNanos) {
+            mTouched = false;
+            mTouchEndedTimestampNanos = whenElapsedNanos;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/power/DeviceIdleController.java
similarity index 84%
rename from services/core/java/com/android/server/DeviceIdleController.java
rename to services/core/java/com/android/server/power/DeviceIdleController.java
index 062992d..dd00446 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/power/DeviceIdleController.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package com.android.server.power;
 
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -30,12 +30,16 @@
 import android.hardware.display.DisplayManager;
 import android.net.INetworkPolicyManager;
 import android.os.Binder;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.util.TimeUtils;
 import android.view.Display;
 import com.android.internal.app.IBatteryStats;
+import com.android.server.SystemService;
 import com.android.server.am.BatteryStatsService;
 
 import java.io.FileDescriptor;
@@ -60,6 +64,11 @@
      */
     private static final long DEFAULT_INACTIVE_TIMEOUT = 30*60*1000L;
     /**
+     * This is the time, after seeing motion, that we wait after becoming inactive from
+     * that until we start looking for motion again.
+     */
+    private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT = 10*60*1000L;
+    /**
      * This is the time, after the inactive timeout elapses, that we will wait looking
      * for significant motion until we truly consider the device to be idle.
      */
@@ -94,11 +103,13 @@
 
     private AlarmManager mAlarmManager;
     private IBatteryStats mBatteryStats;
+    private PowerManagerInternal mLocalPowerManager;
     private INetworkPolicyManager mNetworkPolicyManager;
     private DisplayManager mDisplayManager;
     private SensorManager mSensorManager;
     private Sensor mSigMotionSensor;
     private PendingIntent mAlarmIntent;
+    private Intent mIdleIntent;
     private Display mCurDisplay;
     private boolean mScreenOn;
     private boolean mCharging;
@@ -124,6 +135,7 @@
 
     private int mState;
 
+    private long mInactiveTimeout;
     private long mNextAlarmTime;
     private long mNextIdlePendingDelay;
     private long mNextIdleDelay;
@@ -181,6 +193,7 @@
         synchronized (this) {
             mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
             mBatteryStats = BatteryStatsService.getService();
+            mLocalPowerManager = getLocalService(PowerManagerInternal.class);
             mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
                                 ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
             mDisplayManager = (DisplayManager) getContext().getSystemService(
@@ -193,6 +206,9 @@
                     .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
             mAlarmIntent = PendingIntent.getBroadcast(getContext(), 0, intent, 0);
 
+            mIdleIntent = new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+            mIdleIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+
             IntentFilter filter = new IntentFilter();
             filter.addAction(Intent.ACTION_BATTERY_CHANGED);
             filter.addAction(ACTION_STEP_IDLE_STATE);
@@ -205,6 +221,7 @@
             // a battery update the next time the level drops.
             mCharging = true;
             mState = STATE_ACTIVE;
+            mInactiveTimeout = DEFAULT_INACTIVE_TIMEOUT;
             updateDisplayLocked();
         }
 
@@ -238,12 +255,17 @@
 
     void becomeActiveLocked() {
         if (mState != STATE_ACTIVE) {
+            mLocalPowerManager.setDeviceIdleMode(false);
             try {
                 mNetworkPolicyManager.setDeviceIdleMode(false);
                 mBatteryStats.noteDeviceIdleMode(false, true, false);
             } catch (RemoteException e) {
             }
+            if (mState == STATE_IDLE) {
+                getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
+            }
             mState = STATE_ACTIVE;
+            mInactiveTimeout = DEFAULT_INACTIVE_TIMEOUT;
             mNextIdlePendingDelay = 0;
             mNextIdleDelay = 0;
             cancelAlarmLocked();
@@ -256,7 +278,9 @@
             // Screen has turned off; we are now going to become inactive and start
             // waiting to see if we will ultimately go idle.
             mState = STATE_INACTIVE;
-            scheduleAlarmLocked(DEFAULT_INACTIVE_TIMEOUT, false);
+            mNextIdlePendingDelay = 0;
+            mNextIdleDelay = 0;
+            scheduleAlarmLocked(mInactiveTimeout, false);
         }
     }
 
@@ -283,11 +307,13 @@
                     mNextIdleDelay = DEFAULT_MAX_IDLE_TIMEOUT;
                 }
                 mState = STATE_IDLE;
+                mLocalPowerManager.setDeviceIdleMode(true);
                 try {
                     mNetworkPolicyManager.setDeviceIdleMode(true);
                     mBatteryStats.noteDeviceIdleMode(true, false, false);
                 } catch (RemoteException e) {
                 }
+                getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
                 break;
             case STATE_IDLE:
                 // We have been idling long enough, now it is time to do some work.
@@ -297,11 +323,13 @@
                     mNextIdlePendingDelay = DEFAULT_MAX_IDLE_PENDING_TIMEOUT;
                 }
                 mState = STATE_IDLE_PENDING;
+                mLocalPowerManager.setDeviceIdleMode(false);
                 try {
                     mNetworkPolicyManager.setDeviceIdleMode(false);
                     mBatteryStats.noteDeviceIdleMode(false, false, false);
                 } catch (RemoteException e) {
                 }
+                getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
                 break;
         }
     }
@@ -313,13 +341,18 @@
         // state to wait again for no motion.  Note that we only monitor for significant
         // motion after moving out of the inactive state, so no need to worry about that.
         if (mState != STATE_ACTIVE) {
-            mState = STATE_INACTIVE;
+            mLocalPowerManager.setDeviceIdleMode(false);
             try {
                 mNetworkPolicyManager.setDeviceIdleMode(false);
                 mBatteryStats.noteDeviceIdleMode(false, false, true);
             } catch (RemoteException e) {
             }
-            stepIdleStateLocked();
+            if (mState == STATE_IDLE) {
+                getContext().sendBroadcastAsUser(mIdleIntent, UserHandle.ALL);
+            }
+            mState = STATE_ACTIVE;
+            mInactiveTimeout = DEFAULT_MOTION_INACTIVE_TIMEOUT;
+            becomeInactiveIfAppropriateLocked();
         }
     }
 
@@ -399,26 +432,30 @@
             }
         }
 
-        pw.print("  mSigMotionSensor="); pw.println(mSigMotionSensor);
-        pw.print("  mCurDisplay="); pw.println(mCurDisplay);
-        pw.print("  mScreenOn="); pw.println(mScreenOn);
-        pw.print("  mCharging="); pw.println(mCharging);
-        pw.print("  mSigMotionActive="); pw.println(mSigMotionActive);
-        pw.print("  mState="); pw.println(stateToString(mState));
-        if (mNextAlarmTime != 0) {
-            pw.print("  mNextAlarmTime=");
-            TimeUtils.formatDuration(mNextAlarmTime, SystemClock.elapsedRealtime(), pw);
+        synchronized (this) {
+            pw.print("  mSigMotionSensor="); pw.println(mSigMotionSensor);
+            pw.print("  mCurDisplay="); pw.println(mCurDisplay);
+            pw.print("  mScreenOn="); pw.println(mScreenOn);
+            pw.print("  mCharging="); pw.println(mCharging);
+            pw.print("  mSigMotionActive="); pw.println(mSigMotionActive);
+            pw.print("  mState="); pw.println(stateToString(mState));
+            pw.print("  mInactiveTimeout="); TimeUtils.formatDuration(mInactiveTimeout, pw);
             pw.println();
-        }
-        if (mNextIdlePendingDelay != 0) {
-            pw.print("  mNextIdlePendingDelay=");
-            TimeUtils.formatDuration(mNextIdlePendingDelay, pw);
-            pw.println();
-        }
-        if (mNextIdleDelay != 0) {
-            pw.print("  mNextIdleDelay=");
-            TimeUtils.formatDuration(mNextIdleDelay, pw);
-            pw.println();
+            if (mNextAlarmTime != 0) {
+                pw.print("  mNextAlarmTime=");
+                TimeUtils.formatDuration(mNextAlarmTime, SystemClock.elapsedRealtime(), pw);
+                pw.println();
+            }
+            if (mNextIdlePendingDelay != 0) {
+                pw.print("  mNextIdlePendingDelay=");
+                TimeUtils.formatDuration(mNextIdlePendingDelay, pw);
+                pw.println();
+            }
+            if (mNextIdleDelay != 0) {
+                pw.print("  mNextIdleDelay=");
+                TimeUtils.formatDuration(mNextIdleDelay, pw);
+                pw.println();
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 1349926..c48367e 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -78,6 +78,7 @@
     private static final int MSG_USER_ACTIVITY = 1;
     private static final int MSG_BROADCAST = 2;
     private static final int MSG_WIRELESS_CHARGING_STARTED = 3;
+    private static final int MSG_SCREEN_BRIGHTNESS_BOOST_CHANGED = 4;
 
     private final Object mLock = new Object();
 
@@ -92,6 +93,7 @@
     private final NotifierHandler mHandler;
     private final Intent mScreenOnIntent;
     private final Intent mScreenOffIntent;
+    private final Intent mScreenBrightnessBoostIntent;
 
     // The current interactive state.
     private int mActualInteractiveState;
@@ -128,6 +130,10 @@
         mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
         mScreenOffIntent.addFlags(
                 Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
+        mScreenBrightnessBoostIntent =
+                new Intent(PowerManager.ACTION_SCREEN_BRIGHTNESS_BOOST_CHANGED);
+        mScreenBrightnessBoostIntent.addFlags(
+                Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
 
         // Initialize interactive state for battery stats.
         try {
@@ -349,6 +355,19 @@
     }
 
     /**
+     * Called when screen brightness boost begins or ends.
+     */
+    public void onScreenBrightnessBoostChanged() {
+        if (DEBUG) {
+            Slog.d(TAG, "onScreenBrightnessBoostChanged");
+        }
+
+        mSuspendBlocker.acquire();
+        Message msg = mHandler.obtainMessage(MSG_SCREEN_BRIGHTNESS_BOOST_CHANGED);
+        msg.setAsynchronous(true);
+        mHandler.sendMessage(msg);
+    }
+    /**
      * Called when there has been user activity.
      */
     public void onUserActivity(int event, int uid) {
@@ -457,6 +476,22 @@
         }
     }
 
+    private void sendBrightnessBoostChangedBroadcast() {
+        if (DEBUG) {
+            Slog.d(TAG, "Sending brightness boost changed broadcast.");
+        }
+
+        mContext.sendOrderedBroadcastAsUser(mScreenBrightnessBoostIntent, UserHandle.ALL, null,
+                mScreeBrightnessBoostChangedDone, mHandler, 0, null, null);
+    }
+
+    private final BroadcastReceiver mScreeBrightnessBoostChangedDone = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mSuspendBlocker.release();
+        }
+    };
+
     private void sendWakeUpBroadcast() {
         if (DEBUG) {
             Slog.d(TAG, "Sending wake up broadcast.");
@@ -539,6 +574,9 @@
                 case MSG_WIRELESS_CHARGING_STARTED:
                     playWirelessChargingStartedSound();
                     break;
+                case MSG_SCREEN_BRIGHTNESS_BOOST_CHANGED:
+                    sendBrightnessBoostChangedBroadcast();
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 9e373b7..6c8959c 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -420,6 +420,9 @@
     // True if the battery level is currently considered low.
     private boolean mBatteryLevelLow;
 
+    // True if we are currently in device idle mode.
+    private boolean mDeviceIdleMode;
+
     // True if theater mode is enabled
     private boolean mTheaterModeEnabled;
 
@@ -1900,6 +1903,7 @@
                     }
                 }
                 mScreenBrightnessBoostInProgress = false;
+                mNotifier.onScreenBrightnessBoostChanged();
                 userActivityNoUpdateLocked(now,
                         PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
             }
@@ -2178,6 +2182,12 @@
         }
     }
 
+    private boolean isDeviceIdleModeInternal() {
+        synchronized (mLock) {
+            return mDeviceIdleMode;
+        }
+    }
+
     private void handleBatteryStateChangedLocked() {
         mDirty |= DIRTY_BATTERY_STATE;
         updatePowerStateLocked();
@@ -2275,7 +2285,10 @@
 
             Slog.i(TAG, "Brightness boost activated (uid " + uid +")...");
             mLastScreenBrightnessBoostTime = eventTime;
-            mScreenBrightnessBoostInProgress = true;
+            if (!mScreenBrightnessBoostInProgress) {
+                mScreenBrightnessBoostInProgress = true;
+                mNotifier.onScreenBrightnessBoostChanged();
+            }
             mDirty |= DIRTY_SCREEN_BRIGHTNESS_BOOST;
 
             userActivityNoUpdateLocked(eventTime,
@@ -2284,6 +2297,12 @@
         }
     }
 
+    private boolean isScreenBrightnessBoostedInternal() {
+        synchronized (mLock) {
+            return mScreenBrightnessBoostInProgress;
+        }
+    }
+
     /**
      * Called when a screen brightness boost timeout has occurred.
      *
@@ -3050,6 +3069,16 @@
             }
         }
 
+        @Override // Binder call
+        public boolean isDeviceIdleMode() {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                return isDeviceIdleModeInternal();
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
         /**
          * Reboots the device.
          *
@@ -3218,6 +3247,16 @@
         }
 
         @Override // Binder call
+        public boolean isScreenBrightnessBoosted() {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                return isScreenBrightnessBoostedInternal();
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override // Binder call
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
                     != PackageManager.PERMISSION_GRANTED) {
@@ -3295,5 +3334,12 @@
                 mLowPowerModeListeners.add(listener);
             }
         }
+
+        @Override
+        public void setDeviceIdleMode(boolean enabled) {
+            synchronized (mLock) {
+                mDeviceIdleMode = enabled;
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 0109313..dec195d 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -272,13 +272,14 @@
         alarmFilter.addDataScheme(mAlarmIntent.getScheme());
         final String pathUri = mAlarmIntent.toUri(Intent.URI_INTENT_SCHEME);
         alarmFilter.addDataPath(pathUri, PatternMatcher.PATTERN_LITERAL);
-        mContext.registerReceiver(mBroadcastReceiver, alarmFilter, PERMISSION, null);
 
         // Schedules a restart for when connecting times out. If the connection succeeds,
         // the restart is canceled in mCallback's onConnected.
         scheduleRestart();
         mBound = context.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE, user);
-        if (!mBound) {
+        if (mBound) {
+            mContext.registerReceiver(mBroadcastReceiver, alarmFilter, PERMISSION, null);
+        } else {
             Log.e(TAG, "Can't bind to TrustAgent " + mName.flattenToShortString());
         }
     }
@@ -398,7 +399,6 @@
     }
 
     public void destroy() {
-        mContext.unregisterReceiver(mBroadcastReceiver);
         mHandler.removeMessages(MSG_RESTART_TIMEOUT);
 
         if (!mBound) {
@@ -408,6 +408,7 @@
         mTrustManagerService.mArchive.logAgentStopped(mUserId, mName);
         mContext.unbindService(mConnection);
         mBound = false;
+        mContext.unregisterReceiver(mBroadcastReceiver);
         mTrustAgentService = null;
         mSetTrustAgentFeaturesToken = null;
         mHandler.sendEmptyMessage(MSG_REVOKE_TRUST);
diff --git a/services/core/java/com/android/server/updates/TZInfoInstallReceiver.java b/services/core/java/com/android/server/updates/TZInfoInstallReceiver.java
deleted file mode 100644
index 2fe68f8..0000000
--- a/services/core/java/com/android/server/updates/TZInfoInstallReceiver.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2013 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.updates;
-
-import android.util.Base64;
-
-import java.io.IOException;
-
-public class TZInfoInstallReceiver extends ConfigUpdateInstallReceiver {
-
-    public TZInfoInstallReceiver() {
-        super("/data/misc/zoneinfo/", "tzdata", "metadata/", "version");
-    }
-
-    @Override
-    protected void install(byte[] encodedContent, int version) throws IOException {
-        super.install(Base64.decode(encodedContent, Base64.DEFAULT), version);
-    }
-}
diff --git a/services/core/java/com/android/server/updates/TzDataInstallReceiver.java b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
new file mode 100644
index 0000000..b260e4e
--- /dev/null
+++ b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
@@ -0,0 +1,54 @@
+/*
+ * 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.server.updates;
+
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+import libcore.tzdata.update.TzDataBundleInstaller;
+
+/**
+ * An install receiver responsible for installing timezone data updates.
+ */
+public class TzDataInstallReceiver extends ConfigUpdateInstallReceiver {
+
+    private static final String TAG = "TZDataInstallReceiver";
+
+    private static final File TZ_DATA_DIR = new File("/data/misc/zoneinfo");
+    private static final String UPDATE_DIR_NAME = TZ_DATA_DIR.getPath() + "/updates/";
+    private static final String UPDATE_METADATA_DIR_NAME = "metadata/";
+    private static final String UPDATE_VERSION_FILE_NAME = "version";
+    private static final String UPDATE_CONTENT_FILE_NAME = "tzdata_bundle.zip";
+
+    private final TzDataBundleInstaller installer;
+
+    public TzDataInstallReceiver() {
+        super(UPDATE_DIR_NAME, UPDATE_CONTENT_FILE_NAME, UPDATE_METADATA_DIR_NAME,
+                UPDATE_VERSION_FILE_NAME);
+        installer = new TzDataBundleInstaller(TAG, TZ_DATA_DIR);
+    }
+
+    @Override
+    protected void install(byte[] content, int version) throws IOException {
+        boolean valid = installer.install(content);
+        Slog.i(TAG, "Timezone data install valid for this device: " + valid);
+        // Even if !valid, we call super.install(). Only in the event of an exception should we
+        // not. If we didn't do this we could attempt to install repeatedly.
+        super.install(content, version);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index a58f30d..55ec9fc 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -242,7 +242,7 @@
     }
 
     // This must be called while inside a transaction.
-    boolean stepAnimationLocked(long currentTime) {
+    boolean stepAnimationLocked(long currentTime, final int displayId) {
         if (mService.okToDisplay()) {
             // We will run animations as long as the display isn't frozen.
 
@@ -292,7 +292,7 @@
         }
 
         mAnimator.setAppLayoutChanges(this, WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
-                "AppWindowToken");
+                "AppWindowToken", displayId);
 
         clearAnimation();
         animating = false;
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index c09ea5c..e385be3 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -140,10 +140,9 @@
     }
 
     /**
-     * @param layer The new layer value.
-     * @param inTransaction Whether the call is made within a surface transaction.
+     * NOTE: Must be called with Surface transaction open.
      */
-    void adjustSurface(int layer, boolean inTransaction) {
+    private void adjustBounds() {
         final int dw, dh;
         final float xPos, yPos;
         if (!mStack.isFullscreen()) {
@@ -163,29 +162,24 @@
             yPos = -1 * dh / 6;
         }
 
-        try {
-            if (!inTransaction) {
-                SurfaceControl.openTransaction();
-            }
-            mDimSurface.setPosition(xPos, yPos);
-            mDimSurface.setSize(dw, dh);
-            mDimSurface.setLayer(layer);
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Failure setting size or layer", e);
-        } finally {
-            if (!inTransaction) {
-                SurfaceControl.closeTransaction();
-            }
-        }
+        mDimSurface.setPosition(xPos, yPos);
+        mDimSurface.setSize(dw, dh);
+
         mLastBounds.set(mBounds);
-        mLayer = layer;
     }
 
-    // Assumes that surface transactions are currently closed.
+    /** @param bounds The new bounds to set */
     void setBounds(Rect bounds) {
         mBounds.set(bounds);
         if (isDimming() && !mLastBounds.equals(bounds)) {
-            adjustSurface(mLayer, false);
+            try {
+                SurfaceControl.openTransaction();
+                adjustBounds();
+            } catch (RuntimeException e) {
+                Slog.w(TAG, "Failure setting size", e);
+            } finally {
+                SurfaceControl.closeTransaction();
+            }
         }
     }
 
@@ -224,9 +218,10 @@
             return;
         }
 
-        if (!mLastBounds.equals(mBounds) || mLayer != layer) {
-            adjustSurface(layer, true);
+        if (!mLastBounds.equals(mBounds)) {
+            adjustBounds();
         }
+        setLayer(layer);
 
         long curTime = SystemClock.uptimeMillis();
         final boolean animating = isAnimating();
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 6d09f55..b61a6f7 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -373,10 +373,8 @@
             mService.requestTraversalLocked();
         }
 
-        mAnimationBackgroundSurface.destroySurface();
-        mAnimationBackgroundSurface = null;
-        mDimLayer.destroySurface();
-        mDimLayer = null;
+        close();
+
         mDisplayContent = null;
     }
 
@@ -501,8 +499,14 @@
     }
 
     void close() {
-        mDimLayer.mDimSurface.destroy();
-        mAnimationBackgroundSurface.mDimSurface.destroy();
+        if (mAnimationBackgroundSurface != null) {
+            mAnimationBackgroundSurface.destroySurface();
+            mAnimationBackgroundSurface = null;
+        }
+        if (mDimLayer != null) {
+            mDimLayer.destroySurface();
+            mDimLayer = null;
+        }
     }
 
     public void dump(String prefix, PrintWriter pw) {
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 46fa38a..897b865 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -159,13 +159,13 @@
                     final AppWindowAnimator appAnimator = tokens.get(tokenNdx).mAppAnimator;
                     final boolean wasAnimating = appAnimator.animation != null
                             && appAnimator.animation != AppWindowAnimator.sDummyAnimation;
-                    if (appAnimator.stepAnimationLocked(mCurrentTime)) {
+                    if (appAnimator.stepAnimationLocked(mCurrentTime, displayId)) {
                         mAnimating = mAppWindowAnimating = true;
                     } else if (wasAnimating) {
                         // stopped animating, do one more pass through the layout
                         setAppLayoutChanges(appAnimator,
                                 WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
-                                "appToken " + appAnimator.mAppToken + " done");
+                                "appToken " + appAnimator.mAppToken + " done", displayId);
                         if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
                                 "updateWindowsApps...: done animating " + appAnimator.mAppToken);
                     }
@@ -178,12 +178,12 @@
                 final AppWindowAnimator appAnimator = exitingAppTokens.get(i).mAppAnimator;
                 final boolean wasAnimating = appAnimator.animation != null
                         && appAnimator.animation != AppWindowAnimator.sDummyAnimation;
-                if (appAnimator.stepAnimationLocked(mCurrentTime)) {
+                if (appAnimator.stepAnimationLocked(mCurrentTime, displayId)) {
                     mAnimating = mAppWindowAnimating = true;
                 } else if (wasAnimating) {
                     // stopped animating, do one more pass through the layout
                     setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
-                        "exiting appToken " + appAnimator.mAppToken + " done");
+                        "exiting appToken " + appAnimator.mAppToken + " done", displayId);
                     if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG,
                             "updateWindowsApps...: done animating exiting " + appAnimator.mAppToken);
                 }
@@ -575,11 +575,11 @@
                             // This will set mOrientationChangeComplete and cause a pass through layout.
                             setAppLayoutChanges(appAnimator,
                                     WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER,
-                                    "testTokenMayBeDrawnLocked: freezingScreen");
+                                    "testTokenMayBeDrawnLocked: freezingScreen", displayId);
                         } else {
                             setAppLayoutChanges(appAnimator,
                                     WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
-                                    "testTokenMayBeDrawnLocked");
+                                    "testTokenMayBeDrawnLocked", displayId);
 
                             // We can now show all of the drawn windows!
                             if (!mService.mOpeningApps.contains(wtoken)) {
@@ -792,28 +792,30 @@
         if (displayId < 0) {
             return 0;
         }
-        return mService.getDisplayContentLocked(displayId).pendingLayoutChanges;
+        final DisplayContent displayContent = mService.getDisplayContentLocked(displayId);
+        return (displayContent != null) ? displayContent.pendingLayoutChanges : 0;
     }
 
     void setPendingLayoutChanges(final int displayId, final int changes) {
-        if (displayId >= 0) {
-            mService.getDisplayContentLocked(displayId).pendingLayoutChanges |= changes;
+        if (displayId < 0) {
+            return;
+        }
+        final DisplayContent displayContent = mService.getDisplayContentLocked(displayId);
+        if (displayContent != null) {
+            displayContent.pendingLayoutChanges |= changes;
         }
     }
 
-    void setAppLayoutChanges(final AppWindowAnimator appAnimator, final int changes, String s) {
-        // Used to track which displays layout changes have been done.
-        SparseIntArray displays = new SparseIntArray(2);
+    void setAppLayoutChanges(final AppWindowAnimator appAnimator, final int changes, String reason,
+            final int displayId) {
         WindowList windows = appAnimator.mAppToken.allAppWindows;
         for (int i = windows.size() - 1; i >= 0; i--) {
-            final int displayId = windows.get(i).getDisplayId();
-            if (displayId >= 0 && displays.indexOfKey(displayId) < 0) {
+            if (displayId == windows.get(i).getDisplayId()) {
                 setPendingLayoutChanges(displayId, changes);
                 if (WindowManagerService.DEBUG_LAYOUT_REPEATS) {
-                    mService.debugLayoutRepeats(s, getPendingLayoutChanges(displayId));
+                    mService.debugLayoutRepeats(reason, getPendingLayoutChanges(displayId));
                 }
-                // Keep from processing this display again.
-                displays.put(displayId, changes);
+                break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4c80b07..6df3bf4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -178,7 +178,7 @@
     static final boolean DEBUG_ORIENTATION = false;
     static final boolean DEBUG_APP_ORIENTATION = false;
     static final boolean DEBUG_CONFIGURATION = false;
-    static final boolean DEBUG_APP_TRANSITIONS = false;
+    static final boolean DEBUG_APP_TRANSITIONS = true;
     static final boolean DEBUG_STARTING_WINDOW = false;
     static final boolean DEBUG_WALLPAPER = false;
     static final boolean DEBUG_WALLPAPER_LIGHT = false || DEBUG_WALLPAPER;
@@ -189,7 +189,7 @@
     static final boolean DEBUG_LAYOUT_REPEATS = true;
     static final boolean DEBUG_SURFACE_TRACE = false;
     static final boolean DEBUG_WINDOW_TRACE = false;
-    static final boolean DEBUG_TASK_MOVEMENT = false;
+    static final boolean DEBUG_TASK_MOVEMENT = true;
     static final boolean DEBUG_STACK = false;
     static final boolean DEBUG_DISPLAY = false;
     static final boolean DEBUG_POWER = false;
@@ -507,6 +507,7 @@
     boolean mClientFreezingScreen = false;
     int mAppsFreezingScreen = 0;
     int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+    int mLastKeyguardForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
 
     int mLayoutSeq = 0;
 
@@ -3055,7 +3056,7 @@
                 }
             }
 
-            if (DEBUG_LAYOUT) Slog.v(TAG, "Relayout " + win + ": viewVisibility=" + viewVisibility
+            if (true || DEBUG_LAYOUT) Slog.v(TAG, "Relayout " + win + ": viewVisibility=" + viewVisibility
                     + " req=" + requestedWidth + "x" + requestedHeight + " " + win.mAttrs);
 
             win.mEnforceSizeCompat =
@@ -3115,6 +3116,10 @@
                 }
                 winAnimator.mEnteringAnimation = true;
                 if (toBeDisplayed) {
+                    if ((win.mAttrs.softInputMode & SOFT_INPUT_MASK_ADJUST)
+                            == SOFT_INPUT_ADJUST_RESIZE) {
+                        win.mLayoutNeeded = true;
+                    }
                     if (win.isDrawnLw() && okToDisplay()) {
                         winAnimator.applyEnterAnimationLocked();
                     }
@@ -3700,41 +3705,68 @@
         }
     }
 
-    public int getOrientationFromWindowsLocked() {
-        if (mDisplayFrozen || mOpeningApps.size() > 0 || mClosingApps.size() > 0) {
-            // If the display is frozen, some activities may be in the middle
-            // of restarting, and thus have removed their old window.  If the
-            // window has the flag to hide the lock screen, then the lock screen
-            // can re-appear and inflict its own orientation on us.  Keep the
-            // orientation stable until this all settles down.
-            return mLastWindowForcedOrientation;
+    public int getOrientationLocked() {
+        if (mDisplayFrozen) {
+            if (mLastWindowForcedOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
+                if (DEBUG_ORIENTATION) Slog.v(TAG, "Display is frozen, return "
+                        + mLastWindowForcedOrientation);
+                // If the display is frozen, some activities may be in the middle
+                // of restarting, and thus have removed their old window.  If the
+                // window has the flag to hide the lock screen, then the lock screen
+                // can re-appear and inflict its own orientation on us.  Keep the
+                // orientation stable until this all settles down.
+                return mLastWindowForcedOrientation;
+            }
+        } else {
+            // TODO(multidisplay): Change to the correct display.
+            final WindowList windows = getDefaultWindowListLocked();
+            for (int pos = windows.size() - 1; pos >= 0; --pos) {
+                WindowState win = windows.get(pos);
+                if (win.mAppToken != null) {
+                    // We hit an application window. so the orientation will be determined by the
+                    // app window. No point in continuing further.
+                    break;
+                }
+                if (!win.isVisibleLw() || !win.mPolicyVisibilityAfterAnim) {
+                    continue;
+                }
+                int req = win.mAttrs.screenOrientation;
+                if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) ||
+                        (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){
+                    continue;
+                }
+
+                if (DEBUG_ORIENTATION) Slog.v(TAG, win + " forcing orientation to " + req);
+                if (mPolicy.isKeyguardHostWindow(win.mAttrs)) {
+                    mLastKeyguardForcedOrientation = req;
+                }
+                return (mLastWindowForcedOrientation = req);
+            }
+            mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+            if (mPolicy.isKeyguardLocked()) {
+                // The screen is locked and no top system window is requesting an orientation.
+                // Return either the orientation of the show-when-locked app (if there is any) or
+                // the orientation of the keyguard. No point in searching from the rest of apps.
+                WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
+                AppWindowToken appShowWhenLocked = winShowWhenLocked == null ?
+                        null : winShowWhenLocked.mAppToken;
+                if (appShowWhenLocked != null) {
+                    int req = appShowWhenLocked.requestedOrientation;
+                    if (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+                        req = mLastKeyguardForcedOrientation;
+                    }
+                    if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + appShowWhenLocked
+                            + " -- show when locked, return " + req);
+                    return req;
+                }
+                if (DEBUG_ORIENTATION) Slog.v(TAG,
+                        "No one is requesting an orientation when the screen is locked");
+                return mLastKeyguardForcedOrientation;
+            }
         }
 
-        // TODO(multidisplay): Change to the correct display.
-        final WindowList windows = getDefaultWindowListLocked();
-        for (int pos = windows.size() - 1; pos >= 0; --pos) {
-            WindowState win = windows.get(pos);
-            if (win.mAppToken != null) {
-                // We hit an application window. so the orientation will be determined by the
-                // app window. No point in continuing further.
-                return (mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
-            }
-            if (!win.isVisibleLw() || !win.mPolicyVisibilityAfterAnim) {
-                continue;
-            }
-            int req = win.mAttrs.screenOrientation;
-            if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) ||
-                    (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){
-                continue;
-            }
-
-            if (DEBUG_ORIENTATION) Slog.v(TAG, win + " forcing orientation to " + req);
-            return (mLastWindowForcedOrientation = req);
-        }
-        return (mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
-    }
-
-    public int getOrientationFromAppTokensLocked() {
+        // Top system windows are not requesting an orientation. Start searching from apps.
         int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
         boolean findingBehind = false;
         boolean lastFullscreen = false;
@@ -3804,8 +3836,11 @@
                 findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND);
             }
         }
-        if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation");
-        return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+        if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation, return "
+                + mForcedAppOrientation);
+        // The next app has not been requested to be visible, so we keep the current orientation
+        // to prevent freezing/unfreezing the display too early.
+        return mForcedAppOrientation;
     }
 
     @Override
@@ -3887,11 +3922,7 @@
     boolean updateOrientationFromAppTokensLocked(boolean inTransaction) {
         long ident = Binder.clearCallingIdentity();
         try {
-            int req = getOrientationFromWindowsLocked();
-            if (req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
-                req = getOrientationFromAppTokensLocked();
-            }
-
+            int req = getOrientationLocked();
             if (req != mForcedAppOrientation) {
                 mForcedAppOrientation = req;
                 //send a message to Policy indicating orientation change to take
@@ -4140,8 +4171,8 @@
         }
 
         synchronized(mWindowMap) {
-            if (DEBUG_APP_TRANSITIONS) Slog.w(TAG, "Execute app transition: " + mAppTransition,
-                    new RuntimeException("here").fillInStackTrace());
+            if (DEBUG_APP_TRANSITIONS) Slog.w(TAG, "Execute app transition: " + mAppTransition
+                    + " Callers=" + Debug.getCallers(5));
             if (mAppTransition.isTransitionSet()) {
                 mAppTransition.setReady();
                 final long origId = Binder.clearCallingIdentity();
@@ -4577,22 +4608,26 @@
             if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG, "setAppVisibility(" +
                     token + ", visible=" + visible + "): " + mAppTransition +
                     " hidden=" + wtoken.hidden + " hiddenRequested=" +
-                    wtoken.hiddenRequested, HIDE_STACK_CRAWLS ?
-                            null : new RuntimeException("here").fillInStackTrace());
+                    wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6));
+
+            mOpeningApps.remove(wtoken);
+            mClosingApps.remove(wtoken);
+            wtoken.waitingToShow = wtoken.waitingToHide = false;
+            wtoken.hiddenRequested = !visible;
+
+            mOpeningApps.remove(wtoken);
+            mClosingApps.remove(wtoken);
+            wtoken.waitingToShow = wtoken.waitingToHide = false;
+            wtoken.hiddenRequested = !visible;
 
             // If we are preparing an app transition, then delay changing
             // the visibility of this token until we execute that transition.
             if (okToDisplay() && mAppTransition.isTransitionSet()) {
-                wtoken.hiddenRequested = !visible;
-
                 if (!wtoken.startingDisplayed) {
                     if (DEBUG_APP_TRANSITIONS) Slog.v(
                             TAG, "Setting dummy animation on: " + wtoken);
                     wtoken.mAppAnimator.setDummyAnimation();
                 }
-                mOpeningApps.remove(wtoken);
-                mClosingApps.remove(wtoken);
-                wtoken.waitingToShow = wtoken.waitingToHide = false;
                 wtoken.inPendingTransaction = true;
                 if (visible) {
                     mOpeningApps.add(wtoken);
@@ -9029,39 +9064,41 @@
                     goodToGo = false;
                 }
             }
-            if (goodToGo && isWallpaperVisible(mWallpaperTarget)) {
-                boolean wallpaperGoodToGo = true;
-                for (int curTokenIndex = mWallpaperTokens.size() - 1;
-                        curTokenIndex >= 0 && wallpaperGoodToGo; curTokenIndex--) {
-                    WindowToken token = mWallpaperTokens.get(curTokenIndex);
-                    for (int curWallpaperIndex = token.windows.size() - 1; curWallpaperIndex >= 0;
-                            curWallpaperIndex--) {
-                        WindowState wallpaper = token.windows.get(curWallpaperIndex);
-                        if (wallpaper.mWallpaperVisible && !wallpaper.isDrawnLw()) {
-                            // We've told this wallpaper to be visible, but it is not drawn yet
-                            wallpaperGoodToGo = false;
-                            if (mWallpaperDrawState != WALLPAPER_DRAW_TIMEOUT) {
-                                // wait for this wallpaper until it is drawn or timeout
-                                goodToGo = false;
-                            }
-                            if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) {
-                                mWallpaperDrawState = WALLPAPER_DRAW_PENDING;
-                                mH.removeMessages(H.WALLPAPER_DRAW_PENDING_TIMEOUT);
-                                mH.sendEmptyMessageDelayed(H.WALLPAPER_DRAW_PENDING_TIMEOUT,
-                                        WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
-                            }
-                            if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
-                                    "Wallpaper should be visible but has not been drawn yet. " +
-                                    "mWallpaperDrawState=" + mWallpaperDrawState);
-                            break;
-                        }
-                    }
-                }
-                if (wallpaperGoodToGo) {
-                    mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
-                    mH.removeMessages(H.WALLPAPER_DRAW_PENDING_TIMEOUT);
-                }
-            }
+// Stuck in a state with mWallpaperDrawState == WALLPAPER_DRAW_PENDING without a timeout. Leave
+// commented out until that is understood.
+//            if (goodToGo && isWallpaperVisible(mWallpaperTarget)) {
+//                boolean wallpaperGoodToGo = true;
+//                for (int curTokenIndex = mWallpaperTokens.size() - 1;
+//                        curTokenIndex >= 0 && wallpaperGoodToGo; curTokenIndex--) {
+//                    WindowToken token = mWallpaperTokens.get(curTokenIndex);
+//                    for (int curWallpaperIndex = token.windows.size() - 1; curWallpaperIndex >= 0;
+//                            curWallpaperIndex--) {
+//                        WindowState wallpaper = token.windows.get(curWallpaperIndex);
+//                        if (wallpaper.mWallpaperVisible && !wallpaper.isDrawnLw()) {
+//                            // We've told this wallpaper to be visible, but it is not drawn yet
+//                            wallpaperGoodToGo = false;
+//                            if (mWallpaperDrawState != WALLPAPER_DRAW_TIMEOUT) {
+//                                // wait for this wallpaper until it is drawn or timeout
+//                                goodToGo = false;
+//                            }
+//                            if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) {
+//                                mWallpaperDrawState = WALLPAPER_DRAW_PENDING;
+//                                mH.removeMessages(H.WALLPAPER_DRAW_PENDING_TIMEOUT);
+//                                mH.sendEmptyMessageDelayed(H.WALLPAPER_DRAW_PENDING_TIMEOUT,
+//                                        WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION);
+//                            }
+//                            if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG,
+//                                    "Wallpaper should be visible but has not been drawn yet. " +
+//                                    "mWallpaperDrawState=" + mWallpaperDrawState);
+//                            break;
+//                        }
+//                    }
+//                }
+//                if (wallpaperGoodToGo) {
+//                    mWallpaperDrawState = WALLPAPER_DRAW_NORMAL;
+//                    mH.removeMessages(H.WALLPAPER_DRAW_PENDING_TIMEOUT);
+//                }
+//            }
         }
         if (goodToGo) {
             if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO");
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 7b74e91..6448de2 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -10,6 +10,7 @@
     $(LOCAL_REL_DIR)/com_android_server_AssetAtlasService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \
     $(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_fingerprint_FingerprintService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiCecController.cpp \
     $(LOCAL_REL_DIR)/com_android_server_input_InputApplicationHandle.cpp \
     $(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \
diff --git a/services/core/jni/com_android_server_UsbMidiDevice.cpp b/services/core/jni/com_android_server_UsbMidiDevice.cpp
index 94853b8..cb70144 100644
--- a/services/core/jni/com_android_server_UsbMidiDevice.cpp
+++ b/services/core/jni/com_android_server_UsbMidiDevice.cpp
@@ -94,9 +94,20 @@
     return fds;
 }
 
+static void
+android_server_UsbMidiDevice_close(JNIEnv *env, jobject /* thiz */, jobjectArray fds)
+{
+    int count = env->GetArrayLength(fds);
+    for (int i = 0; i < count; i++) {
+        jobject fd = env->GetObjectArrayElement(fds, i);
+        close(jniGetFDFromFileDescriptor(env, fd));
+    }
+}
+
 static JNINativeMethod method_table[] = {
     { "nativeGetSubdeviceCount", "(II)I", (void*)android_server_UsbMidiDevice_get_subdevice_count },
     { "nativeOpen", "(III)[Ljava/io/FileDescriptor;", (void*)android_server_UsbMidiDevice_open },
+    { "nativeClose", "([Ljava/io/FileDescriptor;)V", (void*)android_server_UsbMidiDevice_close },
 };
 
 int register_android_server_UsbMidiDevice(JNIEnv *env)
diff --git a/core/jni/android_server_FingerprintManager.cpp b/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp
similarity index 82%
rename from core/jni/android_server_FingerprintManager.cpp
rename to services/core/jni/com_android_server_fingerprint_FingerprintService.cpp
index 853425c..5d59234 100644
--- a/core/jni/android_server_FingerprintManager.cpp
+++ b/services/core/jni/com_android_server_fingerprint_FingerprintService.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "Fingerprint-JNI"
 
 #include "JNIHelp.h"
+#include <inttypes.h>
 
 #include <android_runtime/AndroidRuntime.h>
 #include <android_runtime/Log.h>
@@ -47,14 +48,15 @@
 
 class CallbackHandler : public MessageHandler {
     int type;
-    int arg1, arg2;
+    int arg1, arg2, arg3;
 public:
-    CallbackHandler(int type, int arg1, int arg2) : type(type), arg1(arg1), arg2(arg2) { }
+    CallbackHandler(int type, int arg1, int arg2, int arg3)
+        : type(type), arg1(arg1), arg2(arg2), arg3(arg3) { }
 
     virtual void handleMessage(const Message& message) {
         //ALOG(LOG_VERBOSE, LOG_TAG, "hal_notify(msg=%d, arg1=%d, arg2=%d)\n", msg.type, arg1, arg2);
         JNIEnv* env = AndroidRuntime::getJNIEnv();
-        env->CallVoidMethod(gCallback, gFingerprintServiceClassInfo.notify, type, arg1, arg2);
+        env->CallVoidMethod(gCallback, gFingerprintServiceClassInfo.notify, type, arg1, arg2, arg3);
     }
 };
 
@@ -62,6 +64,7 @@
 static void hal_notify_callback(fingerprint_msg_t msg) {
     uint32_t arg1 = 0;
     uint32_t arg2 = 0;
+    uint32_t arg3 = 0;
     switch (msg.type) {
         case FINGERPRINT_ERROR:
             arg1 = msg.data.error;
@@ -71,13 +74,16 @@
             break;
         case FINGERPRINT_PROCESSED:
             arg1 = msg.data.processed.finger.fid;
+            arg2 = msg.data.processed.finger.gid;
             break;
         case FINGERPRINT_TEMPLATE_ENROLLING:
             arg1 = msg.data.enroll.finger.fid;
-            arg2 = msg.data.enroll.samples_remaining;
+            arg2 = msg.data.enroll.finger.gid;
+            arg3 = msg.data.enroll.samples_remaining;
             break;
         case FINGERPRINT_TEMPLATE_REMOVED:
             arg1 = msg.data.removed.finger.fid;
+            arg2 = msg.data.removed.finger.gid;
             break;
         default:
             ALOGE("fingerprint: invalid msg: %d", msg.type);
@@ -86,7 +92,7 @@
     // This call potentially comes in on a thread not owned by us. Hand it off to our
     // looper so it runs on our thread when calling back to FingerprintService.
     // CallbackHandler object is reference-counted, so no cleanup necessary.
-    gLooper->sendMessage(new CallbackHandler(msg.type, arg1, arg2), Message());
+    gLooper->sendMessage(new CallbackHandler(msg.type, arg1, arg2, arg3), Message());
 }
 
 static void nativeInit(JNIEnv *env, jobject clazz, jobject mQueue, jobject callbackObj) {
@@ -95,9 +101,15 @@
     gLooper = android_os_MessageQueue_getMessageQueue(env, mQueue)->getLooper();
 }
 
-static jint nativeEnroll(JNIEnv* env, jobject clazz, jint timeout) {
-    ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnroll()\n");
-    int ret = gContext.device->enroll(gContext.device, 0, timeout);
+static jint nativeEnroll(JNIEnv* env, jobject clazz, jint timeout, jint groupId) {
+    ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnroll(gid=%d, timeout=%d)\n", groupId, timeout);
+    int ret = gContext.device->enroll(gContext.device, groupId, timeout);
+    return reinterpret_cast<jint>(ret);
+}
+
+static jint nativeAuthenticate(JNIEnv* env, jobject clazz, jlong sessionId, jint groupId) {
+    ALOG(LOG_VERBOSE, LOG_TAG, "nativeAuthenticate(sid=%" PRId64 ", gid=%d)\n", sessionId, groupId);
+    int ret = gContext.device->authenticate(gContext.device, sessionId, groupId);
     return reinterpret_cast<jint>(ret);
 }
 
@@ -107,11 +119,11 @@
     return reinterpret_cast<jint>(ret);
 }
 
-static jint nativeRemove(JNIEnv* env, jobject clazz, jint fingerprintId) {
-    ALOG(LOG_VERBOSE, LOG_TAG, "nativeRemove(%d)\n", fingerprintId);
+static jint nativeRemove(JNIEnv* env, jobject clazz, jint fingerId, jint groupId) {
+    ALOG(LOG_VERBOSE, LOG_TAG, "nativeRemove(fid=%d, gid=%d)\n", fingerId, groupId);
     fingerprint_finger_id_t finger;
-    finger.gid = 0;
-    finger.fid = fingerprintId;
+    finger.fid = fingerId;
+    finger.gid = groupId;
     int ret = gContext.device->remove(gContext.device, finger);
     return reinterpret_cast<jint>(ret);
 }
@@ -172,9 +184,10 @@
 
 // TODO: clean up void methods
 static const JNINativeMethod g_methods[] = {
-    { "nativeEnroll", "(I)I", (void*)nativeEnroll },
+    { "nativeAuthenticate", "(JI)I", (void*)nativeAuthenticate },
+    { "nativeEnroll", "(II)I", (void*)nativeEnroll },
     { "nativeEnrollCancel", "()I", (void*)nativeEnrollCancel },
-    { "nativeRemove", "(I)I", (void*)nativeRemove },
+    { "nativeRemove", "(II)I", (void*)nativeRemove },
     { "nativeOpenHal", "()I", (void*)nativeOpenHal },
     { "nativeCloseHal", "()I", (void*)nativeCloseHal },
     { "nativeInit","(Landroid/os/MessageQueue;"
@@ -185,7 +198,7 @@
     jclass clazz = FindClassOrDie(env, FINGERPRINT_SERVICE);
     gFingerprintServiceClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
     gFingerprintServiceClassInfo.notify =
-            GetMethodIDOrDie(env, gFingerprintServiceClassInfo.clazz,"notify", "(III)V");
+            GetMethodIDOrDie(env, gFingerprintServiceClassInfo.clazz,"notify", "(IIII)V");
     int result = RegisterMethodsOrDie(env, FINGERPRINT_SERVICE, g_methods, NELEM(g_methods));
     ALOG(LOG_VERBOSE, LOG_TAG, "FingerprintManager JNI ready.\n");
     return result;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d07cd98..0c58aef 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -78,6 +78,8 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.ContactsContract.QuickContact;
+import android.provider.ContactsInternal;
 import android.provider.Settings;
 import android.security.Credentials;
 import android.security.IKeyChainAliasCallback;
@@ -146,6 +148,8 @@
 
     private static final String LOG_TAG = "DevicePolicyManagerService";
 
+    private static final boolean VERBOSE_LOG = false; // DO NOT SUBMIT WITH TRUE
+
     private static final String DEVICE_POLICIES_XML = "device_policies.xml";
 
     private static final String LOCK_TASK_COMPONENTS_XML = "lock-task-component";
@@ -1140,70 +1144,85 @@
     ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy)
             throws SecurityException {
         final int callingUid = Binder.getCallingUid();
-        final int userHandle = UserHandle.getUserId(callingUid);
-        final DevicePolicyData policy = getUserData(userHandle);
 
-        List<ActiveAdmin> candidates = new ArrayList<ActiveAdmin>();
+        ActiveAdmin result = getActiveAdminWithPolicyForUidLocked(who, reqPolicy, callingUid);
+        if (result != null) {
+            return result;
+        }
 
-        // Build a list of admins for this uid matching the given ComponentName
+        if (who != null) {
+            final int userId = UserHandle.getUserId(callingUid);
+            final DevicePolicyData policy = getUserData(userId);
+            ActiveAdmin admin = policy.mAdminMap.get(who);
+            if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
+                throw new SecurityException("Admin " + admin.info.getComponent()
+                         + " does not own the device");
+            }
+            if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
+                throw new SecurityException("Admin " + admin.info.getComponent()
+                        + " does not own the profile");
+            }
+            throw new SecurityException("Admin " + admin.info.getComponent()
+                    + " did not specify uses-policy for: "
+                    + admin.info.getTagForPolicy(reqPolicy));
+        } else {
+            throw new SecurityException("No active admin owned by uid "
+                    + Binder.getCallingUid() + " for policy #" + reqPolicy);
+        }
+    }
+
+    private ActiveAdmin getActiveAdminWithPolicyForUidLocked(ComponentName who, int reqPolicy,
+            int uid) {
+        // Try to find an admin which can use reqPolicy
+        final int userId = UserHandle.getUserId(uid);
+        final DevicePolicyData policy = getUserData(userId);
         if (who != null) {
             ActiveAdmin admin = policy.mAdminMap.get(who);
             if (admin == null) {
                 throw new SecurityException("No active admin " + who);
             }
-            if (admin.getUid() != callingUid) {
+            if (admin.getUid() != uid) {
                 throw new SecurityException("Admin " + who + " is not owned by uid "
                         + Binder.getCallingUid());
             }
-            candidates.add(admin);
+            if (isActiveAdminWithPolicyForUserLocked(admin, reqPolicy, userId)) {
+                return admin;
+            }
         } else {
             for (ActiveAdmin admin : policy.mAdminList) {
-                if (admin.getUid() == callingUid) {
-                    candidates.add(admin);
-                }
-            }
-        }
-
-        // Try to find an admin which can use reqPolicy
-        for (ActiveAdmin admin : candidates) {
-            boolean ownsDevice = isDeviceOwner(admin.info.getPackageName());
-            boolean ownsProfile = (getProfileOwner(userHandle) != null
-                    && getProfileOwner(userHandle).getPackageName()
-                        .equals(admin.info.getPackageName()));
-            boolean ownsInitialization = isDeviceInitializer(admin.info.getPackageName())
-                    && !hasUserSetupCompleted(userHandle);
-
-            if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
-                if (ownsDevice || (userHandle == UserHandle.USER_OWNER && ownsInitialization)) {
-                    return admin;
-                }
-            } else if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
-                if (ownsDevice || ownsProfile || ownsInitialization) {
-                    return admin;
-                }
-            } else {
-                if (admin.info.usesPolicy(reqPolicy)) {
+                if (admin.getUid() == uid && isActiveAdminWithPolicyForUserLocked(admin, reqPolicy,
+                        userId)) {
                     return admin;
                 }
             }
         }
 
-        if (who != null) {
-            if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
-                throw new SecurityException("Admin " + candidates.get(0).info.getComponent()
-                         + " does not own the device");
+        return null;
+    }
+
+    private boolean isActiveAdminWithPolicyForUserLocked(ActiveAdmin admin, int reqPolicy,
+            int userId) {
+        boolean ownsDevice = isDeviceOwner(admin.info.getPackageName());
+        boolean ownsProfile = (getProfileOwner(userId) != null
+                && getProfileOwner(userId).getPackageName()
+                    .equals(admin.info.getPackageName()));
+        boolean ownsInitialization = isDeviceInitializer(admin.info.getPackageName())
+                && !hasUserSetupCompleted(userId);
+
+        if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
+            if (ownsDevice || (userId == UserHandle.USER_OWNER && ownsInitialization)) {
+                return true;
             }
-            if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
-                throw new SecurityException("Admin " + candidates.get(0).info.getComponent()
-                        + " does not own the profile");
+        } else if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
+            if (ownsDevice || ownsProfile || ownsInitialization) {
+                return true;
             }
-            throw new SecurityException("Admin " + candidates.get(0).info.getComponent()
-                    + " did not specify uses-policy for: "
-                    + candidates.get(0).info.getTagForPolicy(reqPolicy));
         } else {
-            throw new SecurityException("No active admin owned by uid "
-                    + Binder.getCallingUid() + " for policy #" + reqPolicy);
+            if (admin.info.usesPolicy(reqPolicy)) {
+                return true;
+            }
         }
+        return false;
     }
 
     void sendAdminCommandLocked(ActiveAdmin admin, String action) {
@@ -3159,7 +3178,9 @@
                     }
                     PersistentDataBlockManager manager = (PersistentDataBlockManager)
                             mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
-                    manager.wipe();
+                    if (manager != null) {
+                        manager.wipe();
+                    }
                 }
                 boolean wipeExtRequested = (flags & WIPE_EXTERNAL_STORAGE) != 0;
                 wipeDeviceOrUserLocked(wipeExtRequested, userHandle,
@@ -5418,6 +5439,59 @@
         }
     }
 
+    @Override
+    public void startManagedQuickContact(String actualLookupKey, long actualContactId,
+            Intent originalIntent) {
+        final Intent intent = QuickContact.rebuildManagedQuickContactsIntent(
+                actualLookupKey, actualContactId, originalIntent);
+        final int callingUserId = UserHandle.getCallingUserId();
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                final int managedUserId = getManagedUserId(callingUserId);
+                if (managedUserId < 0) {
+                    return;
+                }
+                if (getCrossProfileCallerIdDisabledForUser(managedUserId)) {
+                    if (VERBOSE_LOG) {
+                        Log.v(LOG_TAG,
+                                "Cross-profile contacts access disabled for user " + managedUserId);
+                    }
+                    return;
+                }
+                ContactsInternal.startQuickContactWithErrorToastForUser(
+                        mContext, intent, new UserHandle(managedUserId));
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    /**
+     * @return the user ID of the managed user that is linked to the current user, if any.
+     * Otherwise -1.
+     */
+    public int getManagedUserId(int callingUserId) {
+        if (VERBOSE_LOG) {
+            Log.v(LOG_TAG, "getManagedUserId: callingUserId=" + callingUserId);
+        }
+
+        for (UserInfo ui : mUserManager.getProfiles(callingUserId)) {
+            if (ui.id == callingUserId || !ui.isManagedProfile()) {
+                continue; // Caller user self, or not a managed profile.  Skip.
+            }
+            if (VERBOSE_LOG) {
+                Log.v(LOG_TAG, "Managed user=" + ui.id);
+            }
+            return ui.id;
+        }
+        if (VERBOSE_LOG) {
+            Log.v(LOG_TAG, "Managed user not found.");
+        }
+        return -1;
+    }
+
     /**
      * Sets which packages may enter lock task mode.
      *
@@ -5702,6 +5776,14 @@
             }
         }
 
+        @Override
+        public boolean isActiveAdminWithPolicy(int uid, int reqPolicy) {
+            final int userId = UserHandle.getUserId(uid);
+            synchronized(DevicePolicyManagerService.this) {
+                return getActiveAdminWithPolicyForUidLocked(null, reqPolicy, uid) != null;
+            }
+        }
+
         private void notifyCrossProfileProvidersChanged(int userId, List<String> packages) {
             final List<OnCrossProfileWidgetProvidersChangeListener> listeners;
             synchronized (DevicePolicyManagerService.this) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index bcd8d42..53da75b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -53,6 +53,7 @@
 import com.android.server.accounts.AccountManagerService;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.audio.AudioService;
+import com.android.server.camera.CameraService;
 import com.android.server.clipboard.ClipboardService;
 import com.android.server.content.ContentService;
 import com.android.server.devicepolicy.DevicePolicyManagerService;
@@ -66,7 +67,6 @@
 import com.android.server.media.MediaRouterService;
 import com.android.server.media.MediaSessionService;
 import com.android.server.media.projection.MediaProjectionManagerService;
-import com.android.server.MidiService;
 import com.android.server.net.NetworkPolicyManagerService;
 import com.android.server.net.NetworkStatsService;
 import com.android.server.notification.NotificationManagerService;
@@ -76,6 +76,7 @@
 import com.android.server.pm.LauncherAppsService;
 import com.android.server.pm.PackageManagerService;
 import com.android.server.pm.UserManagerService;
+import com.android.server.power.DeviceIdleController;
 import com.android.server.power.PowerManagerService;
 import com.android.server.power.ShutdownThread;
 import com.android.server.restrictions.RestrictionsManagerService;
@@ -124,6 +125,8 @@
             "com.android.server.print.PrintManagerService";
     private static final String USB_SERVICE_CLASS =
             "com.android.server.usb.UsbService$Lifecycle";
+    private static final String MIDI_SERVICE_CLASS =
+            "com.android.server.midi.MidiService$Lifecycle";
     private static final String WIFI_SERVICE_CLASS =
             "com.android.server.wifi.WifiService";
     private static final String WIFI_P2P_SERVICE_CLASS =
@@ -406,12 +409,10 @@
         AudioService audioService = null;
         MmsServiceBroker mmsService = null;
         EntropyMixer entropyMixer = null;
-        MidiService midiService = null;
+        CameraService cameraService = null;
 
         boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false);
-        boolean disableMedia = SystemProperties.getBoolean("config.disable_media", false);
         boolean disableBluetooth = SystemProperties.getBoolean("config.disable_bluetooth", false);
-        boolean disableTelephony = SystemProperties.getBoolean("config.disable_telephony", false);
         boolean disableLocation = SystemProperties.getBoolean("config.disable_location", false);
         boolean disableSystemUI = SystemProperties.getBoolean("config.disable_systemui", false);
         boolean disableNonCoreServices = SystemProperties.getBoolean("config.disable_noncore", false);
@@ -437,6 +438,9 @@
 
             mContentResolver = context.getContentResolver();
 
+            Slog.i(TAG, "Camera Service");
+            mSystemServiceManager.startService(CameraService.class);
+
             // The AccountManager must come before the ContentService
             try {
                 // TODO: seems like this should be disable-able, but req'd by ContentService
@@ -520,7 +524,6 @@
         LockSettingsService lockSettings = null;
         AssetAtlasService atlas = null;
         MediaRouterService mediaRouter = null;
-        MidiService midi = null;
 
         // Bring up services needed for UI.
         if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
@@ -564,6 +567,10 @@
             }
         }
 
+        // We start this here so that we update our configuration to set watch or television
+        // as appropriate.
+        mSystemServiceManager.startService(UiModeManagerService.class);
+
         try {
             mPackageManagerService.performBootDexOpt();
         } catch (Throwable e) {
@@ -788,32 +795,33 @@
                 }
             }
 
-            if (!disableMedia && !"0".equals(SystemProperties.get("system_init.startaudioservice"))) {
-                try {
-                    Slog.i(TAG, "Audio Service");
-                    audioService = new AudioService(context);
-                    ServiceManager.addService(Context.AUDIO_SERVICE, audioService);
-                } catch (Throwable e) {
-                    reportWtf("starting Audio Service", e);
-                }
+            try {
+                Slog.i(TAG, "Audio Service");
+                audioService = new AudioService(context);
+                ServiceManager.addService(Context.AUDIO_SERVICE, audioService);
+            } catch (Throwable e) {
+                reportWtf("starting Audio Service", e);
             }
 
             if (!disableNonCoreServices) {
                 mSystemServiceManager.startService(DockObserver.class);
             }
 
-            if (!disableMedia) {
-                try {
-                    Slog.i(TAG, "Wired Accessory Manager");
-                    // Listen for wired headset changes
-                    inputManager.setWiredAccessoryCallbacks(
-                            new WiredAccessoryManager(context, inputManager));
-                } catch (Throwable e) {
-                    reportWtf("starting WiredAccessoryManager", e);
-                }
+            try {
+                Slog.i(TAG, "Wired Accessory Manager");
+                // Listen for wired headset changes
+                inputManager.setWiredAccessoryCallbacks(
+                        new WiredAccessoryManager(context, inputManager));
+            } catch (Throwable e) {
+                reportWtf("starting WiredAccessoryManager", e);
             }
 
             if (!disableNonCoreServices) {
+                if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MIDI)) {
+                    // Start MIDI Manager service
+                    mSystemServiceManager.startService(MIDI_SERVICE_CLASS);
+                }
+
                 if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)
                         || mPackageManager.hasSystemFeature(
                                 PackageManager.FEATURE_USB_ACCESSORY)) {
@@ -831,20 +839,8 @@
                 }
             }
 
-            if (!disableNonCoreServices) {
-                try {
-                    Slog.i(TAG, "MIDI Service");
-                    ServiceManager.addService(Context.MIDI_SERVICE,
-                            new MidiService(context));
-                } catch (Throwable e) {
-                    reportWtf("starting MIDI Service", e);
-                }
-            }
-
             mSystemServiceManager.startService(TwilightService.class);
 
-            mSystemServiceManager.startService(UiModeManagerService.class);
-
             mSystemServiceManager.startService(JobSchedulerService.class);
 
             if (!disableNonCoreServices) {
@@ -889,14 +885,12 @@
                 }
             }
 
-            if (!disableMedia) {
-                try {
-                    Slog.i(TAG, "CommonTimeManagementService");
-                    commonTimeMgmtService = new CommonTimeManagementService(context);
-                    ServiceManager.addService("commontime_management", commonTimeMgmtService);
-                } catch (Throwable e) {
-                    reportWtf("starting CommonTimeManagementService service", e);
-                }
+            try {
+                Slog.i(TAG, "CommonTimeManagementService");
+                commonTimeMgmtService = new CommonTimeManagementService(context);
+                ServiceManager.addService("commontime_management", commonTimeMgmtService);
+            } catch (Throwable e) {
+                reportWtf("starting CommonTimeManagementService service", e);
             }
 
             if (!disableNetwork) {
@@ -923,6 +917,11 @@
                 }
             }
 
+            if (!disableNonCoreServices) {
+                ServiceManager.addService(GraphicsStatsService.GRAPHICS_STATS_SERVICE,
+                        new GraphicsStatsService(context));
+            }
+
             if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
                 mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
             }
diff --git a/services/midi/Android.mk b/services/midi/Android.mk
new file mode 100644
index 0000000..faac01c
--- /dev/null
+++ b/services/midi/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := services.midi
+
+LOCAL_SRC_FILES += \
+      $(call all-java-files-under,java)
+
+LOCAL_JAVA_LIBRARIES := services.core
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/core/java/com/android/server/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
similarity index 94%
rename from services/core/java/com/android/server/MidiService.java
rename to services/midi/java/com/android/server/midi/MidiService.java
index 3418930..1d2180e 100644
--- a/services/core/java/com/android/server/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package com.android.server.midi;
 
 import android.content.Context;
 import android.content.Intent;
@@ -40,6 +40,7 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.XmlUtils;
+import com.android.server.SystemService;
 
 import org.xmlpull.v1.XmlPullParser;
 
@@ -47,9 +48,25 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 
 public class MidiService extends IMidiManager.Stub {
+
+    public static class Lifecycle extends SystemService {
+        private MidiService mMidiService;
+
+        public Lifecycle(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onStart() {
+            mMidiService = new MidiService(getContext());
+            publishBinderService(Context.MIDI_SERVICE, mMidiService);
+        }
+    }
+
     private static final String TAG = "MidiService";
 
     private final Context mContext;
@@ -269,7 +286,9 @@
 
         public void binderDied() {
             synchronized (mDevicesByInfo) {
-                removeDeviceLocked(this);
+                if (mDevicesByInfo.remove(mDeviceInfo) != null) {
+                    removeDeviceLocked(this);
+                }
             }
         }
 
@@ -368,6 +387,7 @@
         synchronized (mDevicesByInfo) {
             Device device = mDevicesByServer.get(server.asBinder());
             if (device != null) {
+                mDevicesByInfo.remove(device.getDeviceInfo());
                 removeDeviceLocked(device);
             }
         }
@@ -454,16 +474,14 @@
 
     // synchronize on mDevicesByInfo
     private void removeDeviceLocked(Device device) {
-        if (mDevicesByInfo.remove(device.getDeviceInfo()) != null) {
-            IMidiDeviceServer server = device.getDeviceServer();
-            if (server != null) {
-                mDevicesByServer.remove(server);
-            }
+        IMidiDeviceServer server = device.getDeviceServer();
+        if (server != null) {
+            mDevicesByServer.remove(server);
+        }
 
-            synchronized (mClients) {
-                for (Client c : mClients.values()) {
-                    c.deviceRemoved(device);
-                }
+        synchronized (mClients) {
+            for (Client c : mClients.values()) {
+                c.deviceRemoved(device);
             }
         }
     }
@@ -616,8 +634,11 @@
 
     private void removePackageDeviceServers(String packageName) {
         synchronized (mDevicesByInfo) {
-            for (Device device : mDevicesByInfo.values()) {
+            Iterator<Device> iterator = mDevicesByInfo.values().iterator();
+            while (iterator.hasNext()) {
+                Device device = iterator.next();
                 if (packageName.equals(device.getPackageName())) {
+                    iterator.remove();
                     removeDeviceLocked(device);
                 }
             }
@@ -634,15 +655,19 @@
 
         pw.println("Devices:");
         pw.increaseIndent();
-        for (Device device : mDevicesByInfo.values()) {
-            pw.println(device.toString());
+        synchronized (mDevicesByInfo) {
+            for (Device device : mDevicesByInfo.values()) {
+                pw.println(device.toString());
+            }
         }
         pw.decreaseIndent();
 
         pw.println("Clients:");
         pw.increaseIndent();
-        for (Client client : mClients.values()) {
-            pw.println(client.toString());
+        synchronized (mClients) {
+            for (Client client : mClients.values()) {
+                pw.println(client.toString());
+            }
         }
         pw.decreaseIndent();
     }
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index a232a6e..d41629d 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -611,10 +611,22 @@
     /**
      * Reads a string of specified length from the buffer.
      */
-    private static String readAsciiString(ByteBuffer buf, int byteCount) {
+    private static String readAsciiString(ByteBuffer buf, int byteCount, boolean nullOk) {
         byte[] bytes = new byte[byteCount];
         buf.get(bytes);
-        return new String(bytes, 0, bytes.length, StandardCharsets.US_ASCII);
+        int length = bytes.length;
+        if (!nullOk) {
+            // Stop at the first null byte. This is because some DHCP options (e.g., the domain
+            // name) are passed to netd via FrameworkListener, which refuses arguments containing
+            // null bytes. We don't do this by default because vendorInfo is an opaque string which
+            // could in theory contain null bytes.
+            for (length = 0; length < bytes.length; length++) {
+                if (bytes[length] == 0) {
+                    break;
+                }
+            }
+        }
+        return new String(bytes, 0, length, StandardCharsets.US_ASCII);
     }
 
     /**
@@ -797,7 +809,7 @@
                             break;
                         case DHCP_HOST_NAME:
                             expectedLen = optionLen;
-                            hostName = readAsciiString(packet, optionLen);
+                            hostName = readAsciiString(packet, optionLen, false);
                             break;
                         case DHCP_MTU:
                             expectedLen = 2;
@@ -805,7 +817,7 @@
                             break;
                         case DHCP_DOMAIN_NAME:
                             expectedLen = optionLen;
-                            domainName = readAsciiString(packet, optionLen);
+                            domainName = readAsciiString(packet, optionLen, false);
                             break;
                         case DHCP_BROADCAST_ADDRESS:
                             bcAddr = readIpAddress(packet);
@@ -834,7 +846,7 @@
                             break;
                         case DHCP_MESSAGE:
                             expectedLen = optionLen;
-                            message = readAsciiString(packet, optionLen);
+                            message = readAsciiString(packet, optionLen, false);
                             break;
                         case DHCP_MAX_MESSAGE_SIZE:
                             expectedLen = 2;
@@ -850,7 +862,7 @@
                             break;
                         case DHCP_VENDOR_CLASS_ID:
                             expectedLen = optionLen;
-                            vendorId = readAsciiString(packet, optionLen);
+                            vendorId = readAsciiString(packet, optionLen, true);
                             break;
                         case DHCP_CLIENT_IDENTIFIER: { // Client identifier
                             byte[] id = new byte[optionLen];
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index f25fc62..33979b1 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -10,6 +10,7 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     services.core \
     services.devicepolicy \
+    services.net \
     easymocklib \
     guava \
     mockito-target
diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
new file mode 100644
index 0000000..2658937
--- /dev/null
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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 android.net.dhcp;
+
+import android.net.NetworkUtils;
+import android.system.OsConstants;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+import java.net.Inet4Address;
+import java.nio.ByteBuffer;
+
+import static android.net.dhcp.DhcpPacket.*;
+
+
+public class DhcpPacketTest extends TestCase {
+
+    private static Inet4Address SERVER_ADDR =
+            (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.1");
+    private static Inet4Address CLIENT_ADDR =
+            (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.234");
+    private static byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
+
+    class TestDhcpPacket extends DhcpPacket {
+        private byte mType;
+        // TODO: Make this a map of option numbers to bytes instead.
+        private byte[] mDomainBytes, mVendorInfoBytes;
+
+        public TestDhcpPacket(byte type, byte[] domainBytes, byte[] vendorInfoBytes) {
+            super(0xdeadbeef, INADDR_ANY, CLIENT_ADDR, INADDR_ANY, INADDR_ANY, CLIENT_MAC, true);
+            mType = type;
+            mDomainBytes = domainBytes;
+            mVendorInfoBytes = vendorInfoBytes;
+        }
+
+        public ByteBuffer buildPacket(int encap, short unusedDestUdp, short unusedSrcUdp) {
+            ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
+            fillInPacket(encap, CLIENT_ADDR, SERVER_ADDR,
+                         DHCP_CLIENT, DHCP_SERVER, result, DHCP_BOOTREPLY, false);
+            return result;
+        }
+
+        public void finishPacket(ByteBuffer buffer) {
+            addTlv(buffer, DHCP_MESSAGE_TYPE, mType);
+            if (mDomainBytes != null) {
+                addTlv(buffer, DHCP_DOMAIN_NAME, mDomainBytes);
+            }
+            if (mVendorInfoBytes != null) {
+                addTlv(buffer, DHCP_VENDOR_CLASS_ID, mVendorInfoBytes);
+            }
+            addTlvEnd(buffer);
+        }
+
+        // Convenience method.
+        public ByteBuffer build() {
+            // ENCAP_BOOTP packets don't contain ports, so just pass in 0.
+            ByteBuffer pkt = buildPacket(ENCAP_BOOTP, (short) 0, (short) 0);
+            pkt.flip();
+            return pkt;
+        }
+    }
+
+    private void assertDomainAndVendorInfoParses(
+            String expectedDomain, byte[] domainBytes,
+            String expectedVendorInfo, byte[] vendorInfoBytes) {
+        ByteBuffer packet = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER,
+                domainBytes, vendorInfoBytes).build();
+        DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
+        assertEquals(expectedDomain, offerPacket.mDomainName);
+        assertEquals(expectedVendorInfo, offerPacket.mVendorId);
+    }
+
+    @SmallTest
+    public void testDomainName() throws Exception {
+        byte[] nullByte = new byte[] { 0x00 };
+        byte[] twoNullBytes = new byte[] { 0x00, 0x00 };
+        byte[] nonNullDomain = new byte[] {
+            (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l'
+        };
+        byte[] trailingNullDomain = new byte[] {
+            (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l', 0x00
+        };
+        byte[] embeddedNullsDomain = new byte[] {
+            (byte) 'g', (byte) 'o', (byte) 'o', 0x00, 0x00, (byte) 'g', (byte) 'l'
+        };
+        byte[] metered = "ANDROID_METERED".getBytes("US-ASCII");
+
+        byte[] meteredEmbeddedNull = metered.clone();
+        meteredEmbeddedNull[7] = (char) 0;
+
+        byte[] meteredTrailingNull = metered.clone();
+        meteredTrailingNull[meteredTrailingNull.length - 1] = (char) 0;
+
+        assertDomainAndVendorInfoParses("", nullByte, "\u0000", nullByte);
+        assertDomainAndVendorInfoParses("", twoNullBytes, "\u0000\u0000", twoNullBytes);
+        assertDomainAndVendorInfoParses("goo.gl", nonNullDomain, "ANDROID_METERED", metered);
+        assertDomainAndVendorInfoParses("goo", embeddedNullsDomain,
+                                        "ANDROID\u0000METERED", meteredEmbeddedNull);
+        assertDomainAndVendorInfoParses("goo.gl", trailingNullDomain,
+                                        "ANDROID_METERE\u0000", meteredTrailingNull);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index b631331..a3f3a5d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -32,7 +32,6 @@
 import java.io.IOException;
 
 public class PackageManagerSettingsTests extends AndroidTestCase {
-
     private static final String PACKAGE_NAME_2 = "com.google.app2";
     private static final String PACKAGE_NAME_3 = "com.android.app3";
     private static final String PACKAGE_NAME_1 = "com.google.app1";
@@ -56,7 +55,7 @@
         writeFile(new File(getContext().getFilesDir(), "system/packages.xml"),
                 ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
                 + "<packages>"
-                + "<last-platform-version internal=\"15\" external=\"0\" />"
+                + "<last-platform-version internal=\"15\" external=\"0\" fingerprint=\"foo\" />"
                 + "<permission-trees>"
                 + "<item name=\"com.google.android.permtree\" package=\"com.google.android.permpackage\" />"
                 + "</permission-trees>"
@@ -110,28 +109,32 @@
                 .getBytes());
     }
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
+    private void deleteSystemFolder() {
+        File systemFolder = new File(getContext().getFilesDir(), "system");
+        deleteFolder(systemFolder);
+    }
+
+    private static void deleteFolder(File folder) {
+        File[] files = folder.listFiles();
+        if (files != null) {
+            for (File file : files) {
+                deleteFolder(file);
+            }
+        }
+        folder.delete();
     }
 
     private void writeOldFiles() {
+        deleteSystemFolder();
         writePackagesXml();
         writeStoppedPackagesXml();
         writePackagesList();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-    }
-
     public void testSettingsReadOld() {
-        // Debug.waitForDebugger();
-
         // Write the package files and make sure they're parsed properly the first time
         writeOldFiles();
-        Settings settings = new Settings(getContext(), getContext().getFilesDir());
+        Settings settings = new Settings(getContext().getFilesDir(), new Object());
         assertEquals(true, settings.readLPw(null, null, 0, false));
         assertNotNull(settings.peekPackageLPr(PACKAGE_NAME_3));
         assertNotNull(settings.peekPackageLPr(PACKAGE_NAME_1));
@@ -149,11 +152,12 @@
     public void testNewPackageRestrictionsFile() {
         // Write the package files and make sure they're parsed properly the first time
         writeOldFiles();
-        Settings settings = new Settings(getContext(), getContext().getFilesDir());
+        Settings settings = new Settings(getContext().getFilesDir(), new Object());
         assertEquals(true, settings.readLPw(null, null, 0, false));
+        settings.writeLPr();
 
         // Create Settings again to make it read from the new files
-        settings = new Settings(getContext(), getContext().getFilesDir());
+        settings = new Settings(getContext().getFilesDir(), new Object());
         assertEquals(true, settings.readLPw(null, null, 0, false));
 
         PackageSetting ps = settings.peekPackageLPr(PACKAGE_NAME_2);
@@ -164,7 +168,7 @@
     public void testEnableDisable() {
         // Write the package files and make sure they're parsed properly the first time
         writeOldFiles();
-        Settings settings = new Settings(getContext(), getContext().getFilesDir());
+        Settings settings = new Settings(getContext().getFilesDir(), new Object());
         assertEquals(true, settings.readLPw(null, null, 0, false));
 
         // Enable/Disable a package
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
new file mode 100644
index 0000000..eb7eb15
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.server.pm;
+
+import android.os.Bundle;
+import android.os.FileUtils;
+import android.os.Parcelable;
+import android.test.AndroidTestCase;
+import android.util.AtomicFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+
+public class UserManagerServiceTest extends AndroidTestCase {
+    private static String[] STRING_ARRAY = new String[] {"<tag", "<![CDATA["};
+    private File restrictionsFile;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        restrictionsFile = new File(mContext.getCacheDir(), "restrictions.xml");
+        restrictionsFile.delete();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        restrictionsFile.delete();
+        super.tearDown();
+    }
+
+    public void testWriteReadApplicationRestrictions() throws IOException {
+        AtomicFile atomicFile = new AtomicFile(restrictionsFile);
+        Bundle bundle = createBundle();
+        UserManagerService.writeApplicationRestrictionsLocked(bundle, atomicFile);
+        assertTrue(atomicFile.getBaseFile().exists());
+        String s = FileUtils.readTextFile(restrictionsFile, 10000, "");
+        System.out.println("restrictionsFile: " + s);
+        bundle = UserManagerService.readApplicationRestrictionsLocked(atomicFile);
+        System.out.println("readApplicationRestrictionsLocked bundle: " + bundle);
+        assertBundle(bundle);
+    }
+
+    private Bundle createBundle() {
+        Bundle result = new Bundle();
+        // Tests for 6 allowed types: Integer, Boolean, String, String[], Bundle and Parcelable[]
+        result.putBoolean("boolean_0", false);
+        result.putBoolean("boolean_1", true);
+        result.putInt("integer", 100);
+        result.putString("empty", "");
+        result.putString("string", "text");
+        result.putStringArray("string[]", STRING_ARRAY);
+
+        Bundle bundle = new Bundle();
+        bundle.putString("bundle_string", "bundle_string");
+        bundle.putInt("bundle_int", 1);
+        result.putBundle("bundle", bundle);
+
+        Bundle[] bundleArray = new Bundle[2];
+        bundleArray[0] = new Bundle();
+        bundleArray[0].putString("bundle_array_string", "bundle_array_string");
+        bundleArray[0].putBundle("bundle_array_bundle", bundle);
+        bundleArray[1] = new Bundle();
+        bundleArray[1].putString("bundle_array_string2", "bundle_array_string2");
+        result.putParcelableArray("bundle_array", bundleArray);
+        return result;
+    }
+
+    private void assertBundle(Bundle bundle) {
+        assertFalse(bundle.getBoolean("boolean_0"));
+        assertTrue(bundle.getBoolean("boolean_1"));
+        assertEquals(100, bundle.getInt("integer"));
+        assertEquals("", bundle.getString("empty"));
+        assertEquals("text", bundle.getString("string"));
+        assertEquals(Arrays.asList(STRING_ARRAY), Arrays.asList(bundle.getStringArray("string[]")));
+        Parcelable[] bundle_array = bundle.getParcelableArray("bundle_array");
+        assertEquals(2, bundle_array.length);
+        Bundle bundle1 = (Bundle) bundle_array[0];
+        assertEquals("bundle_array_string", bundle1.getString("bundle_array_string"));
+        assertNotNull(bundle1.getBundle("bundle_array_bundle"));
+        Bundle bundle2 = (Bundle) bundle_array[1];
+        assertEquals("bundle_array_string2", bundle2.getString("bundle_array_string2"));
+        Bundle childBundle = bundle.getBundle("bundle");
+        assertEquals("bundle_string", childBundle.getString("bundle_string"));
+        assertEquals(1, childBundle.getInt("bundle_int"));
+    }
+
+}
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 23e1970..2728af1 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -17,6 +17,7 @@
 package com.android.server.usb;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.hardware.usb.UsbConstants;
 import android.hardware.usb.UsbDevice;
@@ -55,6 +56,7 @@
 
     private final Context mContext;
     private IAudioService mAudioService;
+    private final boolean mHasMidiFeature;
 
     private final AlsaCardsParser mCardsParser = new AlsaCardsParser();
     private final AlsaDevicesParser mDevicesParser = new AlsaDevicesParser();
@@ -126,6 +128,7 @@
 
     /* package */ UsbAlsaManager(Context context) {
         mContext = context;
+        mHasMidiFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MIDI);
 
         // initial scan
         mCardsParser.scan();
@@ -389,7 +392,7 @@
             // mDevicesParser.scan()
 
             boolean hasMidi = mDevicesParser.hasMIDIDevices(addedCard);
-            if (hasMidi) {
+            if (hasMidi && mHasMidiFeature) {
                 int device = mDevicesParser.getDefaultDeviceNum(addedCard);
                 AlsaDevice alsaDevice = waitForAlsaDevice(addedCard, device, AlsaDevice.TYPE_MIDI);
                 if (alsaDevice != null) {
@@ -459,7 +462,11 @@
     }
 
    /* package */ void setPeripheralMidiState(boolean enabled, int card, int device) {
-        if (enabled) {
+        if (!mHasMidiFeature) {
+            return;
+        }
+
+        if (enabled && mPeripheralMidiDevice == null) {
             Bundle properties = new Bundle();
             Resources r = mContext.getResources();
             properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, r.getString(
@@ -469,7 +476,7 @@
             properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, card);
             properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, device);
             mPeripheralMidiDevice = UsbMidiDevice.create(mContext, properties, card, device);
-        } else if (mPeripheralMidiDevice != null) {
+        } else if (!enabled && mPeripheralMidiDevice != null) {
             IoUtils.closeQuietly(mPeripheralMidiDevice);
             mPeripheralMidiDevice = null;
         }
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 41cf2ef..6adb8be 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -126,6 +126,8 @@
     private boolean mAdbEnabled;
     private boolean mAudioSourceEnabled;
     private boolean mMidiEnabled;
+    private int mMidiCard;
+    private int mMidiDevice;
     private Map<String, List<Pair<String, String>>> mOemModeMap;
     private String[] mAccessoryStrings;
     private UsbDebuggingManager mDebuggingManager;
@@ -363,18 +365,6 @@
                 updateState(state);
                 mAdbEnabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB);
 
-                // Upgrade step for previous versions that used persist.service.adb.enable
-                String value = SystemProperties.get("persist.service.adb.enable", "");
-                if (value.length() > 0) {
-                    char enable = value.charAt(0);
-                    if (enable == '1') {
-                        setAdbEnabled(true);
-                    } else if (enable == '0') {
-                        setAdbEnabled(false);
-                    }
-                    SystemProperties.set("persist.service.adb.enable", "");
-                }
-
                 // register observer to listen for settings changes
                 mContentResolver.registerContentObserver(
                         Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
@@ -623,26 +613,24 @@
         private void updateMidiFunction() {
             boolean enabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MIDI);
             if (enabled != mMidiEnabled) {
-                int card = -1;
-                int device = -1;
-
                 if (enabled) {
                     Scanner scanner = null;
                     try {
                         scanner = new Scanner(new File(MIDI_ALSA_PATH));
-                        card = scanner.nextInt();
-                        device = scanner.nextInt();
+                        mMidiCard = scanner.nextInt();
+                        mMidiDevice = scanner.nextInt();
                     } catch (FileNotFoundException e) {
                         Slog.e(TAG, "could not open MIDI PCM file", e);
+                        enabled = false;
                     } finally {
                         if (scanner != null) {
                             scanner.close();
                         }
                     }
                 }
-                mUsbAlsaManager.setPeripheralMidiState(enabled, card, device);
                 mMidiEnabled = enabled;
             }
+            mUsbAlsaManager.setPeripheralMidiState(mMidiEnabled && mConfigured, mMidiCard, mMidiDevice);
         }
 
         @Override
diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
index f23bb93..6ece888 100644
--- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.media.midi.MidiDeviceInfo;
 import android.media.midi.MidiDeviceServer;
-import android.media.midi.MidiDispatcher;
 import android.media.midi.MidiManager;
 import android.media.midi.MidiReceiver;
 import android.media.midi.MidiSender;
@@ -30,6 +29,9 @@
 import android.system.StructPollfd;
 import android.util.Log;
 
+import com.android.internal.midi.MidiEventScheduler;
+import com.android.internal.midi.MidiEventScheduler.MidiEvent;
+
 import libcore.io.IoUtils;
 
 import java.io.Closeable;
@@ -43,10 +45,12 @@
 
     private MidiDeviceServer mServer;
 
-    private final MidiReceiver[] mInputPortReceivers;
+    private final MidiEventScheduler mEventScheduler;
 
     private static final int BUFFER_SIZE = 512;
 
+    private final FileDescriptor[] mFileDescriptors;
+
     // for polling multiple FileDescriptors for MIDI events
     private final StructPollfd[] mPollFDs;
     // streams for reading from ALSA driver
@@ -69,7 +73,7 @@
             return null;
         }
 
-        UsbMidiDevice midiDevice = new UsbMidiDevice(fileDescriptors, fileDescriptors);
+        UsbMidiDevice midiDevice = new UsbMidiDevice(fileDescriptors);
         if (!midiDevice.register(context, properties)) {
             IoUtils.closeQuietly(midiDevice);
             Log.e(TAG, "createDeviceServer failed");
@@ -78,14 +82,15 @@
         return midiDevice;
     }
 
-    private UsbMidiDevice(FileDescriptor[] inputFiles, FileDescriptor[] outputFiles) {
-        int inputCount = inputFiles.length;
-        int outputCount = outputFiles.length;
+    private UsbMidiDevice(FileDescriptor[] fileDescriptors) {
+        mFileDescriptors = fileDescriptors;
+        int inputCount = fileDescriptors.length;
+        int outputCount = fileDescriptors.length;
 
         mPollFDs = new StructPollfd[inputCount];
         mInputStreams = new FileInputStream[inputCount];
         for (int i = 0; i < inputCount; i++) {
-            FileDescriptor fd = inputFiles[i];
+            FileDescriptor fd = fileDescriptors[i];
             StructPollfd pollfd = new StructPollfd();
             pollfd.fd = fd;
             pollfd.events = (short)OsConstants.POLLIN;
@@ -95,21 +100,9 @@
 
         mOutputStreams = new FileOutputStream[outputCount];
         for (int i = 0; i < outputCount; i++) {
-            mOutputStreams[i] = new FileOutputStream(outputFiles[i]);
+            mOutputStreams[i] = new FileOutputStream(fileDescriptors[i]);
         }
-
-        mInputPortReceivers = new MidiReceiver[inputCount];
-        for (int port = 0; port < inputCount; port++) {
-            final int portF = port;
-            mInputPortReceivers[port] = new MidiReceiver() {
-                @Override
-                public void onReceive(byte[] data, int offset, int count, long timestamp)
-                        throws IOException {
-                    // FIXME - timestamps are ignored, future posting not supported yet.
-                    mOutputStreams[portF].write(data, offset, count);
-                }
-            };
-        }
+        mEventScheduler = new MidiEventScheduler(inputCount);
     }
 
     private boolean register(Context context, Bundle properties) {
@@ -119,16 +112,22 @@
             return false;
         }
 
+        int inputCount = mInputStreams.length;
         int outputCount = mOutputStreams.length;
-        mServer = midiManager.createDeviceServer(mInputPortReceivers, outputCount,
+        MidiReceiver[] inputPortReceivers = new MidiReceiver[inputCount];
+        for (int port = 0; port < inputCount; port++) {
+            inputPortReceivers[port] = mEventScheduler.getReceiver(port);
+        }
+
+        mServer = midiManager.createDeviceServer(inputPortReceivers, outputCount,
                 null, null, properties, MidiDeviceInfo.TYPE_USB, null);
         if (mServer == null) {
             return false;
         }
         final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers();
 
-        // FIXME can we only run this when we have a dispatcher that has listeners?
-        new Thread() {
+        // Create input thread
+        new Thread("UsbMidiDevice input thread") {
             @Override
             public void run() {
                 byte[] buffer = new byte[BUFFER_SIZE];
@@ -158,6 +157,33 @@
                 } catch (ErrnoException e) {
                     Log.d(TAG, "reader thread exiting");
                 }
+                Log.d(TAG, "input thread exit");
+            }
+        }.start();
+
+        // Create output thread
+        new Thread("UsbMidiDevice output thread") {
+            @Override
+            public void run() {
+                while (true) {
+                    MidiEvent event;
+                    try {
+                        event = (MidiEvent)mEventScheduler.waitNextEvent();
+                    } catch (InterruptedException e) {
+                        // try again
+                        continue;
+                    }
+                    if (event == null) {
+                        break;
+                    }
+                    try {
+                        mOutputStreams[event.portNumber].write(event.data, 0, event.count);
+                    } catch (IOException e) {
+                        Log.e(TAG, "write failed for port " + event.portNumber);
+                    }
+                    mEventScheduler.addEventToPool(event);
+                }
+                Log.d(TAG, "output thread exit");
             }
         }.start();
 
@@ -166,6 +192,8 @@
 
     @Override
     public void close() throws IOException {
+        mEventScheduler.close();
+
         if (mServer != null) {
             mServer.close();
         }
@@ -176,8 +204,10 @@
         for (int i = 0; i < mOutputStreams.length; i++) {
             mOutputStreams[i].close();
         }
+        nativeClose(mFileDescriptors);
     }
 
     private static native int nativeGetSubdeviceCount(int card, int device);
     private static native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount);
+    private static native void nativeClose(FileDescriptor[] fileDescriptors);
 }
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 22b7bb1..6fa653d 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -223,7 +223,7 @@
 
         //**********************************************************************************************
         // Next CAPABILITY value: 0x00080000
-        //**********************************************************************************************
+        //******************************************************************************************
 
         private final Uri mHandle;
         private final int mHandlePresentation;
@@ -323,7 +323,7 @@
                 builder.append(" CAPABILITY_SHOW_CALLBACK_NUMBER");
             }
             if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
-                builder.append(" CAPABILITY_SPEED_UP_IMS_MT_AUDIO");
+                builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO");
             }
             builder.append("]");
             return builder.toString();
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index a335e47..082474b 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -171,7 +171,7 @@
      * Connection is using WIFI.
      * @hide
      */
-    public static final int CAPABILITY_WIFI = 0x000010000;
+    public static final int CAPABILITY_WIFI = 0x00010000;
 
     /**
      * Indicates that the current device callback number should be shown.
@@ -292,7 +292,7 @@
             builder.append(" CAPABILITY_SHOW_CALLBACK_NUMBER");
         }
         if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
-            builder.append(" CAPABILITY_SPEED_UP_IMS_MT_AUDIO");
+            builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO");
         }
         builder.append("]");
         return builder.toString();
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index b44fa6c..6bc6de63 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -258,11 +258,19 @@
      * <p>
      * Output: nothing.
      */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    @SdkConstant(SdkConstantType.SERVICE_ACTION)
     public static final String ACTION_RESPOND_VIA_MESSAGE =
             "android.intent.action.RESPOND_VIA_MESSAGE";
 
     /**
+     * The emergency dialer may choose to present activities with intent filters for this
+     * action as emergency assistance buttons that launch the activity when clicked.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_EMERGENCY_ASSISTANCE =
+            "android.telephony.action.EMERGENCY_ASSISTANCE";
+
+    /**
      * The lookup key used with the {@link #ACTION_PHONE_STATE_CHANGED} broadcast
      * for a String containing the new call state.
      *
@@ -3308,11 +3316,11 @@
      * @return the preferred network type, defined in RILConstants.java.
      * @hide
      */
-    public int getPreferredNetworkType() {
+    public int getPreferredNetworkType(int subId) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
-                return telephony.getPreferredNetworkType();
+                return telephony.getPreferredNetworkType(subId);
         } catch (RemoteException ex) {
             Rlog.e(TAG, "getPreferredNetworkType RemoteException", ex);
         } catch (NullPointerException ex) {
@@ -3322,6 +3330,27 @@
     }
 
     /**
+     * Sets the network selection mode to automatic.
+     * <p>
+     * Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
+     *
+     * @hide
+     */
+    public void setNetworkSelectionModeAutomatic(int subId) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                telephony.setNetworkSelectionModeAutomatic(subId);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "setNetworkSelectionModeAutomatic RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "setNetworkSelectionModeAutomatic NPE", ex);
+        }
+    }
+
+    /**
      * Set the preferred network type.
      * Used for device configuration by some CDMA operators.
      * <p>
@@ -3329,15 +3358,16 @@
      *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
      *
+     * @param subId the id of the subscription to set the preferred network type for.
      * @param networkType the preferred network type, defined in RILConstants.java.
      * @return true on success; false on any failure.
      * @hide
      */
-    public boolean setPreferredNetworkType(int networkType) {
+    public boolean setPreferredNetworkType(int subId, int networkType) {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null)
-                return telephony.setPreferredNetworkType(networkType);
+                return telephony.setPreferredNetworkType(subId, networkType);
         } catch (RemoteException ex) {
             Rlog.e(TAG, "setPreferredNetworkType RemoteException", ex);
         } catch (NullPointerException ex) {
@@ -3356,7 +3386,8 @@
      * @return true on success; false on any failure.
      */
     public boolean setPreferredNetworkTypeToGlobal() {
-        return setPreferredNetworkType(RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
+        return setPreferredNetworkType(getDefaultSubscription(),
+                RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA);
     }
 
     /**
@@ -4057,6 +4088,34 @@
    }
 
    /**
+    * Returns the Status of Volte
+    *@hide
+    */
+   public boolean isVolteEnabled() {
+       try {
+           return getITelephony().isVolteEnabled();
+       } catch (RemoteException ex) {
+           return false;
+       } catch (NullPointerException ex) {
+           return false;
+       }
+   }
+
+   /**
+    * Returns the Status of Wi-Fi Calling
+    *@hide
+    */
+   public boolean isWifiCallingEnabled() {
+       try {
+           return getITelephony().isWifiCallingEnabled();
+       } catch (RemoteException ex) {
+           return false;
+       } catch (NullPointerException ex) {
+           return false;
+       }
+   }
+
+   /**
     * Set TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC for the default phone.
     *
     * @hide
@@ -4412,4 +4471,22 @@
 
         return retval;
     }
+
+    /**
+     * Resets telephony manager settings back to factory defaults.
+     *
+     * @hide
+     */
+    public void factoryReset(int subId) {
+        if (SubscriptionManager.isUsableSubIdValue(subId)) {
+            // Enable data
+            setDataEnabled(subId, true);
+            // Set network selection mode to automatic
+            setNetworkSelectionModeAutomatic(subId);
+            // Set preferred mobile network type to the best available
+            setPreferredNetworkType(subId, RILConstants.PREFERRED_NETWORK_MODE);
+            // Turn off roaming
+            SubscriptionManager.from(mContext).setDataRoaming(0, subId);
+        }
+    }
 }
diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/com/android/ims/ImsCallProfile.java
index 8740e19..239c16a 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.java
+++ b/telephony/java/com/android/ims/ImsCallProfile.java
@@ -270,7 +270,7 @@
         return "{ serviceType=" + mServiceType +
                 ", callType=" + mCallType +
                 ", restrictCause=" + mRestrictCause +
-                ", callExtras=" + mCallExtras.toString() +
+                //", callExtras=" + mCallExtras.toString() +
                 ", mediaProfile=" + mMediaProfile.toString() + " }";
     }
 
diff --git a/telephony/java/com/android/ims/internal/IImsUt.aidl b/telephony/java/com/android/ims/internal/IImsUt.aidl
index 50a0169..c531ea5 100644
--- a/telephony/java/com/android/ims/internal/IImsUt.aidl
+++ b/telephony/java/com/android/ims/internal/IImsUt.aidl
@@ -79,12 +79,13 @@
     /**
      * Updates the configuration of the call forward.
      */
-    int updateCallForward(int action, int condition, String number, int timeSeconds);
+    int updateCallForward(int action, int condition, String number,
+            int serviceClass, int timeSeconds);
 
     /**
      * Updates the configuration of the call waiting.
      */
-    int updateCallWaiting(boolean enable);
+    int updateCallWaiting(boolean enable, int serviceClass);
 
     /**
      * Updates the configuration of the CLIR supplementary service.
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index f9e15f3..a24859b 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -653,9 +653,10 @@
      * Get the preferred network type.
      * Used for device configuration by some CDMA operators.
      *
+     * @param subId the id of the subscription to query.
      * @return the preferred network type, defined in RILConstants.java.
      */
-    int getPreferredNetworkType();
+    int getPreferredNetworkType(int subId);
 
     /**
      * Check TETHER_DUN_REQUIRED and TETHER_DUN_APN settings, net.tethering.noprovisioning
@@ -667,13 +668,21 @@
     int getTetherApnRequired();
 
     /**
+     * Set the network selection mode to automatic.
+     *
+     * @param subId the id of the subscription to update.
+     */
+    void setNetworkSelectionModeAutomatic(int subId);
+
+    /**
      * Set the preferred network type.
      * Used for device configuration by some CDMA operators.
      *
+     * @param subId the id of the subscription to update.
      * @param networkType the preferred network type, defined in RILConstants.java.
      * @return true on success; false on any failure.
      */
-    boolean setPreferredNetworkType(int networkType);
+    boolean setPreferredNetworkType(int subId, int networkType);
 
     /**
      * User enable/disable Mobile Data.
@@ -901,6 +910,18 @@
     boolean isImsRegistered();
 
     /**
+     * Returns the Status of Wi-Fi Calling
+     *@hide
+     */
+    boolean isWifiCallingEnabled();
+
+     /**
+     * Returns the Status of Volte
+     *@hide
+     */
+    boolean isVolteEnabled();
+
+    /**
       * Returns the unique device ID of phone, for example, the IMEI for
       * GSM and the MEID for CDMA phones. Return null if device ID is not available.
       *
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index cfbebba..b265d47 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -506,6 +506,11 @@
     }
 
     @Override
+    public int checkSelfPermission(String permission) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public void enforcePermission(
             String permission, int pid, int uid, String message) {
         throw new UnsupportedOperationException();
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 7531d7b..a204376 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -31,6 +31,7 @@
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
+import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.KeySet;
 import android.content.pm.ManifestDigest;
 import android.content.pm.PackageInfo;
@@ -191,13 +192,13 @@
 
     /** @hide */
     @Override
-    public void grantPermission(String packageName, String permissionName) {
+    public void grantPermission(String packageName, String permissionName, UserHandle user) {
         throw new UnsupportedOperationException();
     }
 
     /** @hide */
     @Override
-    public void revokePermission(String packageName, String permissionName) {
+    public void revokePermission(String packageName, String permissionName, UserHandle user) {
         throw new UnsupportedOperationException();
     }
 
@@ -725,6 +726,38 @@
      * @hide
      */
     @Override
+    public void verifyIntentFilter(int id, int verificationCode, List<String> outFailedDomains) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public int getIntentVerificationStatus(String packageName, int userId) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
     public VerifierDeviceIdentity getVerifierDeviceIdentity() {
         throw new UnsupportedOperationException();
     }
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
index ecc7782..56c8119 100644
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
@@ -14,9 +14,12 @@
 
 package com.android.test.dynamic;
 
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
 import android.app.Activity;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.os.Bundle;
+import android.util.Log;
 import android.view.View;
 import android.widget.Button;
 import android.widget.GridLayout;
@@ -52,6 +55,26 @@
             button.setWidth(400);
             button.setHeight(400);
             button.setBackgroundResource(icon[i]);
+            AnimatedVectorDrawable d = (AnimatedVectorDrawable) button.getBackground();
+            d.addListener(new AnimatorListener() {
+                    @Override
+                public void onAnimationStart(Animator animation) {
+                    Log.v(LOGCAT, "Animator start");
+                }
+                    @Override
+                public void onAnimationRepeat(Animator animation) {
+                    Log.v(LOGCAT, "Animator repeat");
+                }
+                    @Override
+                public void onAnimationEnd(Animator animation) {
+                    Log.v(LOGCAT, "Animator end");
+                }
+                    @Override
+                public void onAnimationCancel(Animator animation) {
+                    Log.v(LOGCAT, "Animator cancel");
+                }
+            });
+
             container.addView(button);
             button.setOnClickListener(this);
         }
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
index 2ad23ed5..782d112 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
@@ -45,16 +45,18 @@
     }
 
     public void setAssistStructure(AssistStructure as) {
+        mAssistStructure.dump();
         mAssistStructure = as;
         mTextRects.clear();
-        final int N = as.getWindowCount();
+        final int N = as.getWindowNodeCount();
         if (N > 0) {
-            AssistStructure.ViewNode window = new AssistStructure.ViewNode();
             for (int i=0; i<N; i++) {
-                as.getWindowAt(i, window);
-                buildTextRects(window, 0, 0);
+                AssistStructure.WindowNode windowNode = as.getWindowNodeAt(i);
+                buildTextRects(windowNode.getRootViewNode(), windowNode.getLeft(),
+                        windowNode.getTop());
             }
         }
+        Log.d(TAG, "Building text rects in " + this + ": found " + mTextRects.size());
         invalidate();
     }
 
@@ -69,19 +71,19 @@
         }
         int left = parentLeft+root.getLeft();
         int top = parentTop+root.getTop();
-        Log.d(TAG, "View " + root.getClassName() + ": " + left + ", " + top);
-        if (root.getText() != null) {
+        if (root.getText() != null || root.getContentDescription() != null) {
             Rect r = new Rect(left, top, left+root.getWidth(), top+root.getHeight());
-            Log.d(TAG, "Text Rect " + r.toShortString() + ": " + root.getText());
+            Log.d(TAG, "View " + root.getClassName() + " " + left + "," + top + " tr "
+                    + r.toShortString() + ": "
+                    + (root.getText() != null ? root.getText() : root.getContentDescription()));
             mTextRects.add(r);
         }
         final int N = root.getChildCount();
         if (N > 0) {
             left -= root.getScrollX();
             top -= root.getScrollY();
-            AssistStructure.ViewNode child = new AssistStructure.ViewNode();
             for (int i=0; i<N; i++) {
-                root.getChildAt(i, child);
+                AssistStructure.ViewNode child = root.getChildAt(i);
                 buildTextRects(child, left, top);
             }
         }
@@ -92,6 +94,7 @@
         super.onDraw(canvas);
         getLocationOnScreen(mTmpLocation);
         final int N = mTextRects.size();
+        Log.d(TAG, "Drawing text rects in " + this + ": found " + mTextRects.size());
         for (int i=0; i<N; i++) {
             Rect r = mTextRects.get(i);
             canvas.drawRect(r.left-mTmpLocation[0], r.top-mTmpLocation[1],
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
index bc18ca9..ec727c4 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -142,7 +142,6 @@
             if (assistContext != null) {
                 mAssistStructure = AssistStructure.getAssistStructure(assistContext);
                 if (mAssistStructure != null) {
-                    mAssistStructure.dump();
                     if (mAssistVisualizer != null) {
                         mAssistVisualizer.setAssistStructure(mAssistStructure);
                     }
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
new file mode 100644
index 0000000..e61fd29
--- /dev/null
+++ b/tools/aapt2/Android.mk
@@ -0,0 +1,133 @@
+#
+# 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.
+#
+
+# This tool is prebuilt if we're doing an app-only build.
+ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),)
+
+# ==========================================================
+# Setup some common variables for the different build
+# targets here.
+# ==========================================================
+LOCAL_PATH:= $(call my-dir)
+
+main := Main.cpp
+sources := \
+	BigBuffer.cpp \
+	BinaryResourceParser.cpp \
+	ConfigDescription.cpp \
+	Files.cpp \
+	JavaClassGenerator.cpp \
+	Linker.cpp \
+	Locale.cpp \
+	Logger.cpp \
+	ManifestParser.cpp \
+	ManifestValidator.cpp \
+	ResChunkPullParser.cpp \
+	Resolver.cpp \
+	Resource.cpp \
+	ResourceParser.cpp \
+	ResourceTable.cpp \
+	ResourceValues.cpp \
+	SdkConstants.cpp \
+	StringPool.cpp \
+	TableFlattener.cpp \
+	Util.cpp \
+	ScopedXmlPullParser.cpp \
+	SourceXmlPullParser.cpp \
+	XliffXmlPullParser.cpp \
+	XmlFlattener.cpp
+
+testSources := \
+	BigBuffer_test.cpp \
+	Compat_test.cpp \
+	ConfigDescription_test.cpp \
+	JavaClassGenerator_test.cpp \
+	Linker_test.cpp \
+	Locale_test.cpp \
+	ManifestParser_test.cpp \
+	Maybe_test.cpp \
+	ResourceParser_test.cpp \
+	Resource_test.cpp \
+	ResourceTable_test.cpp \
+	ScopedXmlPullParser_test.cpp \
+	StringPiece_test.cpp \
+	StringPool_test.cpp \
+	Util_test.cpp \
+	XliffXmlPullParser_test.cpp \
+	XmlFlattener_test.cpp
+
+cIncludes :=
+
+hostLdLibs := -lz
+hostStaticLibs := \
+	libandroidfw \
+	libutils \
+	liblog \
+	libcutils \
+	libexpat \
+	libziparchive-host
+
+cFlags := -Wall -Werror -Wno-unused-parameter -UNDEBUG
+cppFlags := -std=c++11 -Wno-missing-field-initializers
+
+# ==========================================================
+# Build the host static library: libaapt2
+# ==========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libaapt2
+
+LOCAL_SRC_FILES := $(sources)
+LOCAL_C_INCLUDES += $(cIncludes)
+LOCAL_CFLAGS += $(cFlags)
+LOCAL_CPPFLAGS += $(cppFlags)
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
+# ==========================================================
+# Build the host tests: libaapt2_tests
+# ==========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := libaapt2_tests
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(testSources)
+
+LOCAL_C_INCLUDES += $(cIncludes)
+LOCAL_STATIC_LIBRARIES += libaapt2 $(hostStaticLibs)
+LOCAL_LDLIBS += $(hostLdLibs)
+LOCAL_CFLAGS += $(cFlags)
+LOCAL_CPPFLAGS += $(cppFlags)
+
+include $(BUILD_HOST_NATIVE_TEST)
+
+# ==========================================================
+# Build the host executable: aapt2
+# ==========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := aapt2
+
+LOCAL_SRC_FILES := $(main)
+
+LOCAL_C_INCLUDES += $(cIncludes)
+LOCAL_STATIC_LIBRARIES += libaapt2 $(hostStaticLibs)
+LOCAL_LDLIBS += $(hostLdLibs)
+LOCAL_CFLAGS += $(cFlags)
+LOCAL_CPPFLAGS += $(cppFlags)
+
+include $(BUILD_HOST_EXECUTABLE)
+
+endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK
diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h
new file mode 100644
index 0000000..30047f7
--- /dev/null
+++ b/tools/aapt2/AppInfo.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_APP_INFO_H
+#define AAPT_APP_INFO_H
+
+#include <string>
+
+namespace aapt {
+
+/**
+ * Holds basic information about the app being built. Most of this information
+ * will come from the app's AndroidManifest.
+ */
+struct AppInfo {
+    /**
+     * App's package name.
+     */
+    std::u16string package;
+};
+
+} // namespace aapt
+
+#endif // AAPT_APP_INFO_H
diff --git a/tools/aapt2/BigBuffer.cpp b/tools/aapt2/BigBuffer.cpp
new file mode 100644
index 0000000..8f57172
--- /dev/null
+++ b/tools/aapt2/BigBuffer.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#include "BigBuffer.h"
+
+#include <algorithm>
+#include <memory>
+#include <vector>
+
+namespace aapt {
+
+void* BigBuffer::nextBlockImpl(size_t size) {
+    if (!mBlocks.empty()) {
+        Block& block = mBlocks.back();
+        if (block.mBlockSize - block.size >= size) {
+            void* outBuffer = block.buffer.get() + block.size;
+            block.size += size;
+            mSize += size;
+            return outBuffer;
+        }
+    }
+
+    const size_t actualSize = std::max(mBlockSize, size);
+
+    Block block = {};
+
+    // Zero-allocate the block's buffer.
+    block.buffer = std::unique_ptr<uint8_t[]>(new uint8_t[actualSize]());
+    assert(block.buffer);
+
+    block.size = size;
+    block.mBlockSize = actualSize;
+
+    mBlocks.push_back(std::move(block));
+    mSize += size;
+    return mBlocks.back().buffer.get();
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/BigBuffer.h b/tools/aapt2/BigBuffer.h
new file mode 100644
index 0000000..025142b
--- /dev/null
+++ b/tools/aapt2/BigBuffer.h
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_BIG_BUFFER_H
+#define AAPT_BIG_BUFFER_H
+
+#include <cstring>
+#include <memory>
+#include <vector>
+
+namespace aapt {
+
+/**
+ * Inspired by protobuf's ZeroCopyOutputStream, offers blocks of memory
+ * in which to write without knowing the full size of the entire payload.
+ * This is essentially a list of memory blocks. As one fills up, another
+ * block is allocated and appended to the end of the list.
+ */
+class BigBuffer {
+public:
+    /**
+     * A contiguous block of allocated memory.
+     */
+    struct Block {
+        /**
+         * Pointer to the memory.
+         */
+        std::unique_ptr<uint8_t[]> buffer;
+
+        /**
+         * Size of memory that is currently occupied. The actual
+         * allocation may be larger.
+         */
+        size_t size;
+
+    private:
+        friend class BigBuffer;
+
+        /**
+         * The size of the memory block allocation.
+         */
+        size_t mBlockSize;
+    };
+
+    typedef std::vector<Block>::const_iterator const_iterator;
+
+    /**
+     * Create a BigBuffer with block allocation sizes
+     * of blockSize.
+     */
+    BigBuffer(size_t blockSize);
+
+    BigBuffer(const BigBuffer&) = delete; // No copying.
+
+    BigBuffer(BigBuffer&& rhs);
+
+    /**
+     * Number of occupied bytes in all the allocated blocks.
+     */
+    size_t size() const;
+
+    /**
+     * Returns a pointer to an array of T, where T is
+     * a POD type. The elements are zero-initialized.
+     */
+    template <typename T>
+    T* nextBlock(size_t count = 1);
+
+    /**
+     * Moves the specified BigBuffer into this one. When this method
+     * returns, buffer is empty.
+     */
+    void appendBuffer(BigBuffer&& buffer);
+
+    /**
+     * Pads the block with 'bytes' bytes of zero values.
+     */
+    void pad(size_t bytes);
+
+    /**
+     * Pads the block so that it aligns on a 4 byte boundary.
+     */
+    void align4();
+
+    const_iterator begin() const;
+    const_iterator end() const;
+
+private:
+    /**
+     * Returns a pointer to a buffer of the requested size.
+     * The buffer is zero-initialized.
+     */
+    void* nextBlockImpl(size_t size);
+
+    size_t mBlockSize;
+    size_t mSize;
+    std::vector<Block> mBlocks;
+};
+
+inline BigBuffer::BigBuffer(size_t blockSize) : mBlockSize(blockSize), mSize(0) {
+}
+
+inline BigBuffer::BigBuffer(BigBuffer&& rhs) :
+        mBlockSize(rhs.mBlockSize), mSize(rhs.mSize), mBlocks(std::move(rhs.mBlocks)) {
+}
+
+inline size_t BigBuffer::size() const {
+    return mSize;
+}
+
+template <typename T>
+inline T* BigBuffer::nextBlock(size_t count) {
+    assert(count != 0);
+    return reinterpret_cast<T*>(nextBlockImpl(sizeof(T) * count));
+}
+
+inline void BigBuffer::appendBuffer(BigBuffer&& buffer) {
+    std::move(buffer.mBlocks.begin(), buffer.mBlocks.end(), std::back_inserter(mBlocks));
+    mSize += buffer.mSize;
+    buffer.mBlocks.clear();
+    buffer.mSize = 0;
+}
+
+inline void BigBuffer::pad(size_t bytes) {
+    nextBlock<char>(bytes);
+}
+
+inline void BigBuffer::align4() {
+    const size_t unaligned = mSize % 4;
+    if (unaligned != 0) {
+        pad(4 - unaligned);
+    }
+}
+
+inline BigBuffer::const_iterator BigBuffer::begin() const {
+    return mBlocks.begin();
+}
+
+inline BigBuffer::const_iterator BigBuffer::end() const {
+    return mBlocks.end();
+}
+
+} // namespace aapt
+
+#endif // AAPT_BIG_BUFFER_H
diff --git a/tools/aapt2/BigBuffer_test.cpp b/tools/aapt2/BigBuffer_test.cpp
new file mode 100644
index 0000000..01ee8d7
--- /dev/null
+++ b/tools/aapt2/BigBuffer_test.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+#include "BigBuffer.h"
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(BigBufferTest, AllocateSingleBlock) {
+    BigBuffer buffer(4);
+
+    EXPECT_NE(nullptr, buffer.nextBlock<char>(2));
+    EXPECT_EQ(2u, buffer.size());
+}
+
+TEST(BigBufferTest, ReturnSameBlockIfNextAllocationFits) {
+    BigBuffer buffer(16);
+
+    char* b1 = buffer.nextBlock<char>(8);
+    EXPECT_NE(nullptr, b1);
+
+    char* b2 = buffer.nextBlock<char>(4);
+    EXPECT_NE(nullptr, b2);
+
+    EXPECT_EQ(b1 + 8, b2);
+}
+
+TEST(BigBufferTest, AllocateExactSizeBlockIfLargerThanBlockSize) {
+    BigBuffer buffer(16);
+
+    EXPECT_NE(nullptr, buffer.nextBlock<char>(32));
+    EXPECT_EQ(32u, buffer.size());
+}
+
+TEST(BigBufferTest, AppendAndMoveBlock) {
+    BigBuffer buffer(16);
+
+    uint32_t* b1 = buffer.nextBlock<uint32_t>();
+    ASSERT_NE(nullptr, b1);
+    *b1 = 33;
+
+    {
+        BigBuffer buffer2(16);
+        b1 = buffer2.nextBlock<uint32_t>();
+        ASSERT_NE(nullptr, b1);
+        *b1 = 44;
+
+        buffer.appendBuffer(std::move(buffer2));
+        EXPECT_EQ(0u, buffer2.size());
+        EXPECT_EQ(buffer2.begin(), buffer2.end());
+    }
+
+    EXPECT_EQ(2 * sizeof(uint32_t), buffer.size());
+
+    auto b = buffer.begin();
+    ASSERT_NE(b, buffer.end());
+    ASSERT_EQ(sizeof(uint32_t), b->size);
+    ASSERT_EQ(33u, *reinterpret_cast<uint32_t*>(b->buffer.get()));
+    ++b;
+
+    ASSERT_NE(b, buffer.end());
+    ASSERT_EQ(sizeof(uint32_t), b->size);
+    ASSERT_EQ(44u, *reinterpret_cast<uint32_t*>(b->buffer.get()));
+    ++b;
+
+    ASSERT_EQ(b, buffer.end());
+}
+
+TEST(BigBufferTest, PadAndAlignProperly) {
+    BigBuffer buffer(16);
+
+    ASSERT_NE(buffer.nextBlock<char>(2), nullptr);
+    ASSERT_EQ(2u, buffer.size());
+    buffer.pad(2);
+    ASSERT_EQ(4u, buffer.size());
+    buffer.align4();
+    ASSERT_EQ(4u, buffer.size());
+    buffer.pad(2);
+    ASSERT_EQ(6u, buffer.size());
+    buffer.align4();
+    ASSERT_EQ(8u, buffer.size());
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/BinaryResourceParser.cpp b/tools/aapt2/BinaryResourceParser.cpp
new file mode 100644
index 0000000..d58f05a
--- /dev/null
+++ b/tools/aapt2/BinaryResourceParser.cpp
@@ -0,0 +1,794 @@
+/*
+ * 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.
+ */
+
+#include "BinaryResourceParser.h"
+#include "Logger.h"
+#include "ResChunkPullParser.h"
+#include "ResourceParser.h"
+#include "ResourceTable.h"
+#include "ResourceTypeExtensions.h"
+#include "ResourceValues.h"
+#include "Source.h"
+#include "Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <androidfw/TypeWrappers.h>
+#include <map>
+#include <string>
+
+namespace aapt {
+
+using namespace android;
+
+template <typename T>
+inline static const T* convertTo(const ResChunk_header* chunk) {
+    if (chunk->headerSize < sizeof(T)) {
+        return nullptr;
+    }
+    return reinterpret_cast<const T*>(chunk);
+}
+
+inline static const uint8_t* getChunkData(const ResChunk_header& chunk) {
+    return reinterpret_cast<const uint8_t*>(&chunk) + chunk.headerSize;
+}
+
+inline static size_t getChunkDataLen(const ResChunk_header& chunk) {
+    return chunk.size - chunk.headerSize;
+}
+
+/*
+ * Visitor that converts a reference's resource ID to a resource name,
+ * given a mapping from resource ID to resource name.
+ */
+struct ReferenceIdToNameVisitor : ValueVisitor {
+    ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceName>& cache) : mCache(cache) {
+    }
+
+    void visit(Reference& reference, ValueVisitorArgs&) override {
+        idToName(reference);
+    }
+
+    void visit(Attribute& attr, ValueVisitorArgs&) override {
+        for (auto& entry : attr.symbols) {
+            idToName(entry.symbol);
+        }
+    }
+
+    void visit(Style& style, ValueVisitorArgs&) override {
+        if (style.parent.id.isValid()) {
+            idToName(style.parent);
+        }
+
+        for (auto& entry : style.entries) {
+            idToName(entry.key);
+            entry.value->accept(*this, {});
+        }
+    }
+
+    void visit(Styleable& styleable, ValueVisitorArgs&) override {
+        for (auto& attr : styleable.entries) {
+            idToName(attr);
+        }
+    }
+
+    void visit(Array& array, ValueVisitorArgs&) override {
+        for (auto& item : array.items) {
+            item->accept(*this, {});
+        }
+    }
+
+    void visit(Plural& plural, ValueVisitorArgs&) override {
+        for (auto& item : plural.values) {
+            if (item) {
+                item->accept(*this, {});
+            }
+        }
+    }
+
+private:
+    void idToName(Reference& reference) {
+        if (!reference.id.isValid()) {
+            return;
+        }
+
+        auto cacheIter = mCache.find(reference.id);
+        if (cacheIter == std::end(mCache)) {
+            Logger::note() << "failed to find " << reference.id << std::endl;
+        } else {
+            reference.name = cacheIter->second;
+            reference.id = 0;
+        }
+    }
+
+    const std::map<ResourceId, ResourceName>& mCache;
+};
+
+
+BinaryResourceParser::BinaryResourceParser(std::shared_ptr<ResourceTable> table,
+                                           const Source& source,
+                                           const void* data,
+                                           size_t len) :
+        mTable(table), mSource(source), mData(data), mDataLen(len) {
+}
+
+bool BinaryResourceParser::parse() {
+    ResChunkPullParser parser(mData, mDataLen);
+
+    bool error = false;
+    while(ResChunkPullParser::isGoodEvent(parser.next())) {
+        if (parser.getChunk()->type != android::RES_TABLE_TYPE) {
+            Logger::warn(mSource)
+                    << "unknown chunk of type '"
+                    << parser.getChunk()->type
+                    << "'."
+                    << std::endl;
+            continue;
+        }
+
+        error |= !parseTable(parser.getChunk());
+    }
+
+    if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
+        Logger::error(mSource)
+                << "bad document: "
+                << parser.getLastError()
+                << "."
+                << std::endl;
+        return false;
+    }
+    return !error;
+}
+
+bool BinaryResourceParser::getSymbol(const void* data, ResourceNameRef* outSymbol) {
+    if (!mSymbolEntries || mSymbolEntryCount == 0) {
+        return false;
+    }
+
+    // We only support 32 bit offsets right now.
+    const ptrdiff_t offset = reinterpret_cast<uintptr_t>(data) -
+            reinterpret_cast<uintptr_t>(mData);
+    if (offset > std::numeric_limits<uint32_t>::max()) {
+        return false;
+    }
+
+    for (size_t i = 0; i < mSymbolEntryCount; i++) {
+        if (mSymbolEntries[i].offset == offset) {
+            // This offset is a symbol!
+            const StringPiece16 str = util::getString(mSymbolPool,
+                                                      mSymbolEntries[i].stringIndex);
+            StringPiece16 typeStr;
+            ResourceParser::extractResourceName(str, &outSymbol->package, &typeStr,
+                                                &outSymbol->entry);
+            const ResourceType* type = parseResourceType(typeStr);
+            if (!type) {
+                return false;
+            }
+            outSymbol->type = *type;
+
+            // Since we scan the symbol table in order, we can start looking for the
+            // next symbol from this point.
+            mSymbolEntryCount -= i + 1;
+            mSymbolEntries += i + 1;
+            return true;
+        }
+    }
+    return false;
+}
+
+bool BinaryResourceParser::parseSymbolTable(const ResChunk_header* chunk) {
+    const SymbolTable_header* symbolTableHeader = convertTo<SymbolTable_header>(chunk);
+    if (!symbolTableHeader) {
+        Logger::error(mSource)
+                << "could not parse chunk as SymbolTable_header."
+                << std::endl;
+        return false;
+    }
+
+    const size_t entrySizeBytes = symbolTableHeader->count * sizeof(SymbolTable_entry);
+    if (entrySizeBytes > getChunkDataLen(symbolTableHeader->header)) {
+        Logger::error(mSource)
+                << "entries extend beyond chunk."
+                << std::endl;
+        return false;
+    }
+
+    mSymbolEntries = reinterpret_cast<const SymbolTable_entry*>(
+            getChunkData(symbolTableHeader->header));
+    mSymbolEntryCount = symbolTableHeader->count;
+
+    ResChunkPullParser parser(getChunkData(symbolTableHeader->header) + entrySizeBytes,
+                              getChunkDataLen(symbolTableHeader->header) - entrySizeBytes);
+    if (!ResChunkPullParser::isGoodEvent(parser.next())) {
+        Logger::error(mSource)
+                << "failed to parse chunk: "
+                << parser.getLastError()
+                << "."
+                << std::endl;
+        return false;
+    }
+
+    if (parser.getChunk()->type != android::RES_STRING_POOL_TYPE) {
+        Logger::error(mSource)
+                << "expected Symbol string pool."
+                << std::endl;
+        return false;
+    }
+
+    if (mSymbolPool.setTo(parser.getChunk(), parser.getChunk()->size) != android::NO_ERROR) {
+        Logger::error(mSource)
+                << "failed to parse symbol string pool with code: "
+                << mSymbolPool.getError()
+                << "."
+                << std::endl;
+        return false;
+    }
+    return true;
+}
+
+bool BinaryResourceParser::parseTable(const ResChunk_header* chunk) {
+    const ResTable_header* tableHeader = convertTo<ResTable_header>(chunk);
+    if (!tableHeader) {
+        Logger::error(mSource)
+                << "could not parse chunk as ResTable_header."
+                << std::endl;
+        return false;
+    }
+
+    ResChunkPullParser parser(getChunkData(tableHeader->header),
+                              getChunkDataLen(tableHeader->header));
+    while (ResChunkPullParser::isGoodEvent(parser.next())) {
+        switch (parser.getChunk()->type) {
+        case android::RES_STRING_POOL_TYPE:
+            if (mValuePool.getError() == android::NO_INIT) {
+                if (mValuePool.setTo(parser.getChunk(), parser.getChunk()->size) !=
+                        android::NO_ERROR) {
+                    Logger::error(mSource)
+                            << "failed to parse value string pool with code: "
+                            << mValuePool.getError()
+                            << "."
+                            << std::endl;
+                    return false;
+                }
+
+                // Reserve some space for the strings we are going to add.
+                mTable->getValueStringPool().hintWillAdd(
+                        mValuePool.size(), mValuePool.styleCount());
+            } else {
+                Logger::warn(mSource)
+                    << "unexpected string pool."
+                    << std::endl;
+            }
+            break;
+
+        case RES_TABLE_SYMBOL_TABLE_TYPE:
+            if (!parseSymbolTable(parser.getChunk())) {
+                return false;
+            }
+            break;
+
+        case RES_TABLE_SOURCE_POOL_TYPE: {
+            if (mSourcePool.setTo(getChunkData(*parser.getChunk()),
+                        getChunkDataLen(*parser.getChunk())) != android::NO_ERROR) {
+                Logger::error(mSource)
+                        << "failed to parse source pool with code: "
+                        << mSourcePool.getError()
+                        << "."
+                        << std::endl;
+                return false;
+            }
+            break;
+        }
+
+        case android::RES_TABLE_PACKAGE_TYPE:
+            if (!parsePackage(parser.getChunk())) {
+                return false;
+            }
+            break;
+
+        default:
+            Logger::warn(mSource)
+                << "unexpected chunk of type "
+                << parser.getChunk()->type
+                << "."
+                << std::endl;
+            break;
+        }
+    }
+
+    if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
+        Logger::error(mSource)
+            << "bad resource table: " << parser.getLastError()
+            << "."
+            << std::endl;
+        return false;
+    }
+    return true;
+}
+
+bool BinaryResourceParser::parsePackage(const ResChunk_header* chunk) {
+    if (mValuePool.getError() != android::NO_ERROR) {
+        Logger::error(mSource)
+                << "no value string pool for ResTable."
+                << std::endl;
+        return false;
+    }
+
+    const ResTable_package* packageHeader = convertTo<ResTable_package>(chunk);
+    if (!packageHeader) {
+        Logger::error(mSource)
+                << "could not parse chunk as ResTable_header."
+                << std::endl;
+        return false;
+    }
+
+    if (mTable->getPackageId() == ResourceTable::kUnsetPackageId) {
+        // This is the first time the table has it's package ID set.
+        mTable->setPackageId(packageHeader->id);
+    } else if (mTable->getPackageId() != packageHeader->id) {
+        Logger::error(mSource)
+                << "ResTable_package has package ID "
+                << std::hex << packageHeader->id << std::dec
+                << " but ResourceTable has package ID "
+                << std::hex << mTable->getPackageId() << std::dec
+                << std::endl;
+        return false;
+    }
+
+    size_t len = strnlen16(reinterpret_cast<const char16_t*>(packageHeader->name),
+            sizeof(packageHeader->name) / sizeof(packageHeader->name[0]));
+    mTable->setPackage(StringPiece16(reinterpret_cast<const char16_t*>(packageHeader->name), len));
+
+    ResChunkPullParser parser(getChunkData(packageHeader->header),
+                              getChunkDataLen(packageHeader->header));
+    while (ResChunkPullParser::isGoodEvent(parser.next())) {
+        switch (parser.getChunk()->type) {
+        case android::RES_STRING_POOL_TYPE:
+            if (mTypePool.getError() == android::NO_INIT) {
+                if (mTypePool.setTo(parser.getChunk(), parser.getChunk()->size) !=
+                        android::NO_ERROR) {
+                    Logger::error(mSource)
+                            << "failed to parse type string pool with code "
+                            << mTypePool.getError()
+                            << "."
+                            << std::endl;
+                    return false;
+                }
+            } else if (mKeyPool.getError() == android::NO_INIT) {
+                if (mKeyPool.setTo(parser.getChunk(), parser.getChunk()->size) !=
+                        android::NO_ERROR) {
+                    Logger::error(mSource)
+                            << "failed to parse key string pool with code "
+                            << mKeyPool.getError()
+                            << "."
+                            << std::endl;
+                    return false;
+                }
+            } else {
+                Logger::warn(mSource)
+                        << "unexpected string pool."
+                        << std::endl;
+            }
+            break;
+
+        case android::RES_TABLE_TYPE_SPEC_TYPE:
+            if (!parseTypeSpec(parser.getChunk())) {
+                return false;
+            }
+            break;
+
+        case android::RES_TABLE_TYPE_TYPE:
+            if (!parseType(parser.getChunk())) {
+                return false;
+            }
+            break;
+
+        default:
+            Logger::warn(mSource)
+                    << "unexpected chunk of type "
+                    << parser.getChunk()->type
+                    << "."
+                    << std::endl;
+            break;
+        }
+    }
+
+    if (parser.getEvent() == ResChunkPullParser::Event::BadDocument) {
+        Logger::error(mSource)
+                << "bad package: "
+                << parser.getLastError()
+                << "."
+                << std::endl;
+        return false;
+    }
+
+    // Now go through the table and change resource ID references to
+    // symbolic references.
+
+    ReferenceIdToNameVisitor visitor(mIdIndex);
+    for (auto& type : *mTable) {
+        for (auto& entry : type->entries) {
+            for (auto& configValue : entry->values) {
+                configValue.value->accept(visitor, {});
+            }
+        }
+    }
+    return true;
+}
+
+bool BinaryResourceParser::parseTypeSpec(const ResChunk_header* chunk) {
+    if (mTypePool.getError() != android::NO_ERROR) {
+        Logger::error(mSource)
+                << "no type string pool available for ResTable_typeSpec."
+                << std::endl;
+        return false;
+    }
+
+    const ResTable_typeSpec* typeSpec = convertTo<ResTable_typeSpec>(chunk);
+    if (!typeSpec) {
+        Logger::error(mSource)
+                << "could not parse chunk as ResTable_typeSpec."
+                << std::endl;
+        return false;
+    }
+
+    if (typeSpec->id == 0) {
+        Logger::error(mSource)
+                << "ResTable_typeSpec has invalid id: "
+                << typeSpec->id
+                << "."
+                << std::endl;
+        return false;
+    }
+    return true;
+}
+
+bool BinaryResourceParser::parseType(const ResChunk_header* chunk) {
+    if (mTypePool.getError() != android::NO_ERROR) {
+        Logger::error(mSource)
+                << "no type string pool available for ResTable_typeSpec."
+                << std::endl;
+        return false;
+    }
+
+    if (mKeyPool.getError() != android::NO_ERROR) {
+        Logger::error(mSource)
+                << "no key string pool available for ResTable_type."
+                << std::endl;
+        return false;
+    }
+
+    const ResTable_type* type = convertTo<ResTable_type>(chunk);
+    if (!type) {
+        Logger::error(mSource)
+                << "could not parse chunk as ResTable_type."
+                << std::endl;
+        return false;
+    }
+
+    if (type->id == 0) {
+        Logger::error(mSource)
+                << "ResTable_type has invalid id: "
+                << type->id
+                << "."
+                << std::endl;
+        return false;
+    }
+
+    const ConfigDescription config(type->config);
+    const StringPiece16 typeName = util::getString(mTypePool, type->id - 1);
+
+    const ResourceType* parsedType = parseResourceType(typeName);
+    if (!parsedType) {
+        Logger::error(mSource)
+                << "invalid type name '"
+                << typeName
+                << "' for type with ID "
+                << uint32_t(type->id)
+                << "." << std::endl;
+        return false;
+    }
+
+    android::TypeVariant tv(type);
+    for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) {
+        if (!*it) {
+            continue;
+        }
+
+        const ResTable_entry* entry = *it;
+        const ResourceName name = {
+                mTable->getPackage(),
+                *parsedType,
+                util::getString(mKeyPool, entry->key.index).toString()
+        };
+
+        const ResourceId resId = { mTable->getPackageId(), type->id, it.index() };
+
+        std::unique_ptr<Value> resourceValue;
+        const ResTable_entry_source* sourceBlock = nullptr;
+        if (entry->flags & ResTable_entry::FLAG_COMPLEX) {
+            const ResTable_map_entry* mapEntry = static_cast<const ResTable_map_entry*>(entry);
+            if (mapEntry->size - sizeof(*mapEntry) == sizeof(*sourceBlock)) {
+                const uint8_t* data = reinterpret_cast<const uint8_t*>(mapEntry);
+                data += mapEntry->size - sizeof(*sourceBlock);
+                sourceBlock = reinterpret_cast<const ResTable_entry_source*>(data);
+            }
+
+            // TODO(adamlesinski): Check that the entry count is valid.
+            resourceValue = parseMapEntry(name, config, mapEntry);
+        } else {
+            if (entry->size - sizeof(*entry) == sizeof(*sourceBlock)) {
+                const uint8_t* data = reinterpret_cast<const uint8_t*>(entry);
+                data += entry->size - sizeof(*sourceBlock);
+                sourceBlock = reinterpret_cast<const ResTable_entry_source*>(data);
+            }
+
+            const Res_value* value = reinterpret_cast<const Res_value*>(
+                    reinterpret_cast<const uint8_t*>(entry) + entry->size);
+            resourceValue = parseValue(name, config, value, entry->flags);
+        }
+
+        if (!resourceValue) {
+            // TODO(adamlesinski): For now this is ok, but it really shouldn't be.
+            continue;
+        }
+
+        SourceLine source = mSource.line(0);
+        if (sourceBlock) {
+            size_t len;
+            const char* str = mSourcePool.string8At(sourceBlock->pathIndex, &len);
+            if (str) {
+                source.path.assign(str, len);
+            }
+            source.line = sourceBlock->line;
+        }
+
+        if (!mTable->addResource(name, config, source, std::move(resourceValue))) {
+            return false;
+        }
+
+        if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0) {
+            if (!mTable->markPublic(name, resId, mSource.line(0))) {
+                return false;
+            }
+        }
+
+        // Add this resource name->id mapping to the index so
+        // that we can resolve all ID references to name references.
+        auto cacheIter = mIdIndex.find(resId);
+        if (cacheIter == mIdIndex.end()) {
+            mIdIndex.insert({ resId, name });
+        }
+    }
+    return true;
+}
+
+std::unique_ptr<Item> BinaryResourceParser::parseValue(const ResourceNameRef& name,
+                                                       const ConfigDescription& config,
+                                                       const Res_value* value,
+                                                       uint16_t flags) {
+    if (value->dataType == Res_value::TYPE_STRING) {
+        StringPiece16 str = util::getString(mValuePool, value->data);
+
+        const ResStringPool_span* spans = mValuePool.styleAt(value->data);
+        if (spans != nullptr) {
+            StyleString styleStr = { str.toString() };
+            while (spans->name.index != ResStringPool_span::END) {
+                styleStr.spans.push_back(Span{
+                        util::getString(mValuePool, spans->name.index).toString(),
+                        spans->firstChar,
+                        spans->lastChar
+                });
+                spans++;
+            }
+            return util::make_unique<StyledString>(
+                    mTable->getValueStringPool().makeRef(
+                            styleStr, StringPool::Context{1, config}));
+        } else {
+            // There are no styles associated with this string, so treat it as
+            // a simple string.
+            return util::make_unique<String>(
+                    mTable->getValueStringPool().makeRef(
+                            str, StringPool::Context{1, config}));
+        }
+    }
+
+    if (value->dataType == Res_value::TYPE_REFERENCE ||
+            value->dataType == Res_value::TYPE_ATTRIBUTE) {
+        const Reference::Type type = (value->dataType == Res_value::TYPE_REFERENCE) ?
+                    Reference::Type::kResource : Reference::Type::kAttribute;
+
+        if (value->data != 0) {
+            // This is a normal reference.
+            return util::make_unique<Reference>(value->data, type);
+        }
+
+        // This reference has an invalid ID. Check if it is an unresolved symbol.
+        ResourceNameRef symbol;
+        if (getSymbol(&value->data, &symbol)) {
+            return util::make_unique<Reference>(symbol, type);
+        }
+
+        // This is not an unresolved symbol, so it must be the magic @null reference.
+        Res_value nullType = {};
+        nullType.dataType = Res_value::TYPE_NULL;
+        nullType.data = Res_value::DATA_NULL_UNDEFINED;
+        return util::make_unique<BinaryPrimitive>(nullType);
+    }
+
+    if (value->dataType == ExtendedTypes::TYPE_SENTINEL) {
+        return util::make_unique<Sentinel>();
+    }
+
+    if (value->dataType == ExtendedTypes::TYPE_RAW_STRING) {
+        return util::make_unique<RawString>(
+                mTable->getValueStringPool().makeRef(util::getString(mValuePool, value->data),
+                                                    StringPool::Context{ 1, config }));
+    }
+
+    if (name.type == ResourceType::kId ||
+            (value->dataType == Res_value::TYPE_NULL &&
+            value->data == Res_value::DATA_NULL_UNDEFINED &&
+            (flags & ResTable_entry::FLAG_WEAK) != 0)) {
+        return util::make_unique<Id>();
+    }
+
+    // Treat this as a raw binary primitive.
+    return util::make_unique<BinaryPrimitive>(*value);
+}
+
+std::unique_ptr<Value> BinaryResourceParser::parseMapEntry(const ResourceNameRef& name,
+                                                           const ConfigDescription& config,
+                                                           const ResTable_map_entry* map) {
+    switch (name.type) {
+        case ResourceType::kStyle:
+            return parseStyle(name, config, map);
+        case ResourceType::kAttr:
+            return parseAttr(name, config, map);
+        case ResourceType::kArray:
+            return parseArray(name, config, map);
+        case ResourceType::kStyleable:
+            return parseStyleable(name, config, map);
+        case ResourceType::kPlurals:
+            return parsePlural(name, config, map);
+        default:
+            break;
+    }
+    return {};
+}
+
+std::unique_ptr<Style> BinaryResourceParser::parseStyle(const ResourceNameRef& name,
+                                                        const ConfigDescription& config,
+                                                        const ResTable_map_entry* map) {
+    std::unique_ptr<Style> style = util::make_unique<Style>();
+    if (map->parent.ident == 0) {
+        // The parent is either not set or it is an unresolved symbol.
+        // Check to see if it is a symbol.
+        ResourceNameRef symbol;
+        if (getSymbol(&map->parent.ident, &symbol)) {
+            style->parent.name = symbol.toResourceName();
+        }
+    } else {
+         // The parent is a regular reference to a resource.
+        style->parent.id = map->parent.ident;
+    }
+
+    for (const ResTable_map& mapEntry : map) {
+        style->entries.emplace_back();
+        Style::Entry& styleEntry = style->entries.back();
+
+        if (mapEntry.name.ident == 0) {
+            // The map entry's key (attribute) is not set. This must be
+            // a symbol reference, so resolve it.
+            ResourceNameRef symbol;
+            bool result = getSymbol(&mapEntry.name.ident, &symbol);
+            assert(result);
+            styleEntry.key.name = symbol.toResourceName();
+        } else {
+            // The map entry's key (attribute) is a regular reference.
+            styleEntry.key.id = mapEntry.name.ident;
+        }
+
+        // Parse the attribute's value.
+        styleEntry.value = parseValue(name, config, &mapEntry.value, 0);
+        assert(styleEntry.value);
+    }
+    return style;
+}
+
+std::unique_ptr<Attribute> BinaryResourceParser::parseAttr(const ResourceNameRef& name,
+                                                           const ConfigDescription& config,
+                                                           const ResTable_map_entry* map) {
+    const bool isWeak = (map->flags & ResTable_entry::FLAG_WEAK) != 0;
+    std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
+
+    // First we must discover what type of attribute this is. Find the type mask.
+    auto typeMaskIter = std::find_if(begin(map), end(map), [](const ResTable_map& entry) -> bool {
+        return entry.name.ident == ResTable_map::ATTR_TYPE;
+    });
+
+    if (typeMaskIter != end(map)) {
+        attr->typeMask = typeMaskIter->value.data;
+    }
+
+    if (attr->typeMask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
+        for (const ResTable_map& mapEntry : map) {
+            if (Res_INTERNALID(mapEntry.name.ident)) {
+                continue;
+            }
+
+            attr->symbols.push_back(Attribute::Symbol{
+                    Reference(mapEntry.name.ident),
+                    mapEntry.value.data
+            });
+        }
+    }
+
+    // TODO(adamlesinski): Find min, max, i80n, etc attributes.
+    return attr;
+}
+
+std::unique_ptr<Array> BinaryResourceParser::parseArray(const ResourceNameRef& name,
+                                                        const ConfigDescription& config,
+                                                        const ResTable_map_entry* map) {
+    std::unique_ptr<Array> array = util::make_unique<Array>();
+    for (const ResTable_map& mapEntry : map) {
+        array->items.push_back(parseValue(name, config, &mapEntry.value, 0));
+    }
+    return array;
+}
+
+std::unique_ptr<Styleable> BinaryResourceParser::parseStyleable(const ResourceNameRef& name,
+                                                                const ConfigDescription& config,
+                                                                const ResTable_map_entry* map) {
+    std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
+    for (const ResTable_map& mapEntry : map) {
+        styleable->entries.emplace_back(mapEntry.name.ident);
+    }
+    return styleable;
+}
+
+std::unique_ptr<Plural> BinaryResourceParser::parsePlural(const ResourceNameRef& name,
+                                                          const ConfigDescription& config,
+                                                          const ResTable_map_entry* map) {
+    std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+    for (const ResTable_map& mapEntry : map) {
+        std::unique_ptr<Item> item = parseValue(name, config, &mapEntry.value, 0);
+
+        switch (mapEntry.name.ident) {
+            case android::ResTable_map::ATTR_ZERO:
+                plural->values[Plural::Zero] = std::move(item);
+                break;
+            case android::ResTable_map::ATTR_ONE:
+                plural->values[Plural::One] = std::move(item);
+                break;
+            case android::ResTable_map::ATTR_TWO:
+                plural->values[Plural::Two] = std::move(item);
+                break;
+            case android::ResTable_map::ATTR_FEW:
+                plural->values[Plural::Few] = std::move(item);
+                break;
+            case android::ResTable_map::ATTR_MANY:
+                plural->values[Plural::Many] = std::move(item);
+                break;
+            case android::ResTable_map::ATTR_OTHER:
+                plural->values[Plural::Other] = std::move(item);
+                break;
+        }
+    }
+    return plural;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/BinaryResourceParser.h b/tools/aapt2/BinaryResourceParser.h
new file mode 100644
index 0000000..9268078
--- /dev/null
+++ b/tools/aapt2/BinaryResourceParser.h
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_BINARY_RESOURCE_PARSER_H
+#define AAPT_BINARY_RESOURCE_PARSER_H
+
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "Source.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <string>
+
+namespace aapt {
+
+struct SymbolTable_entry;
+
+/*
+ * Parses a binary resource table (resources.arsc) and adds the entries
+ * to a ResourceTable. This is different than the libandroidfw ResTable
+ * in that it scans the table from top to bottom and doesn't require
+ * support for random access. It is also able to parse non-runtime
+ * chunks and types.
+ */
+class BinaryResourceParser {
+public:
+    /*
+     * Creates a parser, which will read `len` bytes from `data`, and
+     * add any resources parsed to `table`. `source` is for logging purposes.
+     */
+    BinaryResourceParser(std::shared_ptr<ResourceTable> table, const Source& source,
+                         const void* data, size_t len);
+
+    BinaryResourceParser(const BinaryResourceParser&) = delete; // No copy.
+
+    /*
+     * Parses the binary resource table and returns true if successful.
+     */
+    bool parse();
+
+private:
+    // Helper method to retrieve the symbol name for a given table offset specified
+    // as a pointer.
+    bool getSymbol(const void* data, ResourceNameRef* outSymbol);
+
+    bool parseTable(const android::ResChunk_header* chunk);
+    bool parseSymbolTable(const android::ResChunk_header* chunk);
+
+    // Looks up the resource ID in the reference and converts it to a name if available.
+    bool idToName(Reference* reference);
+
+    bool parsePackage(const android::ResChunk_header* chunk);
+    bool parseTypeSpec(const android::ResChunk_header* chunk);
+    bool parseType(const android::ResChunk_header* chunk);
+
+    std::unique_ptr<Item> parseValue(const ResourceNameRef& name,
+            const ConfigDescription& config, const android::Res_value* value, uint16_t flags);
+
+    std::unique_ptr<Value> parseMapEntry(const ResourceNameRef& name,
+            const ConfigDescription& config, const android::ResTable_map_entry* map);
+
+    std::unique_ptr<Style> parseStyle(const ResourceNameRef& name,
+            const ConfigDescription& config, const android::ResTable_map_entry* map);
+
+    std::unique_ptr<Attribute> parseAttr(const ResourceNameRef& name,
+            const ConfigDescription& config, const android::ResTable_map_entry* map);
+
+    std::unique_ptr<Array> parseArray(const ResourceNameRef& name,
+            const ConfigDescription& config, const android::ResTable_map_entry* map);
+
+    std::unique_ptr<Plural> parsePlural(const ResourceNameRef& name,
+            const ConfigDescription& config, const android::ResTable_map_entry* map);
+
+    std::unique_ptr<Styleable> parseStyleable(const ResourceNameRef& name,
+            const ConfigDescription& config, const android::ResTable_map_entry* map);
+
+    std::shared_ptr<ResourceTable> mTable;
+
+    const Source mSource;
+
+    const void* mData;
+    const size_t mDataLen;
+
+    // The package name of the resource table.
+    std::u16string mPackage;
+
+    // The array of symbol entries. Each element points to an offset
+    // in the table and an index into the symbol table string pool.
+    const SymbolTable_entry* mSymbolEntries = nullptr;
+
+    // Number of symbol entries.
+    size_t mSymbolEntryCount = 0;
+
+    // The symbol table string pool. Holds the names of symbols
+    // referenced in this table but not defined nor resolved to an
+    // ID.
+    android::ResStringPool mSymbolPool;
+
+    // The source string pool. Resource entries may have an extra
+    // field that points into this string pool, which denotes where
+    // the resource was parsed from originally.
+    android::ResStringPool mSourcePool;
+
+    // The standard value string pool for resource values.
+    android::ResStringPool mValuePool;
+
+    // The string pool that holds the names of the types defined
+    // in this table.
+    android::ResStringPool mTypePool;
+
+    // The string pool that holds the names of the entries defined
+    // in this table.
+    android::ResStringPool mKeyPool;
+
+    // A mapping of resource ID to resource name. When we finish parsing
+    // we use this to convert all resource IDs to symbolic references.
+    std::map<ResourceId, ResourceName> mIdIndex;
+};
+
+} // namespace aapt
+
+namespace android {
+
+/**
+ * Iterator functionality for ResTable_map_entry.
+ */
+
+inline const ResTable_map* begin(const ResTable_map_entry* map) {
+    return reinterpret_cast<const ResTable_map*>(
+            reinterpret_cast<const uint8_t*>(map) + map->size);
+}
+
+inline const ResTable_map* end(const ResTable_map_entry* map) {
+    return reinterpret_cast<const ResTable_map*>(
+            reinterpret_cast<const uint8_t*>(map) + map->size) + map->count;
+}
+
+} // namespace android
+
+#endif // AAPT_BINARY_RESOURCE_PARSER_H
diff --git a/tools/aapt2/Compat_test.cpp b/tools/aapt2/Compat_test.cpp
new file mode 100644
index 0000000..96aee44
--- /dev/null
+++ b/tools/aapt2/Compat_test.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+namespace aapt {
+
+TEST(CompatTest, VersionAttributesInStyle) {
+}
+
+TEST(CompatTest, VersionAttributesInXML) {
+}
+
+TEST(CompatTest, DoNotOverrideExistingVersionedFiles) {
+}
+
+TEST(CompatTest, VersionAttributesInStyleWithCorrectPrecedence) {
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp
new file mode 100644
index 0000000..6ddf94a
--- /dev/null
+++ b/tools/aapt2/ConfigDescription.cpp
@@ -0,0 +1,752 @@
+/*
+ * 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.
+ */
+
+#include "ConfigDescription.h"
+#include "Locale.h"
+#include "SdkConstants.h"
+#include "StringPiece.h"
+#include "Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+using android::ResTable_config;
+
+static const char* kWildcardName = "any";
+
+static bool parseMcc(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->mcc = 0;
+        return true;
+    }
+    const char* c = name;
+    if (tolower(*c) != 'm') return false;
+    c++;
+    if (tolower(*c) != 'c') return false;
+    c++;
+    if (tolower(*c) != 'c') return false;
+    c++;
+
+    const char* val = c;
+
+    while (*c >= '0' && *c <= '9') {
+        c++;
+    }
+    if (*c != 0) return false;
+    if (c-val != 3) return false;
+
+    int d = atoi(val);
+    if (d != 0) {
+        if (out) out->mcc = d;
+        return true;
+    }
+
+    return false;
+}
+
+static bool parseMnc(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->mcc = 0;
+        return true;
+    }
+    const char* c = name;
+    if (tolower(*c) != 'm') return false;
+    c++;
+    if (tolower(*c) != 'n') return false;
+    c++;
+    if (tolower(*c) != 'c') return false;
+    c++;
+
+    const char* val = c;
+
+    while (*c >= '0' && *c <= '9') {
+        c++;
+    }
+    if (*c != 0) return false;
+    if (c-val == 0 || c-val > 3) return false;
+
+    if (out) {
+        out->mnc = atoi(val);
+        if (out->mnc == 0) {
+            out->mnc = ACONFIGURATION_MNC_ZERO;
+        }
+    }
+
+    return true;
+}
+
+static bool parseLayoutDirection(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+                | ResTable_config::LAYOUTDIR_ANY;
+        return true;
+    } else if (strcmp(name, "ldltr") == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+                | ResTable_config::LAYOUTDIR_LTR;
+        return true;
+    } else if (strcmp(name, "ldrtl") == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+                | ResTable_config::LAYOUTDIR_RTL;
+        return true;
+    }
+
+    return false;
+}
+
+static bool parseScreenLayoutSize(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+                | ResTable_config::SCREENSIZE_ANY;
+        return true;
+    } else if (strcmp(name, "small") == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+                | ResTable_config::SCREENSIZE_SMALL;
+        return true;
+    } else if (strcmp(name, "normal") == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+                | ResTable_config::SCREENSIZE_NORMAL;
+        return true;
+    } else if (strcmp(name, "large") == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+                | ResTable_config::SCREENSIZE_LARGE;
+        return true;
+    } else if (strcmp(name, "xlarge") == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+                | ResTable_config::SCREENSIZE_XLARGE;
+        return true;
+    }
+
+    return false;
+}
+
+static bool parseScreenLayoutLong(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
+                | ResTable_config::SCREENLONG_ANY;
+        return true;
+    } else if (strcmp(name, "long") == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
+                | ResTable_config::SCREENLONG_YES;
+        return true;
+    } else if (strcmp(name, "notlong") == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
+                | ResTable_config::SCREENLONG_NO;
+        return true;
+    }
+
+    return false;
+}
+
+static bool parseOrientation(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->orientation = out->ORIENTATION_ANY;
+        return true;
+    } else if (strcmp(name, "port") == 0) {
+        if (out) out->orientation = out->ORIENTATION_PORT;
+        return true;
+    } else if (strcmp(name, "land") == 0) {
+        if (out) out->orientation = out->ORIENTATION_LAND;
+        return true;
+    } else if (strcmp(name, "square") == 0) {
+        if (out) out->orientation = out->ORIENTATION_SQUARE;
+        return true;
+    }
+
+    return false;
+}
+
+static bool parseUiModeType(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->uiMode =
+                (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+                | ResTable_config::UI_MODE_TYPE_ANY;
+        return true;
+    } else if (strcmp(name, "desk") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+              | ResTable_config::UI_MODE_TYPE_DESK;
+        return true;
+    } else if (strcmp(name, "car") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+              | ResTable_config::UI_MODE_TYPE_CAR;
+        return true;
+    } else if (strcmp(name, "television") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+              | ResTable_config::UI_MODE_TYPE_TELEVISION;
+        return true;
+    } else if (strcmp(name, "appliance") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+              | ResTable_config::UI_MODE_TYPE_APPLIANCE;
+        return true;
+    } else if (strcmp(name, "watch") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+              | ResTable_config::UI_MODE_TYPE_WATCH;
+        return true;
+    }
+
+    return false;
+}
+
+static bool parseUiModeNight(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->uiMode =
+                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+                | ResTable_config::UI_MODE_NIGHT_ANY;
+        return true;
+    } else if (strcmp(name, "night") == 0) {
+        if (out) out->uiMode =
+                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+                | ResTable_config::UI_MODE_NIGHT_YES;
+        return true;
+    } else if (strcmp(name, "notnight") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+              | ResTable_config::UI_MODE_NIGHT_NO;
+        return true;
+    }
+
+    return false;
+}
+
+static bool parseDensity(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->density = ResTable_config::DENSITY_DEFAULT;
+        return true;
+    }
+
+    if (strcmp(name, "anydpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_ANY;
+        return true;
+    }
+
+    if (strcmp(name, "nodpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_NONE;
+        return true;
+    }
+
+    if (strcmp(name, "ldpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_LOW;
+        return true;
+    }
+
+    if (strcmp(name, "mdpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_MEDIUM;
+        return true;
+    }
+
+    if (strcmp(name, "tvdpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_TV;
+        return true;
+    }
+
+    if (strcmp(name, "hdpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_HIGH;
+        return true;
+    }
+
+    if (strcmp(name, "xhdpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_XHIGH;
+        return true;
+    }
+
+    if (strcmp(name, "xxhdpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_XXHIGH;
+        return true;
+    }
+
+    if (strcmp(name, "xxxhdpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
+        return true;
+    }
+
+    char* c = (char*)name;
+    while (*c >= '0' && *c <= '9') {
+        c++;
+    }
+
+    // check that we have 'dpi' after the last digit.
+    if (toupper(c[0]) != 'D' ||
+            toupper(c[1]) != 'P' ||
+            toupper(c[2]) != 'I' ||
+            c[3] != 0) {
+        return false;
+    }
+
+    // temporarily replace the first letter with \0 to
+    // use atoi.
+    char tmp = c[0];
+    c[0] = '\0';
+
+    int d = atoi(name);
+    c[0] = tmp;
+
+    if (d != 0) {
+        if (out) out->density = d;
+        return true;
+    }
+
+    return false;
+}
+
+static bool parseTouchscreen(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
+        return true;
+    } else if (strcmp(name, "notouch") == 0) {
+        if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
+        return true;
+    } else if (strcmp(name, "stylus") == 0) {
+        if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
+        return true;
+    } else if (strcmp(name, "finger") == 0) {
+        if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
+        return true;
+    }
+
+    return false;
+}
+
+static bool parseKeysHidden(const char* name, ResTable_config* out) {
+    uint8_t mask = 0;
+    uint8_t value = 0;
+    if (strcmp(name, kWildcardName) == 0) {
+        mask = ResTable_config::MASK_KEYSHIDDEN;
+        value = ResTable_config::KEYSHIDDEN_ANY;
+    } else if (strcmp(name, "keysexposed") == 0) {
+        mask = ResTable_config::MASK_KEYSHIDDEN;
+        value = ResTable_config::KEYSHIDDEN_NO;
+    } else if (strcmp(name, "keyshidden") == 0) {
+        mask = ResTable_config::MASK_KEYSHIDDEN;
+        value = ResTable_config::KEYSHIDDEN_YES;
+    } else if (strcmp(name, "keyssoft") == 0) {
+        mask = ResTable_config::MASK_KEYSHIDDEN;
+        value = ResTable_config::KEYSHIDDEN_SOFT;
+    }
+
+    if (mask != 0) {
+        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
+        return true;
+    }
+
+    return false;
+}
+
+static bool parseKeyboard(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->keyboard = out->KEYBOARD_ANY;
+        return true;
+    } else if (strcmp(name, "nokeys") == 0) {
+        if (out) out->keyboard = out->KEYBOARD_NOKEYS;
+        return true;
+    } else if (strcmp(name, "qwerty") == 0) {
+        if (out) out->keyboard = out->KEYBOARD_QWERTY;
+        return true;
+    } else if (strcmp(name, "12key") == 0) {
+        if (out) out->keyboard = out->KEYBOARD_12KEY;
+        return true;
+    }
+
+    return false;
+}
+
+static bool parseNavHidden(const char* name, ResTable_config* out) {
+    uint8_t mask = 0;
+    uint8_t value = 0;
+    if (strcmp(name, kWildcardName) == 0) {
+        mask = ResTable_config::MASK_NAVHIDDEN;
+        value = ResTable_config::NAVHIDDEN_ANY;
+    } else if (strcmp(name, "navexposed") == 0) {
+        mask = ResTable_config::MASK_NAVHIDDEN;
+        value = ResTable_config::NAVHIDDEN_NO;
+    } else if (strcmp(name, "navhidden") == 0) {
+        mask = ResTable_config::MASK_NAVHIDDEN;
+        value = ResTable_config::NAVHIDDEN_YES;
+    }
+
+    if (mask != 0) {
+        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
+        return true;
+    }
+
+    return false;
+}
+
+static bool parseNavigation(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->navigation = out->NAVIGATION_ANY;
+        return true;
+    } else if (strcmp(name, "nonav") == 0) {
+        if (out) out->navigation = out->NAVIGATION_NONAV;
+        return true;
+    } else if (strcmp(name, "dpad") == 0) {
+        if (out) out->navigation = out->NAVIGATION_DPAD;
+        return true;
+    } else if (strcmp(name, "trackball") == 0) {
+        if (out) out->navigation = out->NAVIGATION_TRACKBALL;
+        return true;
+    } else if (strcmp(name, "wheel") == 0) {
+        if (out) out->navigation = out->NAVIGATION_WHEEL;
+        return true;
+    }
+
+    return false;
+}
+
+static bool parseScreenSize(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) {
+            out->screenWidth = out->SCREENWIDTH_ANY;
+            out->screenHeight = out->SCREENHEIGHT_ANY;
+        }
+        return true;
+    }
+
+    const char* x = name;
+    while (*x >= '0' && *x <= '9') x++;
+    if (x == name || *x != 'x') return false;
+    std::string xName(name, x-name);
+    x++;
+
+    const char* y = x;
+    while (*y >= '0' && *y <= '9') y++;
+    if (y == name || *y != 0) return false;
+    std::string yName(x, y-x);
+
+    uint16_t w = (uint16_t)atoi(xName.c_str());
+    uint16_t h = (uint16_t)atoi(yName.c_str());
+    if (w < h) {
+        return false;
+    }
+
+    if (out) {
+        out->screenWidth = w;
+        out->screenHeight = h;
+    }
+
+    return true;
+}
+
+static bool parseSmallestScreenWidthDp(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) {
+            out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
+        }
+        return true;
+    }
+
+    if (*name != 's') return false;
+    name++;
+    if (*name != 'w') return false;
+    name++;
+    const char* x = name;
+    while (*x >= '0' && *x <= '9') x++;
+    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+    std::string xName(name, x-name);
+
+    if (out) {
+        out->smallestScreenWidthDp = (uint16_t)atoi(xName.c_str());
+    }
+
+    return true;
+}
+
+static bool parseScreenWidthDp(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) {
+            out->screenWidthDp = out->SCREENWIDTH_ANY;
+        }
+        return true;
+    }
+
+    if (*name != 'w') return false;
+    name++;
+    const char* x = name;
+    while (*x >= '0' && *x <= '9') x++;
+    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+    std::string xName(name, x-name);
+
+    if (out) {
+        out->screenWidthDp = (uint16_t)atoi(xName.c_str());
+    }
+
+    return true;
+}
+
+static bool parseScreenHeightDp(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) {
+            out->screenHeightDp = out->SCREENWIDTH_ANY;
+        }
+        return true;
+    }
+
+    if (*name != 'h') return false;
+    name++;
+    const char* x = name;
+    while (*x >= '0' && *x <= '9') x++;
+    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+    std::string xName(name, x-name);
+
+    if (out) {
+        out->screenHeightDp = (uint16_t)atoi(xName.c_str());
+    }
+
+    return true;
+}
+
+static bool parseVersion(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) {
+            out->sdkVersion = out->SDKVERSION_ANY;
+            out->minorVersion = out->MINORVERSION_ANY;
+        }
+        return true;
+    }
+
+    if (*name != 'v') {
+        return false;
+    }
+
+    name++;
+    const char* s = name;
+    while (*s >= '0' && *s <= '9') s++;
+    if (s == name || *s != 0) return false;
+    std::string sdkName(name, s-name);
+
+    if (out) {
+        out->sdkVersion = (uint16_t)atoi(sdkName.c_str());
+        out->minorVersion = 0;
+    }
+
+    return true;
+}
+
+bool ConfigDescription::parse(const StringPiece& str, ConfigDescription* out) {
+    std::vector<std::string> parts = util::splitAndLowercase(str, '-');
+
+    ConfigDescription config;
+    ssize_t partsConsumed = 0;
+    LocaleValue locale;
+
+    const auto partsEnd = parts.end();
+    auto partIter = parts.begin();
+
+    if (str.size() == 0) {
+        goto success;
+    }
+
+    if (parseMcc(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    if (parseMnc(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    // Locale spans a few '-' separators, so we let it
+    // control the index.
+    partsConsumed = locale.initFromParts(partIter, partsEnd);
+    if (partsConsumed < 0) {
+        return false;
+    } else {
+        locale.writeTo(&config);
+        partIter += partsConsumed;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    if (parseLayoutDirection(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    if (parseSmallestScreenWidthDp(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    if (parseScreenWidthDp(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    if (parseScreenHeightDp(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    if (parseScreenLayoutSize(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    if (parseScreenLayoutLong(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    if (parseOrientation(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    if (parseUiModeType(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    if (parseUiModeNight(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    if (parseDensity(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    if (parseTouchscreen(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    if (parseKeysHidden(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    if (parseKeyboard(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    if (parseNavHidden(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    if (parseNavigation(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    if (parseScreenSize(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    if (parseVersion(partIter->c_str(), &config)) {
+        ++partIter;
+        if (partIter == partsEnd) {
+            goto success;
+        }
+    }
+
+    // Unrecognized.
+    return false;
+
+success:
+    if (out != NULL) {
+        applyVersionForCompatibility(&config);
+        *out = config;
+    }
+    return true;
+}
+
+void ConfigDescription::applyVersionForCompatibility(ConfigDescription* config) {
+    uint16_t minSdk = 0;
+    if (config->density == ResTable_config::DENSITY_ANY) {
+        minSdk = SDK_LOLLIPOP;
+    } else if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
+            || config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY
+            || config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
+        minSdk = SDK_HONEYCOMB_MR2;
+    } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE)
+                != ResTable_config::UI_MODE_TYPE_ANY
+            ||  (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT)
+                != ResTable_config::UI_MODE_NIGHT_ANY) {
+        minSdk = SDK_FROYO;
+    } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE)
+                != ResTable_config::SCREENSIZE_ANY
+            ||  (config->screenLayout & ResTable_config::MASK_SCREENLONG)
+                != ResTable_config::SCREENLONG_ANY
+            || config->density != ResTable_config::DENSITY_DEFAULT) {
+        minSdk = SDK_DONUT;
+    }
+
+    if (minSdk > config->sdkVersion) {
+        config->sdkVersion = minSdk;
+    }
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
new file mode 100644
index 0000000..67b4b75
--- /dev/null
+++ b/tools/aapt2/ConfigDescription.h
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_CONFIG_DESCRIPTION_H
+#define AAPT_CONFIG_DESCRIPTION_H
+
+#include "StringPiece.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <ostream>
+
+namespace aapt {
+
+/*
+ * Subclass of ResTable_config that adds convenient
+ * initialization and comparison methods.
+ */
+struct ConfigDescription : public android::ResTable_config {
+    /*
+     * Parse a string of the form 'fr-sw600dp-land' and fill in the
+     * given ResTable_config with resulting configuration parameters.
+     *
+     * The resulting configuration has the appropriate sdkVersion defined
+     * for backwards compatibility.
+     */
+    static bool parse(const StringPiece& str, ConfigDescription* out = nullptr);
+
+    /**
+     * If the configuration uses an axis that was added after
+     * the original Android release, make sure the SDK version
+     * is set accordingly.
+     */
+    static void applyVersionForCompatibility(ConfigDescription* config);
+
+    ConfigDescription();
+    ConfigDescription(const android::ResTable_config& o);
+    ConfigDescription(const ConfigDescription& o);
+    ConfigDescription(ConfigDescription&& o);
+
+    ConfigDescription& operator=(const android::ResTable_config& o);
+    ConfigDescription& operator=(const ConfigDescription& o);
+    ConfigDescription& operator=(ConfigDescription&& o);
+
+    bool operator<(const ConfigDescription& o) const;
+    bool operator<=(const ConfigDescription& o) const;
+    bool operator==(const ConfigDescription& o) const;
+    bool operator!=(const ConfigDescription& o) const;
+    bool operator>=(const ConfigDescription& o) const;
+    bool operator>(const ConfigDescription& o) const;
+};
+
+inline ConfigDescription::ConfigDescription() {
+    memset(this, 0, sizeof(*this));
+    size = sizeof(android::ResTable_config);
+}
+
+inline ConfigDescription::ConfigDescription(const android::ResTable_config& o) {
+    *static_cast<android::ResTable_config*>(this) = o;
+    size = sizeof(android::ResTable_config);
+}
+
+inline ConfigDescription::ConfigDescription(const ConfigDescription& o) {
+    *static_cast<android::ResTable_config*>(this) = o;
+}
+
+inline ConfigDescription::ConfigDescription(ConfigDescription&& o) {
+    *this = o;
+}
+
+inline ConfigDescription& ConfigDescription::operator=(const android::ResTable_config& o) {
+    *static_cast<android::ResTable_config*>(this) = o;
+    size = sizeof(android::ResTable_config);
+    return *this;
+}
+
+inline ConfigDescription& ConfigDescription::operator=(const ConfigDescription& o) {
+    *static_cast<android::ResTable_config*>(this) = o;
+    return *this;
+}
+
+inline ConfigDescription& ConfigDescription::operator=(ConfigDescription&& o) {
+    *this = o;
+    return *this;
+}
+
+inline bool ConfigDescription::operator<(const ConfigDescription& o) const {
+    return compare(o) < 0;
+}
+
+inline bool ConfigDescription::operator<=(const ConfigDescription& o) const {
+    return compare(o) <= 0;
+}
+
+inline bool ConfigDescription::operator==(const ConfigDescription& o) const {
+    return compare(o) == 0;
+}
+
+inline bool ConfigDescription::operator!=(const ConfigDescription& o) const {
+    return compare(o) != 0;
+}
+
+inline bool ConfigDescription::operator>=(const ConfigDescription& o) const {
+    return compare(o) >= 0;
+}
+
+inline bool ConfigDescription::operator>(const ConfigDescription& o) const {
+    return compare(o) > 0;
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out, const ConfigDescription& o) {
+    return out << o.toString().string();
+}
+
+} // namespace aapt
+
+#endif // AAPT_CONFIG_DESCRIPTION_H
diff --git a/tools/aapt2/ConfigDescription_test.cpp b/tools/aapt2/ConfigDescription_test.cpp
new file mode 100644
index 0000000..c57e351
--- /dev/null
+++ b/tools/aapt2/ConfigDescription_test.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#include "ConfigDescription.h"
+#include "StringPiece.h"
+
+#include <gtest/gtest.h>
+#include <string>
+
+namespace aapt {
+
+static ::testing::AssertionResult TestParse(const StringPiece& input,
+                                            ConfigDescription* config = nullptr) {
+    if (ConfigDescription::parse(input, config)) {
+        return ::testing::AssertionSuccess() << input << " was successfully parsed";
+    }
+    return ::testing::AssertionFailure() << input << " could not be parsed";
+}
+
+TEST(ConfigDescriptionTest, ParseFailWhenQualifiersAreOutOfOrder) {
+    EXPECT_FALSE(TestParse("en-sw600dp-ldrtl"));
+    EXPECT_FALSE(TestParse("land-en"));
+    EXPECT_FALSE(TestParse("hdpi-320dpi"));
+}
+
+TEST(ConfigDescriptionTest, ParseFailWhenQualifiersAreNotMatched) {
+    EXPECT_FALSE(TestParse("en-sw600dp-ILLEGAL"));
+}
+
+TEST(ConfigDescriptionTest, ParseFailWhenQualifiersHaveTrailingDash) {
+    EXPECT_FALSE(TestParse("en-sw600dp-land-"));
+}
+
+TEST(ConfigDescriptionTest, ParseBasicQualifiers) {
+    ConfigDescription config;
+    EXPECT_TRUE(TestParse("", &config));
+    EXPECT_EQ(std::string(""), config.toString().string());
+
+    EXPECT_TRUE(TestParse("fr-land", &config));
+    EXPECT_EQ(std::string("fr-land"), config.toString().string());
+
+    EXPECT_TRUE(TestParse("mcc310-pl-sw720dp-normal-long-port-night-"
+                "xhdpi-keyssoft-qwerty-navexposed-nonav", &config));
+    EXPECT_EQ(std::string("mcc310-pl-sw720dp-normal-long-port-night-"
+                "xhdpi-keyssoft-qwerty-navexposed-nonav-v13"), config.toString().string());
+}
+
+TEST(ConfigDescriptionTest, ParseLocales) {
+    ConfigDescription config;
+    EXPECT_TRUE(TestParse("en-rUS", &config));
+    EXPECT_EQ(std::string("en-rUS"), config.toString().string());
+}
+
+TEST(ConfigDescriptionTest, ParseQualifierAddedInApi13) {
+    ConfigDescription config;
+    EXPECT_TRUE(TestParse("sw600dp", &config));
+    EXPECT_EQ(std::string("sw600dp-v13"), config.toString().string());
+
+    EXPECT_TRUE(TestParse("sw600dp-v8", &config));
+    EXPECT_EQ(std::string("sw600dp-v13"), config.toString().string());
+}
+
+TEST(ConfigDescriptionTest, ParseCarAttribute) {
+    ConfigDescription config;
+    EXPECT_TRUE(TestParse("car", &config));
+    EXPECT_EQ(android::ResTable_config::UI_MODE_TYPE_CAR, config.uiMode);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/Files.cpp b/tools/aapt2/Files.cpp
new file mode 100644
index 0000000..c910c81
--- /dev/null
+++ b/tools/aapt2/Files.cpp
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+
+#include "Files.h"
+#include "Util.h"
+
+#include <cerrno>
+#include <dirent.h>
+#include <string>
+#include <sys/stat.h>
+
+namespace aapt {
+
+FileType getFileType(const StringPiece& path) {
+    struct stat sb;
+    if (stat(path.data(), &sb) < 0) {
+        if (errno == ENOENT || errno == ENOTDIR) {
+            return FileType::kNonexistant;
+        }
+        return FileType::kUnknown;
+    }
+
+    if (S_ISREG(sb.st_mode)) {
+        return FileType::kRegular;
+    } else if (S_ISDIR(sb.st_mode)) {
+        return FileType::kDirectory;
+    } else if (S_ISCHR(sb.st_mode)) {
+        return FileType::kCharDev;
+    } else if (S_ISBLK(sb.st_mode)) {
+        return FileType::kBlockDev;
+    } else if (S_ISFIFO(sb.st_mode)) {
+        return FileType::kFifo;
+    } else if (S_ISLNK(sb.st_mode)) {
+        return FileType::kSymlink;
+    } else if (S_ISSOCK(sb.st_mode)) {
+        return FileType::kSocket;
+    } else {
+        return FileType::kUnknown;
+    }
+}
+
+std::vector<std::string> listFiles(const StringPiece& root) {
+    DIR* dir = opendir(root.data());
+    if (dir == nullptr) {
+        Logger::error(Source{ root.toString() })
+            << "unable to open file: "
+            << strerror(errno)
+            << "."
+            << std::endl;
+        return {};
+    }
+
+    std::vector<std::string> files;
+    dirent* entry;
+    while ((entry = readdir(dir))) {
+        files.emplace_back(entry->d_name);
+    }
+
+    closedir(dir);
+    return files;
+}
+
+inline static int mkdirImpl(const StringPiece& path) {
+#ifdef HAVE_MS_C_RUNTIME
+    return _mkdir(path.toString().c_str());
+#else
+    return mkdir(path.toString().c_str(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
+#endif
+}
+
+bool mkdirs(const StringPiece& path) {
+    const char* start = path.begin();
+    const char* end = path.end();
+    for (const char* current = start; current != end; ++current) {
+        if (*current == sDirSep) {
+            StringPiece parentPath(start, current - start);
+            int result = mkdirImpl(parentPath);
+            if (result < 0 && errno != EEXIST) {
+                return false;
+            }
+        }
+    }
+    return mkdirImpl(path) == 0 || errno == EEXIST;
+}
+
+bool FileFilter::setPattern(const StringPiece& pattern) {
+    mPatternTokens = util::splitAndLowercase(pattern, ':');
+    return true;
+}
+
+bool FileFilter::operator()(const std::string& filename, FileType type) const {
+    if (filename == "." || filename == "..") {
+        return false;
+    }
+
+    const char kDir[] = "dir";
+    const char kFile[] = "file";
+    const size_t filenameLen = filename.length();
+    bool chatty = true;
+    for (const std::string& token : mPatternTokens) {
+        const char* tokenStr = token.c_str();
+        if (*tokenStr == '!') {
+            chatty = false;
+            tokenStr++;
+        }
+
+        if (strncasecmp(tokenStr, kDir, sizeof(kDir)) == 0) {
+            if (type != FileType::kDirectory) {
+                continue;
+            }
+            tokenStr += sizeof(kDir);
+        }
+
+        if (strncasecmp(tokenStr, kFile, sizeof(kFile)) == 0) {
+            if (type != FileType::kRegular) {
+                continue;
+            }
+            tokenStr += sizeof(kFile);
+        }
+
+        bool ignore = false;
+        size_t n = strlen(tokenStr);
+        if (*tokenStr == '*') {
+            // Math suffix.
+            tokenStr++;
+            n--;
+            if (n <= filenameLen) {
+                ignore = strncasecmp(tokenStr, filename.c_str() + filenameLen - n, n) == 0;
+            }
+        } else if (n > 1 && tokenStr[n - 1] == '*') {
+            // Match prefix.
+            ignore = strncasecmp(tokenStr, filename.c_str(), n - 1) == 0;
+        } else {
+            ignore = strcasecmp(tokenStr, filename.c_str()) == 0;
+        }
+
+        if (ignore) {
+            if (chatty) {
+                Logger::warn()
+                    << "skipping " <<
+                    (type == FileType::kDirectory ? "dir '" : "file '")
+                    << filename
+                    << "' due to ignore pattern '"
+                    << token
+                    << "'."
+                    << std::endl;
+            }
+            return false;
+        }
+    }
+    return true;
+}
+
+
+} // namespace aapt
diff --git a/tools/aapt2/Files.h b/tools/aapt2/Files.h
new file mode 100644
index 0000000..e5e196e
--- /dev/null
+++ b/tools/aapt2/Files.h
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_FILES_H
+#define AAPT_FILES_H
+
+#include "Logger.h"
+#include "Source.h"
+#include "StringPiece.h"
+
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+#ifdef _WIN32
+constexpr const char sDirSep = '\\';
+#else
+constexpr const char sDirSep = '/';
+#endif
+
+enum class FileType {
+    kUnknown = 0,
+    kNonexistant,
+    kRegular,
+    kDirectory,
+    kCharDev,
+    kBlockDev,
+    kFifo,
+    kSymlink,
+    kSocket,
+};
+
+FileType getFileType(const StringPiece& path);
+
+/*
+ * Lists files under the directory `root`. Files are listed
+ * with just their leaf (filename) names.
+ */
+std::vector<std::string> listFiles(const StringPiece& root);
+
+/*
+ * Appends a path to `base`, separated by the directory separator.
+ */
+void appendPath(std::string* base, const StringPiece& part);
+
+/*
+ * Appends a series of paths to `base`, separated by the
+ * system directory separator.
+ */
+template <typename... Ts >
+void appendPath(std::string* base, const StringPiece& part, const Ts&... parts);
+
+/*
+ * Makes all the directories in `path`. The last element in the path
+ * is interpreted as a directory.
+ */
+bool mkdirs(const StringPiece& path);
+
+/*
+ * Filter that determines which resource files/directories are
+ * processed by AAPT. Takes a pattern string supplied by the user.
+ * Pattern format is specified in the
+ * FileFilter::setPattern(const std::string&) method.
+ */
+class FileFilter {
+public:
+    /*
+     * Patterns syntax:
+     * - Delimiter is :
+     * - Entry can start with the flag ! to avoid printing a warning
+     *   about the file being ignored.
+     * - Entry can have the flag "<dir>" to match only directories
+     *   or <file> to match only files. Default is to match both.
+     * - Entry can be a simplified glob "<prefix>*" or "*<suffix>"
+     *   where prefix/suffix must have at least 1 character (so that
+     *   we don't match a '*' catch-all pattern.)
+     * - The special filenames "." and ".." are always ignored.
+     * - Otherwise the full string is matched.
+     * - match is not case-sensitive.
+     */
+    bool setPattern(const StringPiece& pattern);
+
+    /**
+     * Applies the filter, returning true for pass, false for fail.
+     */
+    bool operator()(const std::string& filename, FileType type) const;
+
+private:
+    std::vector<std::string> mPatternTokens;
+};
+
+inline void appendPath(std::string* base, const StringPiece& part) {
+    assert(base);
+    *base += sDirSep;
+    base->append(part.data(), part.size());
+}
+
+template <typename... Ts >
+void appendPath(std::string* base, const StringPiece& part, const Ts&... parts) {
+    assert(base);
+    *base += sDirSep;
+    base->append(part.data(), part.size());
+    appendPath(base, parts...);
+}
+
+} // namespace aapt
+
+#endif // AAPT_FILES_H
diff --git a/tools/aapt2/JavaClassGenerator.cpp b/tools/aapt2/JavaClassGenerator.cpp
new file mode 100644
index 0000000..7ec2848
--- /dev/null
+++ b/tools/aapt2/JavaClassGenerator.cpp
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ */
+
+#include "JavaClassGenerator.h"
+#include "Resource.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "StringPiece.h"
+
+#include <ostream>
+#include <set>
+#include <sstream>
+#include <tuple>
+
+namespace aapt {
+
+// The number of attributes to emit per line in a Styleable array.
+constexpr size_t kAttribsPerLine = 4;
+
+JavaClassGenerator::JavaClassGenerator(std::shared_ptr<const ResourceTable> table,
+                                       Options options) :
+        mTable(table), mOptions(options) {
+}
+
+static void generateHeader(std::ostream& out, const StringPiece16& package) {
+    out << "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
+           " *\n"
+           " * This class was automatically generated by the\n"
+           " * aapt tool from the resource data it found. It\n"
+           " * should not be modified by hand.\n"
+           " */\n\n";
+    out << "package " << package << ";"
+        << std::endl
+        << std::endl;
+}
+
+static const std::set<StringPiece16> sJavaIdentifiers = {
+    u"abstract", u"assert", u"boolean", u"break", u"byte",
+    u"case", u"catch", u"char", u"class", u"const", u"continue",
+    u"default", u"do", u"double", u"else", u"enum", u"extends",
+    u"final", u"finally", u"float", u"for", u"goto", u"if",
+    u"implements", u"import", u"instanceof", u"int", u"interface",
+    u"long", u"native", u"new", u"package", u"private", u"protected",
+    u"public", u"return", u"short", u"static", u"strictfp", u"super",
+    u"switch", u"synchronized", u"this", u"throw", u"throws",
+    u"transient", u"try", u"void", u"volatile", u"while", u"true",
+    u"false", u"null"
+};
+
+static bool isValidSymbol(const StringPiece16& symbol) {
+    return sJavaIdentifiers.find(symbol) == sJavaIdentifiers.end();
+}
+
+/*
+ * Java symbols can not contain . or -, but those are valid in a resource name.
+ * Replace those with '_'.
+ */
+static std::u16string transform(const StringPiece16& symbol) {
+    std::u16string output = symbol.toString();
+    for (char16_t& c : output) {
+        if (c == u'.' || c == u'-') {
+            c = u'_';
+        }
+    }
+    return output;
+}
+
+bool JavaClassGenerator::generateType(std::ostream& out, const ResourceTableType& type,
+                                      size_t packageId) {
+    const StringPiece finalModifier = mOptions.useFinal ? " final" : "";
+
+    for (const auto& entry : type.entries) {
+        ResourceId id = { packageId, type.typeId, entry->entryId };
+        assert(id.isValid());
+
+        if (!isValidSymbol(entry->name)) {
+            mError = (std::stringstream()
+                    << "invalid symbol name '"
+                    << StringPiece16(entry->name)
+                    << "'").str();
+            return false;
+        }
+
+        out << "        "
+            << "public static" << finalModifier
+            << " int " << transform(entry->name) << " = " << id << ";" << std::endl;
+    }
+    return true;
+}
+
+struct GenArgs : ValueVisitorArgs {
+    GenArgs(std::ostream& o, const ResourceEntry& e) : out(o), entry(e) {
+    }
+
+    std::ostream& out;
+    const ResourceEntry& entry;
+};
+
+void JavaClassGenerator::visit(const Styleable& styleable, ValueVisitorArgs& a) {
+    const StringPiece finalModifier = mOptions.useFinal ? " final" : "";
+    std::ostream& out = static_cast<GenArgs&>(a).out;
+    const ResourceEntry& entry = static_cast<GenArgs&>(a).entry;
+
+    // This must be sorted by resource ID.
+    std::vector<std::pair<ResourceId, StringPiece16>> sortedAttributes;
+    sortedAttributes.reserve(styleable.entries.size());
+    for (const auto& attr : styleable.entries) {
+        assert(attr.id.isValid() && "no ID set for Styleable entry");
+        assert(attr.name.isValid() && "no name set for Styleable entry");
+        sortedAttributes.emplace_back(attr.id, attr.name.entry);
+    }
+    std::sort(sortedAttributes.begin(), sortedAttributes.end());
+
+    // First we emit the array containing the IDs of each attribute.
+    out << "        "
+        << "public static final int[] " << transform(entry.name) << " = {";
+
+    const size_t attrCount = sortedAttributes.size();
+    for (size_t i = 0; i < attrCount; i++) {
+        if (i % kAttribsPerLine == 0) {
+            out << std::endl << "            ";
+        }
+
+        out << sortedAttributes[i].first;
+        if (i != attrCount - 1) {
+            out << ", ";
+        }
+    }
+    out << std::endl << "        };" << std::endl;
+
+    // Now we emit the indices into the array.
+    for (size_t i = 0; i < attrCount; i++) {
+        out << "        "
+            << "public static" << finalModifier
+            << " int " << transform(entry.name) << "_" << transform(sortedAttributes[i].second)
+            << " = " << i << ";" << std::endl;
+    }
+}
+
+bool JavaClassGenerator::generate(std::ostream& out) {
+    const size_t packageId = mTable->getPackageId();
+
+    generateHeader(out, mTable->getPackage());
+
+    out << "public final class R {" << std::endl;
+
+    for (const auto& type : *mTable) {
+        out << "    public static final class " << type->type << " {" << std::endl;
+        bool result;
+        if (type->type == ResourceType::kStyleable) {
+            for (const auto& entry : type->entries) {
+                assert(!entry->values.empty());
+                if (!isValidSymbol(entry->name)) {
+                    mError = (std::stringstream()
+                            << "invalid symbol name '"
+                            << StringPiece16(entry->name)
+                            << "'").str();
+                    return false;
+                }
+                entry->values.front().value->accept(*this, GenArgs{ out, *entry });
+            }
+        } else {
+            result = generateType(out, *type, packageId);
+        }
+
+        if (!result) {
+            return false;
+        }
+        out << "    }" << std::endl;
+    }
+
+    out << "}" << std::endl;
+    return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/JavaClassGenerator.h b/tools/aapt2/JavaClassGenerator.h
new file mode 100644
index 0000000..5b8e500
--- /dev/null
+++ b/tools/aapt2/JavaClassGenerator.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_JAVA_CLASS_GENERATOR_H
+#define AAPT_JAVA_CLASS_GENERATOR_H
+
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+
+#include <ostream>
+#include <string>
+
+namespace aapt {
+
+/*
+ * Generates the R.java file for a resource table.
+ */
+class JavaClassGenerator : ConstValueVisitor {
+public:
+    /*
+     * A set of options for this JavaClassGenerator.
+     */
+    struct Options {
+        /*
+         * Specifies whether to use the 'final' modifier
+         * on resource entries. Default is true.
+         */
+        bool useFinal = true;
+    };
+
+    JavaClassGenerator(std::shared_ptr<const ResourceTable> table, Options options);
+
+    /*
+     * Writes the R.java file to `out`. Returns true on success.
+     */
+    bool generate(std::ostream& out);
+
+    /*
+     * ConstValueVisitor implementation.
+     */
+    void visit(const Styleable& styleable, ValueVisitorArgs& args);
+
+    const std::string& getError() const;
+
+private:
+    bool generateType(std::ostream& out, const ResourceTableType& type, size_t packageId);
+
+    std::shared_ptr<const ResourceTable> mTable;
+    Options mOptions;
+    std::string mError;
+};
+
+inline const std::string& JavaClassGenerator::getError() const {
+    return mError;
+}
+
+} // namespace aapt
+
+#endif // AAPT_JAVA_CLASS_GENERATOR_H
diff --git a/tools/aapt2/JavaClassGenerator_test.cpp b/tools/aapt2/JavaClassGenerator_test.cpp
new file mode 100644
index 0000000..32050e3
--- /dev/null
+++ b/tools/aapt2/JavaClassGenerator_test.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#include "JavaClassGenerator.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "Util.h"
+
+#include <gtest/gtest.h>
+#include <sstream>
+#include <string>
+
+namespace aapt {
+
+struct JavaClassGeneratorTest : public ::testing::Test {
+    virtual void SetUp() override {
+        mTable = std::make_shared<ResourceTable>();
+        mTable->setPackage(u"android");
+        mTable->setPackageId(0x01);
+    }
+
+    bool addResource(const ResourceNameRef& name, ResourceId id) {
+        return mTable->addResource(name, id, {}, SourceLine{ "test.xml", 21 },
+                                   util::make_unique<Id>());
+    }
+
+    std::shared_ptr<ResourceTable> mTable;
+};
+
+TEST_F(JavaClassGeneratorTest, FailWhenEntryIsJavaKeyword) {
+    ASSERT_TRUE(addResource(ResourceName{ {}, ResourceType::kId, u"class" },
+                            ResourceId{ 0x01, 0x02, 0x0000 }));
+
+    JavaClassGenerator generator(mTable, {});
+
+    std::stringstream out;
+    EXPECT_FALSE(generator.generate(out));
+}
+
+TEST_F(JavaClassGeneratorTest, TransformInvalidJavaIdentifierCharacter) {
+    ASSERT_TRUE(addResource(ResourceName{ {}, ResourceType::kId, u"hey-man" },
+                            ResourceId{ 0x01, 0x02, 0x0000 }));
+
+    ASSERT_TRUE(addResource(ResourceName{ {}, ResourceType::kAttr, u"cool.attr" },
+                            ResourceId{ 0x01, 0x01, 0x0000 }));
+
+    std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
+    Reference ref(ResourceName{ u"android", ResourceType::kAttr, u"cool.attr"});
+    ref.id = ResourceId{ 0x01, 0x01, 0x0000 };
+    styleable->entries.emplace_back(ref);
+
+    ASSERT_TRUE(mTable->addResource(ResourceName{ {}, ResourceType::kStyleable, u"hey.dude" },
+                                    ResourceId{ 0x01, 0x03, 0x0000 }, {},
+                                    SourceLine{ "test.xml", 21 }, std::move(styleable)));
+
+    JavaClassGenerator generator(mTable, {});
+
+    std::stringstream out;
+    EXPECT_TRUE(generator.generate(out));
+    std::string output = out.str();
+
+    EXPECT_NE(std::string::npos,
+              output.find("public static final int hey_man = 0x01020000;"));
+
+    EXPECT_NE(std::string::npos,
+              output.find("public static final int[] hey_dude = {"));
+
+    EXPECT_NE(std::string::npos,
+              output.find("public static final int hey_dude_cool_attr = 0;"));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/Linker.cpp b/tools/aapt2/Linker.cpp
new file mode 100644
index 0000000..a863197
--- /dev/null
+++ b/tools/aapt2/Linker.cpp
@@ -0,0 +1,282 @@
+/*
+ * 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.
+ */
+
+#include "Linker.h"
+#include "Logger.h"
+#include "ResourceParser.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "StringPiece.h"
+#include "Util.h"
+
+#include <androidfw/AssetManager.h>
+#include <array>
+#include <iostream>
+#include <map>
+#include <ostream>
+#include <set>
+#include <sstream>
+#include <tuple>
+#include <vector>
+
+namespace aapt {
+
+Linker::Args::Args(const ResourceNameRef& r, const SourceLine& s) : referrer(r), source(s) {
+}
+
+Linker::Linker(std::shared_ptr<ResourceTable> table, std::shared_ptr<Resolver> resolver) :
+        mTable(table), mResolver(resolver), mError(false) {
+}
+
+bool Linker::linkAndValidate() {
+    std::bitset<256> usedTypeIds;
+    std::array<std::set<uint16_t>, 256> usedIds;
+    usedTypeIds.set(0);
+
+    // First build the graph of references.
+    for (auto& type : *mTable) {
+        if (type->typeId != ResourceTableType::kUnsetTypeId) {
+            // The ID for this type has already been set. We
+            // mark this ID as taken so we don't re-assign it
+            // later.
+            usedTypeIds.set(type->typeId);
+        }
+
+        for (auto& entry : type->entries) {
+            if (type->typeId != ResourceTableType::kUnsetTypeId &&
+                    entry->entryId != ResourceEntry::kUnsetEntryId) {
+                // The ID for this entry has already been set. We
+                // mark this ID as taken so we don't re-assign it
+                // later.
+                usedIds[type->typeId].insert(entry->entryId);
+            }
+
+            for (auto& valueConfig : entry->values) {
+                // Dispatch to the right method of this linker
+                // based on the value's type.
+                valueConfig.value->accept(*this, Args{
+                        ResourceNameRef{ mTable->getPackage(), type->type, entry->name },
+                        valueConfig.source
+                });
+            }
+        }
+    }
+
+    /*
+     * Assign resource IDs that are available.
+     */
+    size_t nextTypeIndex = 0;
+    for (auto& type : *mTable) {
+        if (type->typeId == ResourceTableType::kUnsetTypeId) {
+            while (nextTypeIndex < usedTypeIds.size() && usedTypeIds[nextTypeIndex]) {
+                nextTypeIndex++;
+            }
+            type->typeId = nextTypeIndex++;
+        }
+
+        const auto endEntryIter = std::end(usedIds[type->typeId]);
+        auto nextEntryIter = std::begin(usedIds[type->typeId]);
+        size_t nextIndex = 0;
+        for (auto& entry : type->entries) {
+            if (entry->entryId == ResourceTableType::kUnsetTypeId) {
+                while (nextEntryIter != endEntryIter &&
+                        nextIndex == *nextEntryIter) {
+                    nextIndex++;
+                    ++nextEntryIter;
+                }
+                entry->entryId = nextIndex++;
+
+                // Update callers of this resource with the right ID.
+                auto callersIter = mGraph.find(ResourceNameRef{
+                        mTable->getPackage(),
+                        type->type,
+                        entry->name
+                });
+
+                if (callersIter != std::end(mGraph)) {
+                    for (Node& caller : callersIter->second) {
+                        caller.reference->id = ResourceId(mTable->getPackageId(),
+                                                          type->typeId,
+                                                          entry->entryId);
+                    }
+                }
+            }
+        }
+    }
+
+    return !mError;
+}
+
+const Linker::ResourceNameToSourceMap& Linker::getUnresolvedReferences() const {
+    return mUnresolvedSymbols;
+}
+
+void Linker::visit(Reference& reference, ValueVisitorArgs& a) {
+    Args& args = static_cast<Args&>(a);
+
+    Maybe<ResourceId> result = mResolver->findId(reference.name);
+    if (!result) {
+        addUnresolvedSymbol(reference.name, args.source);
+        return;
+    }
+
+    const ResourceId& id = result.value();
+    if (id.isValid()) {
+        reference.id = id;
+    } else {
+        // We need to update the ID when it is set, so add it
+        // to the graph.
+        mGraph[reference.name].push_back(Node{
+                args.referrer,
+                args.source.path,
+                args.source.line,
+                &reference
+        });
+    }
+
+    // TODO(adamlesinski): Verify the referencedType is another reference
+    // or a compatible primitive.
+}
+
+void Linker::processAttributeValue(const ResourceNameRef& name, const SourceLine& source,
+        const Attribute& attr, std::unique_ptr<Item>& value) {
+    std::unique_ptr<Item> convertedValue;
+    visitFunc<RawString>(*value, [&](RawString& str) {
+        // This is a raw string, so check if it can be converted to anything.
+        // We can NOT swap value with the converted value in here, since
+        // we called through the original value.
+
+        auto onCreateReference = [&](const ResourceName& name) {
+            mTable->addResource(name, ConfigDescription{},
+                    source, util::make_unique<Id>());
+        };
+
+        convertedValue = ResourceParser::parseItemForAttribute(
+                *str.value, attr, mResolver->getDefaultPackage(),
+                onCreateReference);
+        if (!convertedValue && attr.typeMask & android::ResTable_map::TYPE_STRING) {
+            // Last effort is to parse as a string.
+            util::StringBuilder builder;
+            builder.append(*str.value);
+            if (builder) {
+                convertedValue = util::make_unique<String>(
+                        mTable->getValueStringPool().makeRef(builder.str()));
+            }
+        }
+    });
+
+    if (convertedValue) {
+        value = std::move(convertedValue);
+    }
+
+    // Process this new or old value (it can be a reference!).
+    value->accept(*this, Args{ name, source });
+
+    // Flatten the value to see what resource type it is.
+    android::Res_value resValue;
+    value->flatten(resValue);
+
+    // Always allow references.
+    const uint32_t typeMask = attr.typeMask | android::ResTable_map::TYPE_REFERENCE;
+    if (!(typeMask & ResourceParser::androidTypeToAttributeTypeMask(resValue.dataType))) {
+        Logger::error(source)
+                << *value
+                << " is not compatible with attribute "
+                << attr
+                << "."
+                << std::endl;
+        mError = true;
+    }
+}
+
+void Linker::visit(Style& style, ValueVisitorArgs& a) {
+    Args& args = static_cast<Args&>(a);
+
+    if (style.parent.name.isValid()) {
+        visit(style.parent, a);
+    }
+
+    for (Style::Entry& styleEntry : style.entries) {
+        Maybe<Resolver::Entry> result = mResolver->findAttribute(styleEntry.key.name);
+        if (!result || !result.value().attr) {
+            addUnresolvedSymbol(styleEntry.key.name, args.source);
+            continue;
+        }
+
+        const Resolver::Entry& entry = result.value();
+        if (entry.id.isValid()) {
+            styleEntry.key.id = entry.id;
+        } else {
+            // Create a dependency for the style on this attribute.
+            mGraph[styleEntry.key.name].push_back(Node{
+                    args.referrer,
+                    args.source.path,
+                    args.source.line,
+                    &styleEntry.key
+            });
+        }
+        processAttributeValue(args.referrer, args.source, *entry.attr, styleEntry.value);
+    }
+}
+
+void Linker::visit(Attribute& attr, ValueVisitorArgs& a) {
+    static constexpr uint32_t kMask = android::ResTable_map::TYPE_ENUM |
+            android::ResTable_map::TYPE_FLAGS;
+    if (attr.typeMask & kMask) {
+        for (auto& symbol : attr.symbols) {
+            visit(symbol.symbol, a);
+        }
+    }
+}
+
+void Linker::visit(Styleable& styleable, ValueVisitorArgs& a) {
+    for (auto& attrRef : styleable.entries) {
+        visit(attrRef, a);
+    }
+}
+
+void Linker::visit(Sentinel& sentinel, ValueVisitorArgs& a) {
+    Args& args = static_cast<Args&>(a);
+    addUnresolvedSymbol(args.referrer, args.source);
+}
+
+void Linker::visit(Array& array, ValueVisitorArgs& a) {
+    Args& args = static_cast<Args&>(a);
+
+    for (auto& item : array.items) {
+        item->accept(*this, Args{ args.referrer, args.source });
+    }
+}
+
+void Linker::visit(Plural& plural, ValueVisitorArgs& a) {
+    Args& args = static_cast<Args&>(a);
+
+    for (auto& item : plural.values) {
+        if (item) {
+            item->accept(*this, Args{ args.referrer, args.source });
+        }
+    }
+}
+
+void Linker::addUnresolvedSymbol(const ResourceNameRef& name, const SourceLine& source) {
+    mUnresolvedSymbols[name.toResourceName()].push_back(source);
+}
+
+::std::ostream& operator<<(::std::ostream& out, const Linker::Node& node) {
+    return out << node.name << "(" << node.source << ":" << node.line << ")";
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/Linker.h b/tools/aapt2/Linker.h
new file mode 100644
index 0000000..9b911b7
--- /dev/null
+++ b/tools/aapt2/Linker.h
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_LINKER_H
+#define AAPT_LINKER_H
+
+#include "Resolver.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "Source.h"
+#include "StringPiece.h"
+
+#include <androidfw/AssetManager.h>
+#include <map>
+#include <memory>
+#include <ostream>
+#include <set>
+#include <vector>
+
+namespace aapt {
+
+/**
+ * The Linker has two jobs. It follows resource references
+ * and verifies that their targert exists and that their
+ * types are compatible. The Linker will also assign resource
+ * IDs and fill in all the dependent references with the newly
+ * assigned resource IDs.
+ *
+ * To do this, the Linker builds a graph of references. This
+ * can be useful to do other analysis, like building a
+ * dependency graph of source files. The hope is to be able to
+ * add functionality that operates on the graph without
+ * overcomplicating the Linker.
+ *
+ * TODO(adamlesinski): Build the graph first then run the separate
+ * steps over the graph.
+ */
+class Linker : ValueVisitor {
+public:
+    /**
+     * Create a Linker for the given resource table with the sources available in
+     * Resolver. Resolver should contain the ResourceTable as a source too.
+     */
+    Linker(std::shared_ptr<ResourceTable> table, std::shared_ptr<Resolver> resolver);
+
+    Linker(const Linker&) = delete;
+
+    /**
+     * Entry point to the linker. Assigns resource IDs, follows references,
+     * and validates types. Returns true if all references to defined values
+     * are type-compatible. Missing resource references are recorded but do
+     * not cause this method to fail.
+     */
+    bool linkAndValidate();
+
+    /**
+     * Returns any references to resources that were not defined in any of the
+     * sources.
+     */
+    using ResourceNameToSourceMap = std::map<ResourceName, std::vector<SourceLine>>;
+    const ResourceNameToSourceMap& getUnresolvedReferences() const;
+
+private:
+    struct Args : public ValueVisitorArgs {
+        Args(const ResourceNameRef& r, const SourceLine& s);
+
+        const ResourceNameRef& referrer;
+        const SourceLine& source;
+    };
+
+    //
+    // Overrides of ValueVisitor
+    //
+    void visit(Reference& reference, ValueVisitorArgs& args) override;
+    void visit(Attribute& attribute, ValueVisitorArgs& args) override;
+    void visit(Styleable& styleable, ValueVisitorArgs& args) override;
+    void visit(Style& style, ValueVisitorArgs& args) override;
+    void visit(Sentinel& sentinel, ValueVisitorArgs& args) override;
+    void visit(Array& array, ValueVisitorArgs& args) override;
+    void visit(Plural& plural, ValueVisitorArgs& args) override;
+
+    void processAttributeValue(const ResourceNameRef& name, const SourceLine& source,
+            const Attribute& attr, std::unique_ptr<Item>& value);
+
+    void addUnresolvedSymbol(const ResourceNameRef& name, const SourceLine& source);
+
+    /**
+     * Node of the resource table graph.
+     */
+    struct Node {
+        // We use ResourceNameRef and StringPiece, which are safe so long as the ResourceTable
+        // that defines the data isn't modified.
+        ResourceNameRef name;
+        StringPiece source;
+        size_t line;
+
+        // The reference object that points to name.
+        Reference* reference;
+
+        bool operator<(const Node& rhs) const;
+        bool operator==(const Node& rhs) const;
+        bool operator!=(const Node& rhs) const;
+    };
+    friend ::std::ostream& operator<<(::std::ostream&, const Node&);
+
+    std::shared_ptr<ResourceTable> mTable;
+    std::shared_ptr<Resolver> mResolver;
+    std::map<ResourceNameRef, std::vector<Node>> mGraph;
+    std::map<ResourceName, std::vector<SourceLine>> mUnresolvedSymbols;
+    bool mError;
+};
+
+} // namespace aapt
+
+#endif // AAPT_LINKER_H
diff --git a/tools/aapt2/Linker_test.cpp b/tools/aapt2/Linker_test.cpp
new file mode 100644
index 0000000..b1e201b
--- /dev/null
+++ b/tools/aapt2/Linker_test.cpp
@@ -0,0 +1,143 @@
+/*
+ * 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.
+ */
+
+#include "Linker.h"
+#include "Resolver.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "Util.h"
+
+#include <androidfw/AssetManager.h>
+#include <gtest/gtest.h>
+#include <string>
+
+namespace aapt {
+
+struct LinkerTest : public ::testing::Test {
+    virtual void SetUp() override {
+        mTable = std::make_shared<ResourceTable>();
+        mTable->setPackage(u"android");
+        mLinker = std::make_shared<Linker>(mTable, std::make_shared<Resolver>(
+                mTable, std::make_shared<android::AssetManager>()));
+
+        // Create a few attributes for use in the tests.
+
+        addResource(ResourceName{ {}, ResourceType::kAttr, u"integer" },
+                    util::make_unique<Attribute>(false, android::ResTable_map::TYPE_INTEGER));
+
+        addResource(ResourceName{ {}, ResourceType::kAttr, u"string" },
+                    util::make_unique<Attribute>(false, android::ResTable_map::TYPE_STRING));
+
+        addResource(ResourceName{ {}, ResourceType::kId, u"apple" }, util::make_unique<Id>());
+
+        addResource(ResourceName{ {}, ResourceType::kId, u"banana" }, util::make_unique<Id>());
+
+        std::unique_ptr<Attribute> flagAttr = util::make_unique<Attribute>(
+                false, android::ResTable_map::TYPE_FLAGS);
+        flagAttr->symbols.push_back(Attribute::Symbol{
+                ResourceNameRef{ u"android", ResourceType::kId, u"apple" }, 1 });
+        flagAttr->symbols.push_back(Attribute::Symbol{
+                ResourceNameRef{ u"android", ResourceType::kId, u"banana" }, 2 });
+        addResource(ResourceName{ {}, ResourceType::kAttr, u"flags" }, std::move(flagAttr));
+    }
+
+    /*
+     * Convenience method for adding resources with the default configuration and some
+     * bogus source line.
+     */
+    bool addResource(const ResourceNameRef& name, std::unique_ptr<Value> value) {
+        return mTable->addResource(name, {}, SourceLine{ "test.xml", 21 }, std::move(value));
+    }
+
+    std::shared_ptr<ResourceTable> mTable;
+    std::shared_ptr<Linker> mLinker;
+};
+
+TEST_F(LinkerTest, DoNotInterpretEscapedStringAsReference) {
+    ASSERT_TRUE(addResource(ResourceName{ u"android", ResourceType::kString, u"foo" },
+                util::make_unique<String>(mTable->getValueStringPool().makeRef(u"?123"))));
+
+    ASSERT_TRUE(mLinker->linkAndValidate());
+    EXPECT_TRUE(mLinker->getUnresolvedReferences().empty());
+}
+
+TEST_F(LinkerTest, EscapeAndConvertRawString) {
+    std::unique_ptr<Style> style = util::make_unique<Style>();
+    style->entries.push_back(Style::Entry{
+            ResourceNameRef{ u"android", ResourceType::kAttr, u"integer" },
+            util::make_unique<RawString>(mTable->getValueStringPool().makeRef(u"  123"))
+    });
+    const Style* result = style.get();
+    ASSERT_TRUE(addResource(ResourceName{ u"android", ResourceType::kStyle, u"foo" },
+                std::move(style)));
+
+    ASSERT_TRUE(mLinker->linkAndValidate());
+    EXPECT_TRUE(mLinker->getUnresolvedReferences().empty());
+
+    EXPECT_NE(nullptr, dynamic_cast<BinaryPrimitive*>(result->entries.front().value.get()));
+}
+
+TEST_F(LinkerTest, FailToConvertRawString) {
+    std::unique_ptr<Style> style = util::make_unique<Style>();
+    style->entries.push_back(Style::Entry{
+            ResourceNameRef{ u"android", ResourceType::kAttr, u"integer" },
+            util::make_unique<RawString>(mTable->getValueStringPool().makeRef(u"yo what is up?"))
+    });
+    ASSERT_TRUE(addResource(ResourceName{ u"android", ResourceType::kStyle, u"foo" },
+                std::move(style)));
+
+    ASSERT_FALSE(mLinker->linkAndValidate());
+}
+
+TEST_F(LinkerTest, ConvertRawStringToString) {
+    std::unique_ptr<Style> style = util::make_unique<Style>();
+    style->entries.push_back(Style::Entry{
+            ResourceNameRef{ u"android", ResourceType::kAttr, u"string" },
+            util::make_unique<RawString>(
+                    mTable->getValueStringPool().makeRef(u"  \"this  is  \\u00fa\"."))
+    });
+    const Style* result = style.get();
+    ASSERT_TRUE(addResource(ResourceName{ u"android", ResourceType::kStyle, u"foo" },
+                std::move(style)));
+
+    ASSERT_TRUE(mLinker->linkAndValidate());
+    EXPECT_TRUE(mLinker->getUnresolvedReferences().empty());
+
+    const String* str = dynamic_cast<const String*>(result->entries.front().value.get());
+    ASSERT_NE(nullptr, str);
+    EXPECT_EQ(*str->value, u"this  is  \u00fa.");
+}
+
+TEST_F(LinkerTest, ConvertRawStringToFlags) {
+    std::unique_ptr<Style> style = util::make_unique<Style>();
+    style->entries.push_back(Style::Entry{
+            ResourceNameRef{ u"android", ResourceType::kAttr, u"flags" },
+            util::make_unique<RawString>(mTable->getValueStringPool().makeRef(u"banana | apple"))
+    });
+    const Style* result = style.get();
+    ASSERT_TRUE(addResource(ResourceName{ u"android", ResourceType::kStyle, u"foo" },
+                std::move(style)));
+
+    ASSERT_TRUE(mLinker->linkAndValidate());
+    EXPECT_TRUE(mLinker->getUnresolvedReferences().empty());
+
+    const BinaryPrimitive* bin = dynamic_cast<const BinaryPrimitive*>(
+            result->entries.front().value.get());
+    ASSERT_NE(nullptr, bin);
+    EXPECT_EQ(bin->value.data, 1u | 2u);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/Locale.cpp b/tools/aapt2/Locale.cpp
new file mode 100644
index 0000000..eed0ea7
--- /dev/null
+++ b/tools/aapt2/Locale.cpp
@@ -0,0 +1,274 @@
+/*
+ * 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.
+ */
+
+#include "Locale.h"
+#include "Util.h"
+
+#include <algorithm>
+#include <ctype.h>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+using android::ResTable_config;
+
+void LocaleValue::setLanguage(const char* languageChars) {
+     size_t i = 0;
+     while ((*languageChars) != '\0') {
+          language[i++] = ::tolower(*languageChars);
+          languageChars++;
+     }
+}
+
+void LocaleValue::setRegion(const char* regionChars) {
+    size_t i = 0;
+    while ((*regionChars) != '\0') {
+         region[i++] = ::toupper(*regionChars);
+         regionChars++;
+    }
+}
+
+void LocaleValue::setScript(const char* scriptChars) {
+    size_t i = 0;
+    while ((*scriptChars) != '\0') {
+         if (i == 0) {
+             script[i++] = ::toupper(*scriptChars);
+         } else {
+             script[i++] = ::tolower(*scriptChars);
+         }
+         scriptChars++;
+    }
+}
+
+void LocaleValue::setVariant(const char* variantChars) {
+     size_t i = 0;
+     while ((*variantChars) != '\0') {
+          variant[i++] = *variantChars;
+          variantChars++;
+     }
+}
+
+static inline bool isAlpha(const std::string& str) {
+    return std::all_of(std::begin(str), std::end(str), ::isalpha);
+}
+
+static inline bool isNumber(const std::string& str) {
+    return std::all_of(std::begin(str), std::end(str), ::isdigit);
+}
+
+bool LocaleValue::initFromFilterString(const std::string& str) {
+     // A locale (as specified in the filter) is an underscore separated name such
+     // as "en_US", "en_Latn_US", or "en_US_POSIX".
+     std::vector<std::string> parts = util::splitAndLowercase(str, '_');
+
+     const int numTags = parts.size();
+     bool valid = false;
+     if (numTags >= 1) {
+         const std::string& lang = parts[0];
+         if (isAlpha(lang) && (lang.length() == 2 || lang.length() == 3)) {
+             setLanguage(lang.c_str());
+             valid = true;
+         }
+     }
+
+     if (!valid || numTags == 1) {
+         return valid;
+     }
+
+     // At this point, valid == true && numTags > 1.
+     const std::string& part2 = parts[1];
+     if ((part2.length() == 2 && isAlpha(part2)) ||
+         (part2.length() == 3 && isNumber(part2))) {
+         setRegion(part2.c_str());
+     } else if (part2.length() == 4 && isAlpha(part2)) {
+         setScript(part2.c_str());
+     } else if (part2.length() >= 5 && part2.length() <= 8) {
+         setVariant(part2.c_str());
+     } else {
+         valid = false;
+     }
+
+     if (!valid || numTags == 2) {
+         return valid;
+     }
+
+     // At this point, valid == true && numTags > 1.
+     const std::string& part3 = parts[2];
+     if (((part3.length() == 2 && isAlpha(part3)) ||
+         (part3.length() == 3 && isNumber(part3))) && script[0]) {
+         setRegion(part3.c_str());
+     } else if (part3.length() >= 5 && part3.length() <= 8) {
+         setVariant(part3.c_str());
+     } else {
+         valid = false;
+     }
+
+     if (!valid || numTags == 3) {
+         return valid;
+     }
+
+     const std::string& part4 = parts[3];
+     if (part4.length() >= 5 && part4.length() <= 8) {
+         setVariant(part4.c_str());
+     } else {
+         valid = false;
+     }
+
+     if (!valid || numTags > 4) {
+         return false;
+     }
+
+     return true;
+}
+
+ssize_t LocaleValue::initFromParts(std::vector<std::string>::iterator iter,
+        std::vector<std::string>::iterator end) {
+    const std::vector<std::string>::iterator startIter = iter;
+
+    std::string& part = *iter;
+    if (part[0] == 'b' && part[1] == '+') {
+        // This is a "modified" BCP-47 language tag. Same semantics as BCP-47 tags,
+        // except that the separator is "+" and not "-".
+        std::vector<std::string> subtags = util::splitAndLowercase(part, '+');
+        subtags.erase(subtags.begin());
+        if (subtags.size() == 1) {
+            setLanguage(subtags[0].c_str());
+        } else if (subtags.size() == 2) {
+            setLanguage(subtags[0].c_str());
+
+            // The second tag can either be a region, a variant or a script.
+            switch (subtags[1].size()) {
+                case 2:
+                case 3:
+                    setRegion(subtags[1].c_str());
+                    break;
+                case 4:
+                    setScript(subtags[1].c_str());
+                    break;
+                case 5:
+                case 6:
+                case 7:
+                case 8:
+                    setVariant(subtags[1].c_str());
+                    break;
+                default:
+                    return -1;
+            }
+        } else if (subtags.size() == 3) {
+            // The language is always the first subtag.
+            setLanguage(subtags[0].c_str());
+
+            // The second subtag can either be a script or a region code.
+            // If its size is 4, it's a script code, else it's a region code.
+            if (subtags[1].size() == 4) {
+                setScript(subtags[1].c_str());
+            } else if (subtags[1].size() == 2 || subtags[1].size() == 3) {
+                setRegion(subtags[1].c_str());
+            } else {
+                return -1;
+            }
+
+            // The third tag can either be a region code (if the second tag was
+            // a script), else a variant code.
+            if (subtags[2].size() > 4) {
+                setVariant(subtags[2].c_str());
+            } else {
+                setRegion(subtags[2].c_str());
+            }
+        } else if (subtags.size() == 4) {
+            setLanguage(subtags[0].c_str());
+            setScript(subtags[1].c_str());
+            setRegion(subtags[2].c_str());
+            setVariant(subtags[3].c_str());
+        } else {
+            return -1;
+        }
+
+        ++iter;
+
+    } else {
+        if ((part.length() == 2 || part.length() == 3)
+                && isAlpha(part) && part != "car") {
+            setLanguage(part.c_str());
+            ++iter;
+
+            if (iter != end) {
+                const std::string& regionPart = *iter;
+                if (regionPart.c_str()[0] == 'r' && regionPart.length() == 3) {
+                    setRegion(regionPart.c_str() + 1);
+                    ++iter;
+                }
+            }
+        }
+    }
+
+    return static_cast<ssize_t>(iter - startIter);
+}
+
+
+std::string LocaleValue::toDirName() const {
+    std::string dirName;
+    if (language[0]) {
+        dirName += language;
+    } else {
+        return dirName;
+    }
+
+    if (script[0]) {
+        dirName += "-s";
+        dirName += script;
+    }
+
+    if (region[0]) {
+        dirName += "-r";
+        dirName += region;
+    }
+
+    if (variant[0]) {
+        dirName += "-v";
+        dirName += variant;
+    }
+
+    return dirName;
+}
+
+void LocaleValue::initFromResTable(const ResTable_config& config) {
+    config.unpackLanguage(language);
+    config.unpackRegion(region);
+    if (config.localeScript[0]) {
+        memcpy(script, config.localeScript, sizeof(config.localeScript));
+    }
+
+    if (config.localeVariant[0]) {
+        memcpy(variant, config.localeVariant, sizeof(config.localeVariant));
+    }
+}
+
+void LocaleValue::writeTo(ResTable_config* out) const {
+    out->packLanguage(language);
+    out->packRegion(region);
+
+    if (script[0]) {
+        memcpy(out->localeScript, script, sizeof(out->localeScript));
+    }
+
+    if (variant[0]) {
+        memcpy(out->localeVariant, variant, sizeof(out->localeVariant));
+    }
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/Locale.h b/tools/aapt2/Locale.h
new file mode 100644
index 0000000..ceec764
--- /dev/null
+++ b/tools/aapt2/Locale.h
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_LOCALE_VALUE_H
+#define AAPT_LOCALE_VALUE_H
+
+#include <androidfw/ResourceTypes.h>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+/**
+ * A convenience class to build and parse locales.
+ */
+struct LocaleValue {
+    char language[4];
+    char region[4];
+    char script[4];
+    char variant[8];
+
+    inline LocaleValue();
+
+    /**
+     * Initialize this LocaleValue from a config string.
+     */
+    bool initFromFilterString(const std::string& config);
+
+    /**
+     * Initialize this LocaleValue from parts of a vector.
+     */
+    ssize_t initFromParts(std::vector<std::string>::iterator iter,
+            std::vector<std::string>::iterator end);
+
+    /**
+     * Initialize this LocaleValue from a ResTable_config.
+     */
+    void initFromResTable(const android::ResTable_config& config);
+
+    /**
+     * Set the locale in a ResTable_config from this LocaleValue.
+     */
+    void writeTo(android::ResTable_config* out) const;
+
+    std::string toDirName() const;
+
+    inline int compare(const LocaleValue& other) const;
+
+    inline bool operator<(const LocaleValue& o) const;
+    inline bool operator<=(const LocaleValue& o) const;
+    inline bool operator==(const LocaleValue& o) const;
+    inline bool operator!=(const LocaleValue& o) const;
+    inline bool operator>=(const LocaleValue& o) const;
+    inline bool operator>(const LocaleValue& o) const;
+
+private:
+     void setLanguage(const char* language);
+     void setRegion(const char* language);
+     void setScript(const char* script);
+     void setVariant(const char* variant);
+};
+
+//
+// Implementation
+//
+
+LocaleValue::LocaleValue() {
+    memset(this, 0, sizeof(LocaleValue));
+}
+
+int LocaleValue::compare(const LocaleValue& other) const {
+    return memcmp(this, &other, sizeof(LocaleValue));
+}
+
+bool LocaleValue::operator<(const LocaleValue& o) const {
+    return compare(o) < 0;
+}
+
+bool LocaleValue::operator<=(const LocaleValue& o) const {
+    return compare(o) <= 0;
+}
+
+bool LocaleValue::operator==(const LocaleValue& o) const {
+    return compare(o) == 0;
+}
+
+bool LocaleValue::operator!=(const LocaleValue& o) const {
+    return compare(o) != 0;
+}
+
+bool LocaleValue::operator>=(const LocaleValue& o) const {
+    return compare(o) >= 0;
+}
+
+bool LocaleValue::operator>(const LocaleValue& o) const {
+    return compare(o) > 0;
+}
+
+} // namespace aapt
+
+#endif // AAPT_LOCALE_VALUE_H
diff --git a/tools/aapt2/Locale_test.cpp b/tools/aapt2/Locale_test.cpp
new file mode 100644
index 0000000..4e154d6
--- /dev/null
+++ b/tools/aapt2/Locale_test.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#include "Locale.h"
+#include "Util.h"
+
+#include <gtest/gtest.h>
+#include <string>
+
+namespace aapt {
+
+static ::testing::AssertionResult TestLanguage(const char* input, const char* lang) {
+    std::vector<std::string> parts = util::splitAndLowercase(std::string(input), '-');
+    LocaleValue lv;
+    ssize_t count = lv.initFromParts(std::begin(parts), std::end(parts));
+    if (count < 0) {
+        return ::testing::AssertionFailure() << " failed to parse '" << input << "'.";
+    }
+
+    if (count != 1) {
+        return ::testing::AssertionFailure() << count
+            << " parts were consumed parsing '" << input << "' but expected 1.";
+    }
+
+    if (memcmp(lv.language, lang, std::min(strlen(lang), sizeof(lv.language))) != 0) {
+        return ::testing::AssertionFailure() << "expected " << lang << " but got "
+            << std::string(lv.language, sizeof(lv.language)) << ".";
+    }
+
+    return ::testing::AssertionSuccess();
+}
+
+static ::testing::AssertionResult TestLanguageRegion(const char* input, const char* lang,
+                                                     const char* region) {
+    std::vector<std::string> parts = util::splitAndLowercase(std::string(input), '-');
+    LocaleValue lv;
+    ssize_t count = lv.initFromParts(std::begin(parts), std::end(parts));
+    if (count < 0) {
+        return ::testing::AssertionFailure() << " failed to parse '" << input << "'.";
+    }
+
+    if (count != 2) {
+        return ::testing::AssertionFailure() << count
+            << " parts were consumed parsing '" << input << "' but expected 2.";
+    }
+
+    if (memcmp(lv.language, lang, std::min(strlen(lang), sizeof(lv.language))) != 0) {
+        return ::testing::AssertionFailure() << "expected " << input << " but got "
+            << std::string(lv.language, sizeof(lv.language)) << ".";
+    }
+
+    if (memcmp(lv.region, region, std::min(strlen(region), sizeof(lv.region))) != 0) {
+        return ::testing::AssertionFailure() << "expected " << region << " but got "
+            << std::string(lv.region, sizeof(lv.region)) << ".";
+    }
+
+    return ::testing::AssertionSuccess();
+}
+
+TEST(ConfigDescriptionTest, ParseLanguage) {
+    EXPECT_TRUE(TestLanguage("en", "en"));
+    EXPECT_TRUE(TestLanguage("fr", "fr"));
+    EXPECT_FALSE(TestLanguage("land", ""));
+    EXPECT_TRUE(TestLanguage("fr-land", "fr"));
+
+    EXPECT_TRUE(TestLanguageRegion("fr-rCA", "fr", "CA"));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/Logger.cpp b/tools/aapt2/Logger.cpp
new file mode 100644
index 0000000..3847185
--- /dev/null
+++ b/tools/aapt2/Logger.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+#include "Logger.h"
+#include "Source.h"
+
+#include <memory>
+#include <iostream>
+
+namespace aapt {
+
+Log::Log(std::ostream& _out, std::ostream& _err) : out(_out), err(_err) {
+}
+
+std::shared_ptr<Log> Logger::sLog(std::make_shared<Log>(std::cerr, std::cerr));
+
+void Logger::setLog(const std::shared_ptr<Log>& log) {
+    sLog = log;
+}
+
+std::ostream& Logger::error() {
+    return sLog->err << "error: ";
+}
+
+std::ostream& Logger::error(const Source& source) {
+    return sLog->err << source << ": error: ";
+}
+
+std::ostream& Logger::error(const SourceLine& source) {
+    return sLog->err << source << ": error: ";
+}
+
+std::ostream& Logger::warn() {
+    return sLog->err << "warning: ";
+}
+
+std::ostream& Logger::warn(const Source& source) {
+    return sLog->err << source << ": warning: ";
+}
+
+std::ostream& Logger::warn(const SourceLine& source) {
+    return sLog->err << source << ": warning: ";
+}
+
+std::ostream& Logger::note() {
+    return sLog->out << "note: ";
+}
+
+std::ostream& Logger::note(const Source& source) {
+    return sLog->err << source << ": note: ";
+}
+
+std::ostream& Logger::note(const SourceLine& source) {
+    return sLog->err << source << ": note: ";
+}
+
+SourceLogger::SourceLogger(const Source& source)
+: mSource(source) {
+}
+
+std::ostream& SourceLogger::error() {
+    return Logger::error(mSource);
+}
+
+std::ostream& SourceLogger::error(size_t line) {
+    return Logger::error(SourceLine{ mSource.path, line });
+}
+
+std::ostream& SourceLogger::warn() {
+    return Logger::warn(mSource);
+}
+
+std::ostream& SourceLogger::warn(size_t line) {
+    return Logger::warn(SourceLine{ mSource.path, line });
+}
+
+std::ostream& SourceLogger::note() {
+    return Logger::note(mSource);
+}
+
+std::ostream& SourceLogger::note(size_t line) {
+    return Logger::note(SourceLine{ mSource.path, line });
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/Logger.h b/tools/aapt2/Logger.h
new file mode 100644
index 0000000..1d437eb
--- /dev/null
+++ b/tools/aapt2/Logger.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_LOGGER_H
+#define AAPT_LOGGER_H
+
+#include "Source.h"
+
+#include <memory>
+#include <ostream>
+#include <string>
+#include <utils/String8.h>
+
+namespace aapt {
+
+struct Log {
+    Log(std::ostream& out, std::ostream& err);
+    Log(const Log& rhs) = delete;
+
+    std::ostream& out;
+    std::ostream& err;
+};
+
+class Logger {
+public:
+    static void setLog(const std::shared_ptr<Log>& log);
+
+    static std::ostream& error();
+    static std::ostream& error(const Source& source);
+    static std::ostream& error(const SourceLine& sourceLine);
+
+    static std::ostream& warn();
+    static std::ostream& warn(const Source& source);
+    static std::ostream& warn(const SourceLine& sourceLine);
+
+    static std::ostream& note();
+    static std::ostream& note(const Source& source);
+    static std::ostream& note(const SourceLine& sourceLine);
+
+private:
+    static std::shared_ptr<Log> sLog;
+};
+
+class SourceLogger {
+public:
+    SourceLogger(const Source& source);
+
+    std::ostream& error();
+    std::ostream& error(size_t line);
+
+    std::ostream& warn();
+    std::ostream& warn(size_t line);
+
+    std::ostream& note();
+    std::ostream& note(size_t line);
+
+private:
+    Source mSource;
+};
+
+inline ::std::ostream& operator<<(::std::ostream& out, const std::u16string& str) {
+    android::String8 utf8(str.data(), str.size());
+    return out.write(utf8.string(), utf8.size());
+}
+
+} // namespace aapt
+
+#endif // AAPT_LOGGER_H
diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp
new file mode 100644
index 0000000..f4e80c5
--- /dev/null
+++ b/tools/aapt2/Main.cpp
@@ -0,0 +1,1421 @@
+/*
+ * 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.
+ */
+
+#include "AppInfo.h"
+#include "BigBuffer.h"
+#include "BinaryResourceParser.h"
+#include "Files.h"
+#include "JavaClassGenerator.h"
+#include "Linker.h"
+#include "ManifestParser.h"
+#include "ManifestValidator.h"
+#include "ResourceParser.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "SdkConstants.h"
+#include "SourceXmlPullParser.h"
+#include "StringPiece.h"
+#include "TableFlattener.h"
+#include "Util.h"
+#include "XmlFlattener.h"
+
+#include <algorithm>
+#include <androidfw/AssetManager.h>
+#include <cstdlib>
+#include <dirent.h>
+#include <errno.h>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <sys/stat.h>
+
+using namespace aapt;
+
+void printTable(const ResourceTable& table) {
+    std::cout << "ResourceTable package=" << table.getPackage();
+    if (table.getPackageId() != ResourceTable::kUnsetPackageId) {
+        std::cout << " id=" << std::hex << table.getPackageId() << std::dec;
+    }
+    std::cout << std::endl
+         << "---------------------------------------------------------" << std::endl;
+
+    for (const auto& type : table) {
+        std::cout << "Type " << type->type;
+        if (type->typeId != ResourceTableType::kUnsetTypeId) {
+            std::cout << " [" << type->typeId << "]";
+        }
+        std::cout << " (" << type->entries.size() << " entries)" << std::endl;
+        for (const auto& entry : type->entries) {
+            std::cout << "  " << entry->name;
+            if (entry->entryId != ResourceEntry::kUnsetEntryId) {
+                std::cout << " [" << entry->entryId << "]";
+            }
+            std::cout << " (" << entry->values.size() << " configurations)";
+            if (entry->publicStatus.isPublic) {
+                std::cout << " PUBLIC";
+            }
+            std::cout << std::endl;
+            for (const auto& value : entry->values) {
+                std::cout << "    " << value.config << " (" << value.source << ") : ";
+                value.value->print(std::cout);
+                std::cout << std::endl;
+            }
+        }
+    }
+}
+
+void printStringPool(const StringPool& pool) {
+    std::cout << "String pool of length " << pool.size() << std::endl
+         << "---------------------------------------------------------" << std::endl;
+
+    size_t i = 0;
+    for (const auto& entry : pool) {
+        std::cout << "[" << i << "]: "
+             << entry->value
+             << " (Priority " << entry->context.priority
+             << ", Config '" << entry->context.config << "')"
+             << std::endl;
+        i++;
+    }
+}
+
+std::unique_ptr<FileReference> makeFileReference(StringPool& pool, const StringPiece& filename,
+        ResourceType type, const ConfigDescription& config) {
+    std::stringstream path;
+    path << "res/" << type;
+    if (config != ConfigDescription{}) {
+        path << "-" << config;
+    }
+    path << "/" << filename;
+    return util::make_unique<FileReference>(pool.makeRef(util::utf8ToUtf16(path.str())));
+}
+
+/**
+ * Collect files from 'root', filtering out any files that do not
+ * match the FileFilter 'filter'.
+ */
+bool walkTree(const StringPiece& root, const FileFilter& filter,
+        std::vector<Source>& outEntries) {
+    bool error = false;
+
+    for (const std::string& dirName : listFiles(root)) {
+        std::string dir(root.toString());
+        appendPath(&dir, dirName);
+
+        FileType ft = getFileType(dir);
+        if (!filter(dirName, ft)) {
+            continue;
+        }
+
+        if (ft != FileType::kDirectory) {
+            continue;
+        }
+
+        for (const std::string& fileName : listFiles(dir)) {
+            std::string file(dir);
+            appendPath(&file, fileName);
+
+            FileType ft = getFileType(file);
+            if (!filter(fileName, ft)) {
+                continue;
+            }
+
+            if (ft != FileType::kRegular) {
+                Logger::error(Source{ file })
+                    << "not a regular file."
+                    << std::endl;
+                error = true;
+                continue;
+            }
+            outEntries.emplace_back(Source{ file });
+        }
+    }
+    return !error;
+}
+
+bool loadBinaryResourceTable(std::shared_ptr<ResourceTable> table, const Source& source) {
+    std::ifstream ifs(source.path, std::ifstream::in | std::ifstream::binary);
+    if (!ifs) {
+        Logger::error(source) << strerror(errno) << std::endl;
+        return false;
+    }
+
+    std::streampos fsize = ifs.tellg();
+    ifs.seekg(0, std::ios::end);
+    fsize = ifs.tellg() - fsize;
+    ifs.seekg(0, std::ios::beg);
+
+    assert(fsize >= 0);
+    size_t dataSize = static_cast<size_t>(fsize);
+    char* buf = new char[dataSize];
+    ifs.read(buf, dataSize);
+
+    BinaryResourceParser parser(table, source, buf, dataSize);
+    bool result = parser.parse();
+
+    delete [] buf;
+    return result;
+}
+
+bool loadResTable(android::ResTable* table, const Source& source) {
+    std::ifstream ifs(source.path, std::ifstream::in | std::ifstream::binary);
+    if (!ifs) {
+        Logger::error(source) << strerror(errno) << std::endl;
+        return false;
+    }
+
+    std::streampos fsize = ifs.tellg();
+    ifs.seekg(0, std::ios::end);
+    fsize = ifs.tellg() - fsize;
+    ifs.seekg(0, std::ios::beg);
+
+    assert(fsize >= 0);
+    size_t dataSize = static_cast<size_t>(fsize);
+    char* buf = new char[dataSize];
+    ifs.read(buf, dataSize);
+
+    bool result = table->add(buf, dataSize, -1, true) == android::NO_ERROR;
+
+    delete [] buf;
+    return result;
+}
+
+void versionStylesForCompat(std::shared_ptr<ResourceTable> table) {
+    for (auto& type : *table) {
+        if (type->type != ResourceType::kStyle) {
+            continue;
+        }
+
+        for (auto& entry : type->entries) {
+            // Add the versioned styles we want to create
+            // here. They are added to the table after
+            // iterating over the original set of styles.
+            //
+            // A stack is used since auto-generated styles
+            // from later versions should override
+            // auto-generated styles from earlier versions.
+            // Iterating over the styles is done in order,
+            // so we will always visit sdkVersions from smallest
+            // to largest.
+            std::stack<ResourceConfigValue> addStack;
+
+            for (ResourceConfigValue& configValue : entry->values) {
+                visitFunc<Style>(*configValue.value, [&](Style& style) {
+                    // Collect which entries we've stripped and the smallest
+                    // SDK level which was stripped.
+                    size_t minSdkStripped = std::numeric_limits<size_t>::max();
+                    std::vector<Style::Entry> stripped;
+
+                    // Iterate over the style's entries and erase/record the
+                    // attributes whose SDK level exceeds the config's sdkVersion.
+                    auto iter = style.entries.begin();
+                    while (iter != style.entries.end()) {
+                        if (iter->key.name.package == u"android") {
+                            size_t sdkLevel = findAttributeSdkLevel(iter->key.name.entry);
+                            if (sdkLevel > 1 && sdkLevel > configValue.config.sdkVersion) {
+                                // Record that we are about to strip this.
+                                stripped.emplace_back(std::move(*iter));
+                                minSdkStripped = std::min(minSdkStripped, sdkLevel);
+
+                                // Erase this from this style.
+                                iter = style.entries.erase(iter);
+                                continue;
+                            }
+                        }
+                        ++iter;
+                    }
+
+                    if (!stripped.empty()) {
+                        // We have stripped attributes, so let's create a new style to hold them.
+                        ConfigDescription versionConfig(configValue.config);
+                        versionConfig.sdkVersion = minSdkStripped;
+
+                        ResourceConfigValue value = {
+                                versionConfig,
+                                configValue.source,
+                                {},
+
+                                // Create a copy of the original style.
+                                std::unique_ptr<Value>(configValue.value->clone())
+                        };
+
+                        Style& newStyle = static_cast<Style&>(*value.value);
+
+                        // Move the recorded stripped attributes into this new style.
+                        std::move(stripped.begin(), stripped.end(),
+                                  std::back_inserter(newStyle.entries));
+
+                        // We will add this style to the table later. If we do it now, we will
+                        // mess up iteration.
+                        addStack.push(std::move(value));
+                    }
+                });
+            }
+
+            auto comparator =
+                    [](const ResourceConfigValue& lhs, const ConfigDescription& rhs) -> bool {
+                        return lhs.config < rhs;
+                    };
+
+            while (!addStack.empty()) {
+                ResourceConfigValue& value = addStack.top();
+                auto iter = std::lower_bound(entry->values.begin(), entry->values.end(),
+                                             value.config, comparator);
+                if (iter == entry->values.end() || iter->config != value.config) {
+                    entry->values.insert(iter, std::move(value));
+                }
+                addStack.pop();
+            }
+        }
+    }
+}
+
+bool collectXml(std::shared_ptr<ResourceTable> table, const Source& source,
+                const ResourceName& name,
+                const ConfigDescription& config) {
+    std::ifstream in(source.path, std::ifstream::binary);
+    if (!in) {
+        Logger::error(source) << strerror(errno) << std::endl;
+        return false;
+    }
+
+    std::set<size_t> sdkLevels;
+
+    SourceXmlPullParser pullParser(in);
+    while (XmlPullParser::isGoodEvent(pullParser.next())) {
+        if (pullParser.getEvent() != XmlPullParser::Event::kStartElement) {
+            continue;
+        }
+
+        const auto endIter = pullParser.endAttributes();
+        for (auto iter = pullParser.beginAttributes(); iter != endIter; ++iter) {
+            if (iter->namespaceUri == u"http://schemas.android.com/apk/res/android") {
+                size_t sdkLevel = findAttributeSdkLevel(iter->name);
+                if (sdkLevel > 1) {
+                    sdkLevels.insert(sdkLevel);
+                }
+            }
+
+            ResourceNameRef refName;
+            bool create = false;
+            bool privateRef = false;
+            if (ResourceParser::tryParseReference(iter->value, &refName, &create, &privateRef) &&
+                    create) {
+                table->addResource(refName, {}, source.line(pullParser.getLineNumber()),
+                                   util::make_unique<Id>());
+            }
+        }
+    }
+
+    std::unique_ptr<FileReference> fileResource = makeFileReference(
+            table->getValueStringPool(),
+            util::utf16ToUtf8(name.entry) + ".xml",
+            name.type,
+            config);
+    table->addResource(name, config, source.line(0), std::move(fileResource));
+
+    for (size_t level : sdkLevels) {
+        Logger::note(source)
+                << "creating v" << level << " versioned file."
+                << std::endl;
+        ConfigDescription newConfig = config;
+        newConfig.sdkVersion = level;
+
+        std::unique_ptr<FileReference> fileResource = makeFileReference(
+                table->getValueStringPool(),
+                util::utf16ToUtf8(name.entry) + ".xml",
+                name.type,
+                newConfig);
+        table->addResource(name, newConfig, source.line(0), std::move(fileResource));
+    }
+    return true;
+}
+
+struct CompileXml {
+    Source source;
+    ResourceName name;
+    ConfigDescription config;
+};
+
+bool compileXml(std::shared_ptr<Resolver> resolver, const CompileXml& item,
+                const Source& outputSource, std::queue<CompileXml>* queue) {
+    std::ifstream in(item.source.path, std::ifstream::binary);
+    if (!in) {
+        Logger::error(item.source) << strerror(errno) << std::endl;
+        return false;
+    }
+
+    BigBuffer outBuffer(1024);
+    std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<SourceXmlPullParser>(in);
+    XmlFlattener flattener(resolver);
+
+    // We strip attributes that do not belong in this version of the resource.
+    // Non-version qualified resources have an implicit version 1 requirement.
+    XmlFlattener::Options options = { item.config.sdkVersion ? item.config.sdkVersion : 1 };
+    Maybe<size_t> minStrippedSdk = flattener.flatten(item.source, xmlParser, &outBuffer, options);
+    if (!minStrippedSdk) {
+        return false;
+    }
+
+    if (minStrippedSdk.value() > 0) {
+        // Something was stripped, so let's generate a new file
+        // with the version of the smallest SDK version stripped.
+        CompileXml newWork = item;
+        newWork.config.sdkVersion = minStrippedSdk.value();
+        queue->push(newWork);
+    }
+
+    std::ofstream out(outputSource.path, std::ofstream::binary);
+    if (!out) {
+        Logger::error(outputSource) << strerror(errno) << std::endl;
+        return false;
+    }
+
+    if (!util::writeAll(out, outBuffer)) {
+        Logger::error(outputSource) << strerror(errno) << std::endl;
+        return false;
+    }
+    return true;
+}
+
+struct AaptOptions {
+    enum class Phase {
+        LegacyFull,
+        Collect,
+        Link,
+        Compile,
+    };
+
+    // The phase to process.
+    Phase phase;
+
+    // Details about the app.
+    AppInfo appInfo;
+
+    // The location of the manifest file.
+    Source manifest;
+
+    // The files to process.
+    std::vector<Source> sources;
+
+    // The libraries these files may reference.
+    std::vector<Source> libraries;
+
+    // Output directory.
+    Source output;
+
+    // Whether to generate a Java Class.
+    Maybe<Source> generateJavaClass;
+
+    // Whether to output verbose details about
+    // compilation.
+    bool verbose = false;
+};
+
+bool compileAndroidManifest(std::shared_ptr<Resolver> resolver, const AaptOptions& options) {
+    Source outSource = options.output;
+    appendPath(&outSource.path, "AndroidManifest.xml");
+
+    if (options.verbose) {
+        Logger::note(outSource) << "compiling AndroidManifest.xml." << std::endl;
+    }
+
+    std::ifstream in(options.manifest.path, std::ifstream::binary);
+    if (!in) {
+        Logger::error(options.manifest) << strerror(errno) << std::endl;
+        return false;
+    }
+
+    BigBuffer outBuffer(1024);
+    std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<SourceXmlPullParser>(in);
+    XmlFlattener flattener(resolver);
+
+    Maybe<size_t> result = flattener.flatten(options.manifest, xmlParser, &outBuffer,
+                                             XmlFlattener::Options{});
+    if (!result) {
+        return false;
+    }
+
+    std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(new uint8_t[outBuffer.size()]);
+    uint8_t* p = data.get();
+    for (const auto& b : outBuffer) {
+        memcpy(p, b.buffer.get(), b.size);
+        p += b.size;
+    }
+
+    android::ResXMLTree tree;
+    if (tree.setTo(data.get(), outBuffer.size()) != android::NO_ERROR) {
+        return false;
+    }
+
+    ManifestValidator validator(resolver->getResTable());
+    if (!validator.validate(options.manifest, &tree)) {
+        return false;
+    }
+
+    std::ofstream out(outSource.path, std::ofstream::binary);
+    if (!out) {
+        Logger::error(outSource) << strerror(errno) << std::endl;
+        return false;
+    }
+
+    if (!util::writeAll(out, outBuffer)) {
+        Logger::error(outSource) << strerror(errno) << std::endl;
+        return false;
+    }
+    return true;
+}
+
+bool loadAppInfo(const Source& source, AppInfo* outInfo) {
+    std::ifstream ifs(source.path, std::ifstream::in | std::ifstream::binary);
+    if (!ifs) {
+        Logger::error(source) << strerror(errno) << std::endl;
+        return false;
+    }
+
+    ManifestParser parser;
+    std::shared_ptr<XmlPullParser> pullParser = std::make_shared<SourceXmlPullParser>(ifs);
+    return parser.parse(source, pullParser, outInfo);
+}
+
+/**
+ * Parses legacy options and walks the source directories collecting
+ * files to process.
+ */
+bool prepareLegacy(std::vector<StringPiece>::const_iterator argsIter,
+        const std::vector<StringPiece>::const_iterator argsEndIter,
+        AaptOptions &options) {
+    options.phase = AaptOptions::Phase::LegacyFull;
+
+    std::vector<StringPiece> sourceDirs;
+    while (argsIter != argsEndIter) {
+        if (*argsIter == "-S") {
+            ++argsIter;
+            if (argsIter == argsEndIter) {
+                Logger::error() << "-S missing argument." << std::endl;
+                return false;
+            }
+            sourceDirs.push_back(*argsIter);
+        } else if (*argsIter == "-I") {
+            ++argsIter;
+            if (argsIter == argsEndIter) {
+                Logger::error() << "-I missing argument." << std::endl;
+                return false;
+            }
+            options.libraries.push_back(Source{ argsIter->toString() });
+        } else if (*argsIter == "-M") {
+            ++argsIter;
+            if (argsIter == argsEndIter) {
+                Logger::error() << "-M missing argument." << std::endl;
+                return false;
+            }
+
+            if (!options.manifest.path.empty()) {
+                Logger::error() << "multiple -M flags are not allowed." << std::endl;
+                return false;
+            }
+            options.manifest.path = argsIter->toString();
+        } else if (*argsIter == "-o") {
+            ++argsIter;
+            if (argsIter == argsEndIter) {
+                Logger::error() << "-o missing argument." << std::endl;
+                return false;
+            }
+            options.output = Source{ argsIter->toString() };
+        } else if (*argsIter == "-J") {
+            ++argsIter;
+            if (argsIter == argsEndIter) {
+                Logger::error() << "-J missing argument." << std::endl;
+                return false;
+            }
+            options.generateJavaClass = make_value<Source>(Source{ argsIter->toString() });
+        } else if (*argsIter == "-v") {
+            options.verbose = true;
+        } else {
+            Logger::error() << "unrecognized option '" << *argsIter << "'." << std::endl;
+            return false;
+        }
+
+        ++argsIter;
+    }
+
+    if (options.manifest.path.empty()) {
+        Logger::error() << "must specify manifest file with -M." << std::endl;
+        return false;
+    }
+
+    // Load the App's package name, etc.
+    if (!loadAppInfo(options.manifest, &options.appInfo)) {
+        return false;
+    }
+
+    /**
+     * Set up the file filter to ignore certain files.
+     */
+    const char* customIgnore = getenv("ANDROID_AAPT_IGNORE");
+    FileFilter fileFilter;
+    if (customIgnore && customIgnore[0]) {
+        fileFilter.setPattern(customIgnore);
+    } else {
+        fileFilter.setPattern(
+                "!.svn:!.git:!.ds_store:!*.scc:.*:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~");
+    }
+
+    /*
+     * Enumerate the files in each source directory.
+     */
+    for (const StringPiece& source : sourceDirs) {
+        if (!walkTree(source, fileFilter, options.sources)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool prepareCollect(std::vector<StringPiece>::const_iterator argsIter,
+        const std::vector<StringPiece>::const_iterator argsEndIter,
+        AaptOptions& options) {
+    options.phase = AaptOptions::Phase::Collect;
+
+    while (argsIter != argsEndIter) {
+        if (*argsIter == "--package") {
+            ++argsIter;
+            if (argsIter == argsEndIter) {
+                Logger::error() << "--package missing argument." << std::endl;
+                return false;
+            }
+            options.appInfo.package = util::utf8ToUtf16(*argsIter);
+        } else if (*argsIter == "-o") {
+            ++argsIter;
+            if (argsIter == argsEndIter) {
+                Logger::error() << "-o missing argument." << std::endl;
+                return false;
+            }
+            options.output = Source{ argsIter->toString() };
+        } else if (*argsIter == "-v") {
+            options.verbose = true;
+        } else if (argsIter->data()[0] != '-') {
+            options.sources.push_back(Source{ argsIter->toString() });
+        } else {
+            Logger::error()
+                    << "unknown option '"
+                    << *argsIter
+                    << "'."
+                    << std::endl;
+            return false;
+        }
+        ++argsIter;
+    }
+    return true;
+}
+
+bool prepareLink(std::vector<StringPiece>::const_iterator argsIter,
+        const std::vector<StringPiece>::const_iterator argsEndIter,
+        AaptOptions& options) {
+    options.phase = AaptOptions::Phase::Link;
+
+    while (argsIter != argsEndIter) {
+        if (*argsIter == "--package") {
+            ++argsIter;
+            if (argsIter == argsEndIter) {
+                Logger::error() << "--package missing argument." << std::endl;
+                return false;
+            }
+            options.appInfo.package = util::utf8ToUtf16(*argsIter);
+        } else if (*argsIter == "-o") {
+            ++argsIter;
+            if (argsIter == argsEndIter) {
+                Logger::error() << "-o missing argument." << std::endl;
+                return false;
+            }
+            options.output = Source{ argsIter->toString() };
+        } else if (*argsIter == "-I") {
+            ++argsIter;
+            if (argsIter == argsEndIter) {
+                Logger::error() << "-I missing argument." << std::endl;
+                return false;
+            }
+            options.libraries.push_back(Source{ argsIter->toString() });
+        } else if (*argsIter == "--java") {
+            ++argsIter;
+            if (argsIter == argsEndIter) {
+                Logger::error() << "--java missing argument." << std::endl;
+                return false;
+            }
+            options.generateJavaClass = make_value<Source>(Source{ argsIter->toString() });
+        } else if (*argsIter == "-v") {
+            options.verbose = true;
+        } else if (argsIter->data()[0] != '-') {
+            options.sources.push_back(Source{ argsIter->toString() });
+        } else {
+            Logger::error()
+                    << "unknown option '"
+                    << *argsIter
+                    << "'."
+                    << std::endl;
+            return false;
+        }
+        ++argsIter;
+    }
+    return true;
+}
+
+bool prepareCompile(std::vector<StringPiece>::const_iterator argsIter,
+        const std::vector<StringPiece>::const_iterator argsEndIter,
+        AaptOptions& options) {
+    options.phase = AaptOptions::Phase::Compile;
+
+    while (argsIter != argsEndIter) {
+        if (*argsIter == "--package") {
+            ++argsIter;
+            if (argsIter == argsEndIter) {
+                Logger::error() << "--package missing argument." << std::endl;
+                return false;
+            }
+            options.appInfo.package = util::utf8ToUtf16(*argsIter);
+        } else if (*argsIter == "-o") {
+            ++argsIter;
+            if (argsIter == argsEndIter) {
+                Logger::error() << "-o missing argument." << std::endl;
+                return false;
+            }
+            options.output = Source{ argsIter->toString() };
+        } else if (*argsIter == "-I") {
+            ++argsIter;
+            if (argsIter == argsEndIter) {
+                Logger::error() << "-I missing argument." << std::endl;
+                return false;
+            }
+            options.libraries.push_back(Source{ argsIter->toString() });
+        } else if (*argsIter == "-v") {
+            options.verbose = true;
+        } else if (argsIter->data()[0] != '-') {
+            options.sources.push_back(Source{ argsIter->toString() });
+        } else {
+            Logger::error()
+                    << "unknown option '"
+                    << *argsIter
+                    << "'."
+                    << std::endl;
+            return false;
+        }
+        ++argsIter;
+    }
+    return true;
+}
+
+struct CollectValuesItem {
+    Source source;
+    ConfigDescription config;
+};
+
+bool collectValues(std::shared_ptr<ResourceTable> table, const CollectValuesItem& item) {
+    std::ifstream in(item.source.path, std::ifstream::binary);
+    if (!in) {
+        Logger::error(item.source) << strerror(errno) << std::endl;
+        return false;
+    }
+
+    std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<SourceXmlPullParser>(in);
+    ResourceParser parser(table, item.source, item.config, xmlParser);
+    return parser.parse();
+}
+
+struct ResourcePathData {
+    std::u16string resourceDir;
+    std::u16string name;
+    std::string extension;
+    ConfigDescription config;
+};
+
+/**
+ * Resource file paths are expected to look like:
+ * [--/res/]type[-config]/name
+ */
+Maybe<ResourcePathData> extractResourcePathData(const Source& source) {
+    std::vector<std::string> parts = util::splitAndLowercase(source.path, '/');
+    if (parts.size() < 2) {
+        Logger::error(source) << "bad resource path." << std::endl;
+        return {};
+    }
+
+    std::string& dir = parts[parts.size() - 2];
+    StringPiece dirStr = dir;
+
+    ConfigDescription config;
+    size_t dashPos = dir.find('-');
+    if (dashPos != std::string::npos) {
+        StringPiece configStr = dirStr.substr(dashPos + 1, dir.size() - (dashPos + 1));
+        if (!ConfigDescription::parse(configStr, &config)) {
+            Logger::error(source)
+                    << "invalid configuration '"
+                    << configStr
+                    << "'."
+                    << std::endl;
+            return {};
+        }
+        dirStr = dirStr.substr(0, dashPos);
+    }
+
+    std::string& filename = parts[parts.size() - 1];
+    StringPiece name = filename;
+    StringPiece extension;
+    size_t dotPos = filename.find('.');
+    if (dotPos != std::string::npos) {
+        extension = name.substr(dotPos + 1, filename.size() - (dotPos + 1));
+        name = name.substr(0, dotPos);
+    }
+
+    return ResourcePathData{
+            util::utf8ToUtf16(dirStr),
+            util::utf8ToUtf16(name),
+            extension.toString(),
+            config
+    };
+}
+
+static bool doLegacy(std::shared_ptr<ResourceTable> table, std::shared_ptr<Resolver> resolver,
+                     const AaptOptions& options) {
+    bool error = false;
+    std::queue<CompileXml> xmlCompileQueue;
+
+    //
+    // Read values XML files and XML/PNG files.
+    // Need to parse the resource type/config/filename.
+    //
+    for (const Source& source : options.sources) {
+        Maybe<ResourcePathData> maybePathData = extractResourcePathData(source);
+        if (!maybePathData) {
+            return false;
+        }
+
+        const ResourcePathData& pathData = maybePathData.value();
+        if (pathData.resourceDir == u"values") {
+            if (options.verbose) {
+                Logger::note(source) << "collecting values..." << std::endl;
+            }
+
+            error |= !collectValues(table, CollectValuesItem{ source, pathData.config });
+            continue;
+        }
+
+        const ResourceType* type = parseResourceType(pathData.resourceDir);
+        if (!type) {
+            Logger::error(source)
+                    << "invalid resource type '"
+                    << pathData.resourceDir
+                    << "'."
+                    << std::endl;
+            return false;
+        }
+
+        ResourceName resourceName = { table->getPackage(), *type, pathData.name };
+        if (pathData.extension == "xml") {
+            if (options.verbose) {
+                Logger::note(source) << "collecting XML..." << std::endl;
+            }
+
+            error |= !collectXml(table, source, resourceName, pathData.config);
+            xmlCompileQueue.push(CompileXml{
+                    source,
+                    resourceName,
+                    pathData.config
+            });
+        } else {
+            std::unique_ptr<FileReference> fileReference = makeFileReference(
+                    table->getValueStringPool(),
+                    util::utf16ToUtf8(pathData.name) + "." + pathData.extension,
+                    *type, pathData.config);
+
+            error |= !table->addResource(resourceName, pathData.config, source.line(0),
+                                         std::move(fileReference));
+        }
+    }
+
+    if (error) {
+        return false;
+    }
+
+    versionStylesForCompat(table);
+
+    //
+    // Verify all references and data types.
+    //
+    Linker linker(table, resolver);
+    if (!linker.linkAndValidate()) {
+        Logger::error()
+                << "linking failed."
+                << std::endl;
+        return false;
+    }
+
+    const auto& unresolvedRefs = linker.getUnresolvedReferences();
+    if (!unresolvedRefs.empty()) {
+        for (const auto& entry : unresolvedRefs) {
+            for (const auto& source : entry.second) {
+                Logger::error(source)
+                        << "unresolved symbol '"
+                        << entry.first
+                        << "'."
+                        << std::endl;
+            }
+        }
+        return false;
+    }
+
+    //
+    // Compile the XML files.
+    //
+    while (!xmlCompileQueue.empty()) {
+        const CompileXml& item = xmlCompileQueue.front();
+
+        // Create the output path from the resource name.
+        std::stringstream outputPath;
+        outputPath << item.name.type;
+        if (item.config != ConfigDescription{}) {
+            outputPath << "-" << item.config.toString();
+        }
+
+        Source outSource = options.output;
+        appendPath(&outSource.path, "res");
+        appendPath(&outSource.path, outputPath.str());
+
+        if (!mkdirs(outSource.path)) {
+            Logger::error(outSource) << strerror(errno) << std::endl;
+            return false;
+        }
+
+        appendPath(&outSource.path, util::utf16ToUtf8(item.name.entry) + ".xml");
+
+        if (options.verbose) {
+            Logger::note(outSource) << "compiling XML file." << std::endl;
+        }
+
+        error |= !compileXml(resolver, item, outSource, &xmlCompileQueue);
+        xmlCompileQueue.pop();
+    }
+
+    if (error) {
+        return false;
+    }
+
+    //
+    // Compile the AndroidManifest.xml file.
+    //
+    if (!compileAndroidManifest(resolver, options)) {
+        return false;
+    }
+
+    //
+    // Generate the Java R class.
+    //
+    if (options.generateJavaClass) {
+        Source outPath = options.generateJavaClass.value();
+        if (options.verbose) {
+            Logger::note()
+                    << "writing symbols to "
+                    << outPath
+                    << "."
+                    << std::endl;
+        }
+
+        for (std::string& part : util::split(util::utf16ToUtf8(table->getPackage()), '.')) {
+            appendPath(&outPath.path, part);
+        }
+
+        if (!mkdirs(outPath.path)) {
+            Logger::error(outPath) << strerror(errno) << std::endl;
+            return false;
+        }
+
+        appendPath(&outPath.path, "R.java");
+
+        std::ofstream fout(outPath.path);
+        if (!fout) {
+            Logger::error(outPath) << strerror(errno) << std::endl;
+            return false;
+        }
+
+        JavaClassGenerator generator(table, JavaClassGenerator::Options{});
+        if (!generator.generate(fout)) {
+            Logger::error(outPath)
+                    << generator.getError()
+                    << "."
+                    << std::endl;
+            return false;
+        }
+    }
+
+    //
+    // Flatten resource table.
+    //
+    if (table->begin() != table->end()) {
+        BigBuffer buffer(1024);
+        TableFlattener::Options tableOptions;
+        tableOptions.useExtendedChunks = false;
+        TableFlattener flattener(tableOptions);
+        if (!flattener.flatten(&buffer, *table)) {
+            Logger::error()
+                    << "failed to flatten resource table->"
+                    << std::endl;
+            return false;
+        }
+
+        if (options.verbose) {
+            Logger::note()
+                    << "Final resource table size="
+                    << util::formatSize(buffer.size())
+                    << std::endl;
+        }
+
+        std::string outTable(options.output.path);
+        appendPath(&outTable, "resources.arsc");
+
+        std::ofstream fout(outTable, std::ofstream::binary);
+        if (!fout) {
+            Logger::error(Source{outTable})
+                    << strerror(errno)
+                    << "."
+                    << std::endl;
+            return false;
+        }
+
+        if (!util::writeAll(fout, buffer)) {
+            Logger::error(Source{outTable})
+                    << strerror(errno)
+                    << "."
+                    << std::endl;
+            return false;
+        }
+        fout.flush();
+    }
+    return true;
+}
+
+static bool doCollect(std::shared_ptr<ResourceTable> table, std::shared_ptr<Resolver> resolver,
+                      const AaptOptions& options) {
+    bool error = false;
+
+    //
+    // Read values XML files and XML/PNG files.
+    // Need to parse the resource type/config/filename.
+    //
+    for (const Source& source : options.sources) {
+        Maybe<ResourcePathData> maybePathData = extractResourcePathData(source);
+        if (!maybePathData) {
+            return false;
+        }
+
+        const ResourcePathData& pathData = maybePathData.value();
+        if (pathData.resourceDir == u"values") {
+            if (options.verbose) {
+                Logger::note(source) << "collecting values..." << std::endl;
+            }
+
+            error |= !collectValues(table, CollectValuesItem{ source, pathData.config });
+            continue;
+        }
+
+        const ResourceType* type = parseResourceType(pathData.resourceDir);
+        if (!type) {
+            Logger::error(source)
+                    << "invalid resource type '"
+                    << pathData.resourceDir
+                    << "'."
+                    << std::endl;
+            return false;
+        }
+
+        ResourceName resourceName = { table->getPackage(), *type, pathData.name };
+        if (pathData.extension == "xml") {
+            if (options.verbose) {
+                Logger::note(source) << "collecting XML..." << std::endl;
+            }
+
+            error |= !collectXml(table, source, resourceName, pathData.config);
+        } else {
+            std::unique_ptr<FileReference> fileReference = makeFileReference(
+                    table->getValueStringPool(),
+                    util::utf16ToUtf8(pathData.name) + "." + pathData.extension,
+                    *type,
+                    pathData.config);
+            error |= !table->addResource(resourceName, pathData.config, source.line(0),
+                                         std::move(fileReference));
+        }
+    }
+
+    if (error) {
+        return false;
+    }
+
+    Linker linker(table, resolver);
+    if (!linker.linkAndValidate()) {
+        return false;
+    }
+
+    //
+    // Flatten resource table->
+    //
+    if (table->begin() != table->end()) {
+        BigBuffer buffer(1024);
+        TableFlattener::Options tableOptions;
+        tableOptions.useExtendedChunks = true;
+        TableFlattener flattener(tableOptions);
+        if (!flattener.flatten(&buffer, *table)) {
+            Logger::error()
+                    << "failed to flatten resource table->"
+                    << std::endl;
+            return false;
+        }
+
+        std::ofstream fout(options.output.path, std::ofstream::binary);
+        if (!fout) {
+            Logger::error(options.output)
+                    << strerror(errno)
+                    << "."
+                    << std::endl;
+            return false;
+        }
+
+        if (!util::writeAll(fout, buffer)) {
+            Logger::error(options.output)
+                    << strerror(errno)
+                    << "."
+                    << std::endl;
+            return false;
+        }
+        fout.flush();
+    }
+    return true;
+}
+
+static bool doLink(std::shared_ptr<ResourceTable> table, std::shared_ptr<Resolver> resolver,
+                   const AaptOptions& options) {
+    bool error = false;
+
+    for (const Source& source : options.sources) {
+        error |= !loadBinaryResourceTable(table, source);
+    }
+
+    if (error) {
+        return false;
+    }
+
+    versionStylesForCompat(table);
+
+    Linker linker(table, resolver);
+    if (!linker.linkAndValidate()) {
+        return false;
+    }
+
+    const auto& unresolvedRefs = linker.getUnresolvedReferences();
+    if (!unresolvedRefs.empty()) {
+        for (const auto& entry : unresolvedRefs) {
+            for (const auto& source : entry.second) {
+                Logger::error(source)
+                        << "unresolved symbol '"
+                        << entry.first
+                        << "'."
+                        << std::endl;
+            }
+        }
+        return false;
+    }
+
+    //
+    // Generate the Java R class.
+    //
+    if (options.generateJavaClass) {
+        Source outPath = options.generateJavaClass.value();
+        if (options.verbose) {
+            Logger::note()
+                    << "writing symbols to "
+                    << outPath
+                    << "."
+                    << std::endl;
+        }
+
+        for (std::string& part : util::split(util::utf16ToUtf8(table->getPackage()), '.')) {
+            appendPath(&outPath.path, part);
+        }
+
+        if (!mkdirs(outPath.path)) {
+            Logger::error(outPath) << strerror(errno) << std::endl;
+            return false;
+        }
+
+        appendPath(&outPath.path, "R.java");
+
+        std::ofstream fout(outPath.path);
+        if (!fout) {
+            Logger::error(outPath) << strerror(errno) << std::endl;
+            return false;
+        }
+
+        JavaClassGenerator generator(table, JavaClassGenerator::Options{});
+        if (!generator.generate(fout)) {
+            Logger::error(outPath)
+                    << generator.getError()
+                    << "."
+                    << std::endl;
+            return false;
+        }
+    }
+
+    //
+    // Flatten resource table.
+    //
+    if (table->begin() != table->end()) {
+        BigBuffer buffer(1024);
+        TableFlattener::Options tableOptions;
+        tableOptions.useExtendedChunks = false;
+        TableFlattener flattener(tableOptions);
+        if (!flattener.flatten(&buffer, *table)) {
+            Logger::error()
+                    << "failed to flatten resource table->"
+                    << std::endl;
+            return false;
+        }
+
+        if (options.verbose) {
+            Logger::note()
+                    << "Final resource table size="
+                    << util::formatSize(buffer.size())
+                    << std::endl;
+        }
+
+        std::ofstream fout(options.output.path, std::ofstream::binary);
+        if (!fout) {
+            Logger::error(options.output)
+                    << strerror(errno)
+                    << "."
+                    << std::endl;
+            return false;
+        }
+
+        if (!util::writeAll(fout, buffer)) {
+            Logger::error(options.output)
+                    << strerror(errno)
+                    << "."
+                    << std::endl;
+            return false;
+        }
+        fout.flush();
+    }
+    return true;
+}
+
+static bool doCompile(std::shared_ptr<ResourceTable> table, std::shared_ptr<Resolver> resolver,
+                      const AaptOptions& options) {
+    std::queue<CompileXml> xmlCompileQueue;
+
+    for (const Source& source : options.sources) {
+        Maybe<ResourcePathData> maybePathData = extractResourcePathData(source);
+        if (!maybePathData) {
+            return false;
+        }
+
+        ResourcePathData& pathData = maybePathData.value();
+        const ResourceType* type = parseResourceType(pathData.resourceDir);
+        if (!type) {
+            Logger::error(source)
+                    << "invalid resource type '"
+                    << pathData.resourceDir
+                    << "'."
+                    << std::endl;
+            return false;
+        }
+
+        ResourceName resourceName = { table->getPackage(), *type, pathData.name };
+        if (pathData.extension == "xml") {
+            xmlCompileQueue.push(CompileXml{
+                    source,
+                    resourceName,
+                    pathData.config
+            });
+        } else {
+            // TODO(adamlesinski): Handle images here.
+        }
+    }
+
+    bool error = false;
+    while (!xmlCompileQueue.empty()) {
+        const CompileXml& item = xmlCompileQueue.front();
+
+        // Create the output path from the resource name.
+        std::stringstream outputPath;
+        outputPath << item.name.type;
+        if (item.config != ConfigDescription{}) {
+            outputPath << "-" << item.config.toString();
+        }
+
+        Source outSource = options.output;
+        appendPath(&outSource.path, "res");
+        appendPath(&outSource.path, outputPath.str());
+
+        if (!mkdirs(outSource.path)) {
+            Logger::error(outSource) << strerror(errno) << std::endl;
+            return false;
+        }
+
+        appendPath(&outSource.path, util::utf16ToUtf8(item.name.entry) + ".xml");
+
+        if (options.verbose) {
+            Logger::note(outSource) << "compiling XML file." << std::endl;
+        }
+
+        error |= !compileXml(resolver, item, outSource, &xmlCompileQueue);
+        xmlCompileQueue.pop();
+    }
+    return !error;
+}
+
+int main(int argc, char** argv) {
+    Logger::setLog(std::make_shared<Log>(std::cerr, std::cerr));
+
+    std::vector<StringPiece> args;
+    args.reserve(argc - 1);
+    for (int i = 1; i < argc; i++) {
+        args.emplace_back(argv[i], strlen(argv[i]));
+    }
+
+    if (args.empty()) {
+        Logger::error() << "no command specified." << std::endl;
+        return 1;
+    }
+
+    AaptOptions options;
+
+    // Check the command we're running.
+    const StringPiece& command = args.front();
+    if (command == "package") {
+        if (!prepareLegacy(std::begin(args) + 1, std::end(args), options)) {
+            return 1;
+        }
+    } else if (command == "collect") {
+        if (!prepareCollect(std::begin(args) + 1, std::end(args), options)) {
+            return 1;
+        }
+    } else if (command == "link") {
+        if (!prepareLink(std::begin(args) + 1, std::end(args), options)) {
+            return 1;
+        }
+    } else if (command == "compile") {
+        if (!prepareCompile(std::begin(args) + 1, std::end(args), options)) {
+            return 1;
+        }
+    } else {
+        Logger::error() << "unknown command '" << command << "'." << std::endl;
+        return 1;
+    }
+
+    //
+    // Verify we have some common options set.
+    //
+
+    if (options.sources.empty()) {
+        Logger::error() << "no sources specified." << std::endl;
+        return false;
+    }
+
+    if (options.output.path.empty()) {
+        Logger::error() << "no output directory specified." << std::endl;
+        return false;
+    }
+
+    if (options.appInfo.package.empty()) {
+        Logger::error() << "no package name specified." << std::endl;
+        return false;
+    }
+
+
+    //
+    // Every phase needs a resource table and a resolver/linker.
+    //
+
+    std::shared_ptr<ResourceTable> table = std::make_shared<ResourceTable>();
+    table->setPackage(options.appInfo.package);
+    if (options.appInfo.package == u"android") {
+        table->setPackageId(0x01);
+    } else {
+        table->setPackageId(0x7f);
+    }
+
+    //
+    // Load the included libraries.
+    //
+    std::shared_ptr<android::AssetManager> libraries = std::make_shared<android::AssetManager>();
+    for (const Source& source : options.libraries) {
+        if (util::stringEndsWith(source.path, ".arsc")) {
+            // We'll process these last so as to avoid a cookie issue.
+            continue;
+        }
+
+        int32_t cookie;
+        if (!libraries->addAssetPath(android::String8(source.path.data()), &cookie)) {
+            Logger::error(source) << "failed to load library." << std::endl;
+            return false;
+        }
+    }
+
+    for (const Source& source : options.libraries) {
+        if (!util::stringEndsWith(source.path, ".arsc")) {
+            // We've already processed this.
+            continue;
+        }
+
+        // Dirty hack but there is no other way to get a
+        // writeable ResTable.
+        if (!loadResTable(const_cast<android::ResTable*>(&libraries->getResources(false)),
+                          source)) {
+            return false;
+        }
+    }
+
+    // Make the resolver that will cache IDs for us.
+    std::shared_ptr<Resolver> resolver = std::make_shared<Resolver>(table, libraries);
+
+    //
+    // Dispatch to the real phase here.
+    //
+
+    bool result = true;
+    switch (options.phase) {
+        case AaptOptions::Phase::LegacyFull:
+            result = doLegacy(table, resolver, options);
+            break;
+
+        case AaptOptions::Phase::Collect:
+            result = doCollect(table, resolver, options);
+            break;
+
+        case AaptOptions::Phase::Link:
+            result = doLink(table, resolver, options);
+            break;
+
+        case AaptOptions::Phase::Compile:
+            result = doCompile(table, resolver, options);
+            break;
+    }
+
+    if (!result) {
+        Logger::error()
+                << "aapt exiting with failures."
+                << std::endl;
+        return 1;
+    }
+    return 0;
+}
diff --git a/tools/aapt2/ManifestParser.cpp b/tools/aapt2/ManifestParser.cpp
new file mode 100644
index 0000000..b8f0a43
--- /dev/null
+++ b/tools/aapt2/ManifestParser.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+#include "AppInfo.h"
+#include "Logger.h"
+#include "ManifestParser.h"
+#include "Source.h"
+#include "XmlPullParser.h"
+
+#include <string>
+
+namespace aapt {
+
+bool ManifestParser::parse(const Source& source, std::shared_ptr<XmlPullParser> parser,
+                           AppInfo* outInfo) {
+    SourceLogger logger = { source };
+
+    int depth = 0;
+    while (XmlPullParser::isGoodEvent(parser->next())) {
+        XmlPullParser::Event event = parser->getEvent();
+        if (event == XmlPullParser::Event::kEndElement) {
+            depth--;
+            continue;
+        } else if (event != XmlPullParser::Event::kStartElement) {
+            continue;
+        }
+
+        depth++;
+
+        const std::u16string& element = parser->getElementName();
+        if (depth == 1) {
+            if (element == u"manifest") {
+                if (!parseManifest(logger, parser, outInfo)) {
+                    return false;
+                }
+            } else {
+                logger.error()
+                        << "unexpected top-level element '"
+                        << element
+                        << "'."
+                        << std::endl;
+                return false;
+            }
+        } else {
+            XmlPullParser::skipCurrentElement(parser.get());
+        }
+    }
+
+    if (parser->getEvent() == XmlPullParser::Event::kBadDocument) {
+            logger.error(parser->getLineNumber())
+                << "failed to parse manifest: "
+                << parser->getLastError()
+                << "."
+                << std::endl;
+        return false;
+    }
+    return true;
+}
+
+bool ManifestParser::parseManifest(SourceLogger& logger, std::shared_ptr<XmlPullParser> parser,
+                                   AppInfo* outInfo) {
+    auto attrIter = parser->findAttribute(u"", u"package");
+    if (attrIter == parser->endAttributes() || attrIter->value.empty()) {
+        logger.error() << "no 'package' attribute found for element <manifest>." << std::endl;
+        return false;
+    }
+    outInfo->package = attrIter->value;
+    return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ManifestParser.h b/tools/aapt2/ManifestParser.h
new file mode 100644
index 0000000..f2e43d4
--- /dev/null
+++ b/tools/aapt2/ManifestParser.h
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_MANIFEST_PARSER_H
+#define AAPT_MANIFEST_PARSER_H
+
+#include "AppInfo.h"
+#include "Logger.h"
+#include "Source.h"
+#include "XmlPullParser.h"
+
+namespace aapt {
+
+/*
+ * Parses an AndroidManifest.xml file and fills in an AppInfo structure with
+ * app data.
+ */
+class ManifestParser {
+public:
+    ManifestParser() = default;
+    ManifestParser(const ManifestParser&) = delete;
+
+    bool parse(const Source& source, std::shared_ptr<XmlPullParser> parser, AppInfo* outInfo);
+
+private:
+    bool parseManifest(SourceLogger& logger, std::shared_ptr<XmlPullParser> parser,
+                       AppInfo* outInfo);
+};
+
+} // namespace aapt
+
+#endif // AAPT_MANIFEST_PARSER_H
diff --git a/tools/aapt2/ManifestParser_test.cpp b/tools/aapt2/ManifestParser_test.cpp
new file mode 100644
index 0000000..be3a6fb
--- /dev/null
+++ b/tools/aapt2/ManifestParser_test.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#include "AppInfo.h"
+#include "ManifestParser.h"
+#include "SourceXmlPullParser.h"
+
+#include <gtest/gtest.h>
+#include <sstream>
+#include <string>
+
+namespace aapt {
+
+TEST(ManifestParserTest, FindPackage) {
+    std::stringstream input;
+    input << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+             "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+             "package=\"android\">\n"
+             "</manifest>\n";
+
+    ManifestParser parser;
+    AppInfo info;
+    std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<SourceXmlPullParser>(input);
+    ASSERT_TRUE(parser.parse(Source{ "AndroidManifest.xml" }, xmlParser, &info));
+
+    EXPECT_EQ(std::u16string(u"android"), info.package);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ManifestValidator.cpp b/tools/aapt2/ManifestValidator.cpp
new file mode 100644
index 0000000..596c758
--- /dev/null
+++ b/tools/aapt2/ManifestValidator.cpp
@@ -0,0 +1,209 @@
+/*
+ * 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.
+ */
+
+#include "Logger.h"
+#include "ManifestValidator.h"
+#include "Maybe.h"
+#include "Source.h"
+#include "Util.h"
+
+#include <androidfw/ResourceTypes.h>
+
+namespace aapt {
+
+ManifestValidator::ManifestValidator(const android::ResTable& table)
+: mTable(table) {
+}
+
+bool ManifestValidator::validate(const Source& source, android::ResXMLParser* parser) {
+    SourceLogger logger(source);
+
+    android::ResXMLParser::event_code_t code;
+    while ((code = parser->next()) != android::ResXMLParser::END_DOCUMENT &&
+            code != android::ResXMLParser::BAD_DOCUMENT) {
+        if (code != android::ResXMLParser::START_TAG) {
+            continue;
+        }
+
+        size_t len = 0;
+        const StringPiece16 namespaceUri(parser->getElementNamespace(&len), len);
+        if (!namespaceUri.empty()) {
+            continue;
+        }
+
+        const StringPiece16 name(parser->getElementName(&len), len);
+        if (name.empty()) {
+            logger.error(parser->getLineNumber())
+                    << "failed to get the element name."
+                    << std::endl;
+            return false;
+        }
+
+        if (name == u"manifest") {
+            if (!validateManifest(source, parser)) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+Maybe<StringPiece16> ManifestValidator::getAttributeValue(android::ResXMLParser* parser,
+                                                          size_t idx) {
+    android::Res_value value;
+    if (parser->getAttributeValue(idx, &value) < 0) {
+        return StringPiece16();
+    }
+
+    const android::ResStringPool* pool = &parser->getStrings();
+    if (value.dataType == android::Res_value::TYPE_REFERENCE) {
+        ssize_t strIdx = mTable.resolveReference(&value, 0x10000000u);
+        if (strIdx < 0) {
+            return {};
+        }
+        pool = mTable.getTableStringBlock(strIdx);
+    }
+
+    if (value.dataType != android::Res_value::TYPE_STRING || !pool) {
+        return {};
+    }
+    return util::getString(*pool, value.data);
+}
+
+Maybe<StringPiece16> ManifestValidator::getAttributeInlineValue(android::ResXMLParser* parser,
+                                                                size_t idx) {
+    android::Res_value value;
+    if (parser->getAttributeValue(idx, &value) < 0) {
+        return StringPiece16();
+    }
+
+    if (value.dataType != android::Res_value::TYPE_STRING) {
+        return {};
+    }
+    return util::getString(parser->getStrings(), value.data);
+}
+
+bool ManifestValidator::validateInlineAttribute(android::ResXMLParser* parser, size_t idx,
+                                                SourceLogger& logger,
+                                                const StringPiece16& charSet) {
+    size_t len = 0;
+    StringPiece16 element(parser->getElementName(&len), len);
+    StringPiece16 attributeName(parser->getAttributeName(idx, &len), len);
+    Maybe<StringPiece16> result = getAttributeInlineValue(parser, idx);
+    if (!result) {
+        logger.error(parser->getLineNumber())
+                << "<"
+                << element
+                << "> must have a '"
+                << attributeName
+                << "' attribute with a string literal value."
+                << std::endl;
+        return false;
+    }
+    return validateAttributeImpl(element, attributeName, result.value(), charSet,
+                                 parser->getLineNumber(), logger);
+}
+
+bool ManifestValidator::validateAttribute(android::ResXMLParser* parser, size_t idx,
+                                          SourceLogger& logger, const StringPiece16& charSet) {
+    size_t len = 0;
+    StringPiece16 element(parser->getElementName(&len), len);
+    StringPiece16 attributeName(parser->getAttributeName(idx, &len), len);
+    Maybe<StringPiece16> result = getAttributeValue(parser, idx);
+    if (!result) {
+        logger.error(parser->getLineNumber())
+                << "<"
+                << element
+                << "> must have a '"
+                << attributeName
+                << "' attribute that points to a string."
+                << std::endl;
+        return false;
+    }
+    return validateAttributeImpl(element, attributeName, result.value(), charSet,
+                                 parser->getLineNumber(), logger);
+}
+
+bool ManifestValidator::validateAttributeImpl(const StringPiece16& element,
+                                              const StringPiece16& attributeName,
+                                              const StringPiece16& attributeValue,
+                                              const StringPiece16& charSet, size_t lineNumber,
+                                              SourceLogger& logger) {
+    StringPiece16::const_iterator badIter =
+            util::findNonAlphaNumericAndNotInSet(attributeValue, charSet);
+    if (badIter != attributeValue.end()) {
+        logger.error(lineNumber)
+                << "tag <"
+                << element
+                << "> attribute '"
+                << attributeName
+                << "' has invalid character '"
+                << *badIter
+                << "'."
+                << std::endl;
+        return false;
+    }
+
+    if (!attributeValue.empty()) {
+        StringPiece16 trimmed = util::trimWhitespace(attributeValue);
+        if (attributeValue.begin() != trimmed.begin()) {
+            logger.error(lineNumber)
+                    << "tag <"
+                    << element
+                    << "> attribute '"
+                    << attributeName
+                    << "' can not start with whitespace."
+                    << std::endl;
+            return false;
+        }
+
+        if (attributeValue.end() != trimmed.end()) {
+            logger.error(lineNumber)
+                    << "tag <"
+                    << element
+                    << "> attribute '"
+                    << attributeName
+                    << "' can not end with whitespace."
+                    << std::endl;
+            return false;
+        }
+    }
+    return true;
+}
+
+constexpr const char16_t* kPackageIdentSet = u"._";
+
+bool ManifestValidator::validateManifest(const Source& source, android::ResXMLParser* parser) {
+    bool error = false;
+    SourceLogger logger(source);
+
+    const size_t attrCount = parser->getAttributeCount();
+    for (size_t i = 0; i < attrCount; i++) {
+        size_t len = 0;
+        StringPiece16 attrNamespace(parser->getAttributeNamespace(i, &len), len);
+        StringPiece16 attrName(parser->getAttributeName(i, &len), len);
+        if (attrNamespace.empty() && attrName == u"package") {
+            error |= !validateInlineAttribute(parser, i, logger, kPackageIdentSet);
+        } else if (attrNamespace == u"android") {
+            if (attrName == u"sharedUserId") {
+                error |= !validateInlineAttribute(parser, i, logger, kPackageIdentSet);
+            }
+        }
+    }
+    return !error;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ManifestValidator.h b/tools/aapt2/ManifestValidator.h
new file mode 100644
index 0000000..3188784
--- /dev/null
+++ b/tools/aapt2/ManifestValidator.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_MANIFEST_VALIDATOR_H
+#define AAPT_MANIFEST_VALIDATOR_H
+
+#include "Logger.h"
+#include "Maybe.h"
+#include "Source.h"
+#include "StringPiece.h"
+
+#include <androidfw/ResourceTypes.h>
+
+namespace aapt {
+
+class ManifestValidator {
+public:
+    ManifestValidator(const android::ResTable& table);
+    ManifestValidator(const ManifestValidator&) = delete;
+
+    bool validate(const Source& source, android::ResXMLParser* parser);
+
+private:
+    bool validateManifest(const Source& source, android::ResXMLParser* parser);
+
+    Maybe<StringPiece16> getAttributeInlineValue(android::ResXMLParser* parser, size_t idx);
+    Maybe<StringPiece16> getAttributeValue(android::ResXMLParser* parser, size_t idx);
+
+    bool validateInlineAttribute(android::ResXMLParser* parser, size_t idx,
+                                 SourceLogger& logger, const StringPiece16& charSet);
+    bool validateAttribute(android::ResXMLParser* parser, size_t idx, SourceLogger& logger,
+                           const StringPiece16& charSet);
+    bool validateAttributeImpl(const StringPiece16& element, const StringPiece16& attributeName,
+                               const StringPiece16& attributeValue, const StringPiece16& charSet,
+                               size_t lineNumber, SourceLogger& logger);
+
+    const android::ResTable& mTable;
+};
+
+} // namespace aapt
+
+#endif // AAPT_MANIFEST_VALIDATOR_H
diff --git a/tools/aapt2/Maybe.h b/tools/aapt2/Maybe.h
new file mode 100644
index 0000000..f6a396d
--- /dev/null
+++ b/tools/aapt2/Maybe.h
@@ -0,0 +1,224 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_MAYBE_H
+#define AAPT_MAYBE_H
+
+#include <cassert>
+#include <type_traits>
+#include <utility>
+
+namespace aapt {
+
+/**
+ * Either holds a valid value of type T, or holds Nothing.
+ * The value is stored inline in this structure, so no
+ * heap memory is used when creating a Maybe<T> object.
+ */
+template <typename T>
+class Maybe {
+public:
+    /**
+     * Construct Nothing.
+     */
+    inline Maybe();
+
+    inline ~Maybe();
+
+    template <typename U>
+    inline Maybe(const Maybe<U>& rhs);
+
+    template <typename U>
+    inline Maybe(Maybe<U>&& rhs);
+
+    template <typename U>
+    inline Maybe& operator=(const Maybe<U>& rhs);
+
+    template <typename U>
+    inline Maybe& operator=(Maybe<U>&& rhs);
+
+    /**
+     * Construct a Maybe holding a value.
+     */
+    inline Maybe(const T& value);
+
+    /**
+     * Construct a Maybe holding a value.
+     */
+    inline Maybe(T&& value);
+
+    /**
+     * True if this holds a value, false if
+     * it holds Nothing.
+     */
+    inline operator bool() const;
+
+    /**
+     * Gets the value if one exists, or else
+     * panics.
+     */
+    inline T& value();
+
+    /**
+     * Gets the value if one exists, or else
+     * panics.
+     */
+    inline const T& value() const;
+
+private:
+    template <typename U>
+    friend class Maybe;
+
+    void destroy();
+
+    bool mNothing;
+
+    typename std::aligned_storage<sizeof(T), alignof(T)>::type mStorage;
+};
+
+template <typename T>
+Maybe<T>::Maybe()
+: mNothing(true) {
+}
+
+template <typename T>
+Maybe<T>::~Maybe() {
+    if (!mNothing) {
+        destroy();
+    }
+}
+
+template <typename T>
+template <typename U>
+Maybe<T>::Maybe(const Maybe<U>& rhs)
+: mNothing(rhs.mNothing) {
+    if (!rhs.mNothing) {
+        new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
+    }
+}
+
+template <typename T>
+template <typename U>
+Maybe<T>::Maybe(Maybe<U>&& rhs)
+: mNothing(rhs.mNothing) {
+    if (!rhs.mNothing) {
+        rhs.mNothing = true;
+
+        // Move the value from rhs.
+        new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
+
+        // Since the value in rhs is now Nothing,
+        // run the destructor for the value.
+        rhs.destroy();
+    }
+}
+
+template <typename T>
+template <typename U>
+Maybe<T>& Maybe<T>::operator=(const Maybe<U>& rhs) {
+    if (mNothing && rhs.mNothing) {
+        // Both are nothing, nothing to do.
+        return *this;
+    } else if  (!mNothing && !rhs.mNothing) {
+        // We both are something, so assign rhs to us.
+        reinterpret_cast<T&>(mStorage) = reinterpret_cast<const U&>(rhs.mStorage);
+    } else if (mNothing) {
+        // We are nothing but rhs is something.
+        mNothing = rhs.mNothing;
+
+        // Copy the value from rhs.
+        new (&mStorage) T(reinterpret_cast<const U&>(rhs.mStorage));
+    } else {
+        // We are something but rhs is nothing, so destroy our value.
+        mNothing = rhs.mNothing;
+        destroy();
+    }
+    return *this;
+}
+
+template <typename T>
+template <typename U>
+Maybe<T>& Maybe<T>::operator=(Maybe<U>&& rhs) {
+    if (mNothing && rhs.mNothing) {
+        // Both are nothing, nothing to do.
+        return *this;
+    } else if  (!mNothing && !rhs.mNothing) {
+        // We both are something, so move assign rhs to us.
+        rhs.mNothing = true;
+        reinterpret_cast<T&>(mStorage) = std::move(reinterpret_cast<U&>(rhs.mStorage));
+        rhs.destroy();
+    } else if (mNothing) {
+        // We are nothing but rhs is something.
+        mNothing = rhs.mNothing;
+
+        // Move the value from rhs.
+        new (&mStorage) T(std::move(reinterpret_cast<U&>(rhs.mStorage)));
+        rhs.destroy();
+    } else {
+        // We are something but rhs is nothing, so destroy our value.
+        mNothing = rhs.mNothing;
+        destroy();
+    }
+    return *this;
+}
+
+template <typename T>
+Maybe<T>::Maybe(const T& value)
+: mNothing(false) {
+    new (&mStorage) T(value);
+}
+
+template <typename T>
+Maybe<T>::Maybe(T&& value)
+: mNothing(false) {
+    new (&mStorage) T(std::forward<T>(value));
+}
+
+template <typename T>
+Maybe<T>::operator bool() const {
+    return !mNothing;
+}
+
+template <typename T>
+T& Maybe<T>::value() {
+    assert(!mNothing && "Maybe<T>::value() called on Nothing");
+    return reinterpret_cast<T&>(mStorage);
+}
+
+template <typename T>
+const T& Maybe<T>::value() const {
+    assert(!mNothing && "Maybe<T>::value() called on Nothing");
+    return reinterpret_cast<const T&>(mStorage);
+}
+
+template <typename T>
+void Maybe<T>::destroy() {
+    reinterpret_cast<T&>(mStorage).~T();
+}
+
+template <typename T>
+inline Maybe<typename std::remove_reference<T>::type> make_value(T&& value) {
+    return Maybe<typename std::remove_reference<T>::type>(std::forward<T>(value));
+}
+
+template <typename T>
+inline Maybe<T> make_nothing() {
+    return Maybe<T>();
+}
+
+} // namespace aapt
+
+#endif // AAPT_MAYBE_H
diff --git a/tools/aapt2/Maybe_test.cpp b/tools/aapt2/Maybe_test.cpp
new file mode 100644
index 0000000..348d7dd
--- /dev/null
+++ b/tools/aapt2/Maybe_test.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include <string>
+
+#include "Maybe.h"
+
+namespace aapt {
+
+struct Dummy {
+    Dummy() {
+        std::cerr << "Constructing Dummy " << (void *) this << std::endl;
+    }
+
+    Dummy(const Dummy& rhs) {
+        std::cerr << "Copying Dummy " << (void *) this << " from " << (const void*) &rhs << std::endl;
+    }
+
+    Dummy(Dummy&& rhs) {
+        std::cerr << "Moving Dummy " << (void *) this << " from " << (void*) &rhs << std::endl;
+    }
+
+    ~Dummy() {
+        std::cerr << "Destroying Dummy " << (void *) this << std::endl;
+    }
+};
+
+TEST(MaybeTest, MakeNothing) {
+    Maybe<int> val = make_nothing<int>();
+    EXPECT_FALSE(val);
+
+    Maybe<std::string> val2 = make_nothing<std::string>();
+    EXPECT_FALSE(val2);
+
+    val2 = make_nothing<std::string>();
+    EXPECT_FALSE(val2);
+}
+
+TEST(MaybeTest, MakeSomething) {
+    Maybe<int> val = make_value(23);
+    ASSERT_TRUE(val);
+    EXPECT_EQ(23, val.value());
+
+    Maybe<std::string> val2 = make_value(std::string("hey"));
+    ASSERT_TRUE(val2);
+    EXPECT_EQ(std::string("hey"), val2.value());
+}
+
+TEST(MaybeTest, Lifecycle) {
+    Maybe<Dummy> val = make_nothing<Dummy>();
+
+    Maybe<Dummy> val2 = make_value(Dummy());
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ResChunkPullParser.cpp b/tools/aapt2/ResChunkPullParser.cpp
new file mode 100644
index 0000000..78ea60e
--- /dev/null
+++ b/tools/aapt2/ResChunkPullParser.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#include "ResChunkPullParser.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <cstddef>
+
+namespace aapt {
+
+using android::ResChunk_header;
+
+ResChunkPullParser::Event ResChunkPullParser::next() {
+    if (!isGoodEvent(mEvent)) {
+        return mEvent;
+    }
+
+    if (mEvent == Event::StartDocument) {
+        mCurrentChunk = mData;
+    } else {
+        mCurrentChunk = reinterpret_cast<const ResChunk_header*>(
+                reinterpret_cast<const char*>(mCurrentChunk) + mCurrentChunk->size);
+    }
+
+    const std::ptrdiff_t diff = reinterpret_cast<const char*>(mCurrentChunk)
+            - reinterpret_cast<const char*>(mData);
+    assert(diff >= 0 && "diff is negative");
+    const size_t offset = static_cast<const size_t>(diff);
+
+    if (offset == mLen) {
+        mCurrentChunk = nullptr;
+        return (mEvent = Event::EndDocument);
+    } else if (offset + sizeof(ResChunk_header) > mLen) {
+        mLastError = "chunk is past the end of the document";
+        mCurrentChunk = nullptr;
+        return (mEvent = Event::BadDocument);
+    }
+
+    if (mCurrentChunk->headerSize < sizeof(ResChunk_header)) {
+        mLastError = "chunk has too small header";
+        mCurrentChunk = nullptr;
+        return (mEvent = Event::BadDocument);
+    } else if (mCurrentChunk->size < mCurrentChunk->headerSize) {
+        mLastError = "chunk's total size is smaller than header";
+        mCurrentChunk = nullptr;
+        return (mEvent = Event::BadDocument);
+    } else if (offset + mCurrentChunk->size > mLen) {
+        mLastError = "chunk's data extends past the end of the document";
+        mCurrentChunk = nullptr;
+        return (mEvent = Event::BadDocument);
+    }
+    return (mEvent = Event::Chunk);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ResChunkPullParser.h b/tools/aapt2/ResChunkPullParser.h
new file mode 100644
index 0000000..7366c89
--- /dev/null
+++ b/tools/aapt2/ResChunkPullParser.h
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_RES_CHUNK_PULL_PARSER_H
+#define AAPT_RES_CHUNK_PULL_PARSER_H
+
+#include <androidfw/ResourceTypes.h>
+#include <string>
+
+namespace aapt {
+
+/**
+ * A pull parser, modeled after XmlPullParser, that reads
+ * android::ResChunk_header structs from a block of data.
+ *
+ * An android::ResChunk_header specifies a type, headerSize,
+ * and size. The pull parser will verify that the chunk's size
+ * doesn't extend beyond the available data, and will iterate
+ * over each chunk in the given block of data.
+ *
+ * Processing nested chunks is done by creating a new ResChunkPullParser
+ * pointing to the data portion of a chunk.
+ */
+class ResChunkPullParser {
+public:
+    enum class Event {
+        StartDocument,
+        EndDocument,
+        BadDocument,
+
+        Chunk,
+    };
+
+    /**
+     * Returns false if the event is EndDocument or BadDocument.
+     */
+    static bool isGoodEvent(Event event);
+
+    /**
+     * Create a ResChunkPullParser to read android::ResChunk_headers
+     * from the memory pointed to by data, of len bytes.
+     */
+    ResChunkPullParser(const void* data, size_t len);
+
+    ResChunkPullParser(const ResChunkPullParser&) = delete;
+
+    Event getEvent() const;
+    const std::string& getLastError() const;
+    const android::ResChunk_header* getChunk() const;
+
+    /**
+     * Move to the next android::ResChunk_header.
+     */
+    Event next();
+
+private:
+    Event mEvent;
+    const android::ResChunk_header* mData;
+    size_t mLen;
+    const android::ResChunk_header* mCurrentChunk;
+    std::string mLastError;
+};
+
+//
+// Implementation
+//
+
+inline bool ResChunkPullParser::isGoodEvent(ResChunkPullParser::Event event) {
+    return event != Event::EndDocument && event != Event::BadDocument;
+}
+
+inline ResChunkPullParser::ResChunkPullParser(const void* data, size_t len) :
+        mEvent(Event::StartDocument),
+        mData(reinterpret_cast<const android::ResChunk_header*>(data)),
+        mLen(len),
+        mCurrentChunk(nullptr) {
+}
+
+inline ResChunkPullParser::Event ResChunkPullParser::getEvent() const {
+    return mEvent;
+}
+
+inline const std::string& ResChunkPullParser::getLastError() const {
+    return mLastError;
+}
+
+inline const android::ResChunk_header* ResChunkPullParser::getChunk() const {
+    return mCurrentChunk;
+}
+
+} // namespace aapt
+
+#endif // AAPT_RES_CHUNK_PULL_PARSER_H
diff --git a/tools/aapt2/Resolver.cpp b/tools/aapt2/Resolver.cpp
new file mode 100644
index 0000000..93b5e98
--- /dev/null
+++ b/tools/aapt2/Resolver.cpp
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+#include "Maybe.h"
+#include "Resolver.h"
+#include "Resource.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "Util.h"
+
+#include <androidfw/AssetManager.h>
+#include <androidfw/ResourceTypes.h>
+#include <memory>
+#include <vector>
+
+namespace aapt {
+
+Resolver::Resolver(std::shared_ptr<const ResourceTable> table,
+                   std::shared_ptr<const android::AssetManager> sources) :
+        mTable(table), mSources(sources) {
+}
+
+Maybe<ResourceId> Resolver::findId(const ResourceName& name) {
+    Maybe<Entry> result = findAttribute(name);
+    if (result) {
+        return result.value().id;
+    }
+    return {};
+}
+
+Maybe<Resolver::Entry> Resolver::findAttribute(const ResourceName& name) {
+    auto cacheIter = mCache.find(name);
+    if (cacheIter != std::end(mCache)) {
+        return Entry{ cacheIter->second.id, cacheIter->second.attr.get() };
+    }
+
+    const ResourceTableType* type;
+    const ResourceEntry* entry;
+    std::tie(type, entry) = mTable->findResource(name);
+    if (type && entry) {
+        Entry result = {};
+        if (mTable->getPackageId() != ResourceTable::kUnsetPackageId &&
+                type->typeId != ResourceTableType::kUnsetTypeId &&
+                entry->entryId != ResourceEntry::kUnsetEntryId) {
+            result.id = ResourceId(mTable->getPackageId(), type->typeId, entry->entryId);
+        }
+
+        if (!entry->values.empty()) {
+            visitFunc<Attribute>(*entry->values.front().value, [&result](Attribute& attr) {
+                    result.attr = &attr;
+            });
+        }
+        return result;
+    }
+
+    const CacheEntry* cacheEntry = buildCacheEntry(name);
+    if (cacheEntry) {
+        return Entry{ cacheEntry->id, cacheEntry->attr.get() };
+    }
+    return {};
+}
+
+/**
+ * This is called when we need to lookup a resource name in the AssetManager.
+ * Since the values in the AssetManager are not parsed like in a ResourceTable,
+ * we must create Attribute objects here if we find them.
+ */
+const Resolver::CacheEntry* Resolver::buildCacheEntry(const ResourceName& name) {
+    const android::ResTable& table = mSources->getResources(false);
+
+    const StringPiece16 type16 = toString(name.type);
+    ResourceId resId {
+        table.identifierForName(
+                name.entry.data(), name.entry.size(),
+                type16.data(), type16.size(),
+                name.package.data(), name.package.size())
+    };
+
+    if (!resId.isValid()) {
+        return nullptr;
+    }
+
+    CacheEntry& entry = mCache[name];
+    entry.id = resId;
+
+    //
+    // Now check to see if this resource is an Attribute.
+    //
+
+    const android::ResTable::bag_entry* bagBegin;
+    ssize_t bags = table.lockBag(resId.id, &bagBegin);
+    if (bags < 1) {
+        table.unlockBag(bagBegin);
+        return &entry;
+    }
+
+    // Look for the ATTR_TYPE key in the bag and check the types it supports.
+    uint32_t attrTypeMask = 0;
+    for (ssize_t i = 0; i < bags; i++) {
+        if (bagBegin[i].map.name.ident == android::ResTable_map::ATTR_TYPE) {
+            attrTypeMask = bagBegin[i].map.value.data;
+        }
+    }
+
+    entry.attr = util::make_unique<Attribute>(false);
+
+    if (attrTypeMask & android::ResTable_map::TYPE_ENUM ||
+            attrTypeMask & android::ResTable_map::TYPE_FLAGS) {
+        for (ssize_t i = 0; i < bags; i++) {
+            if (Res_INTERNALID(bagBegin[i].map.name.ident)) {
+                // Internal IDs are special keys, which are not enum/flag symbols, so skip.
+                continue;
+            }
+
+            android::ResTable::resource_name symbolName;
+            bool result = table.getResourceName(bagBegin[i].map.name.ident, false,
+                    &symbolName);
+            assert(result);
+            const ResourceType* type = parseResourceType(
+                    StringPiece16(symbolName.type, symbolName.typeLen));
+            assert(type);
+
+            entry.attr->symbols.push_back(Attribute::Symbol{
+                    Reference(ResourceNameRef(
+                                StringPiece16(symbolName.package, symbolName.packageLen),
+                                *type,
+                                StringPiece16(symbolName.name, symbolName.nameLen))),
+                            bagBegin[i].map.value.data
+            });
+        }
+    }
+
+    entry.attr->typeMask |= attrTypeMask;
+    table.unlockBag(bagBegin);
+    return &entry;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/Resolver.h b/tools/aapt2/Resolver.h
new file mode 100644
index 0000000..90a8cd9
--- /dev/null
+++ b/tools/aapt2/Resolver.h
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_RESOLVER_H
+#define AAPT_RESOLVER_H
+
+#include "Maybe.h"
+#include "Resource.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+
+#include <androidfw/AssetManager.h>
+#include <androidfw/ResourceTypes.h>
+#include <memory>
+#include <vector>
+
+namespace aapt {
+
+/**
+ * Resolves symbolic references (package:type/entry) into resource IDs/objects.
+ * Encapsulates the search of library sources as well as the local ResourceTable.
+ */
+class Resolver {
+public:
+    /**
+     * Creates a resolver with a local ResourceTable and an AssetManager
+     * loaded with library packages.
+     */
+    Resolver(std::shared_ptr<const ResourceTable> table,
+             std::shared_ptr<const android::AssetManager> sources);
+
+    Resolver(const Resolver&) = delete; // Not copyable.
+
+    /**
+     * Holds the result of a resource name lookup.
+     */
+    struct Entry {
+        /**
+         * The ID of the resource. ResourceId::isValid() may
+         * return false if the resource has not been assigned
+         * an ID.
+         */
+        ResourceId id;
+
+        /**
+         * If the resource is an attribute, this will point
+         * to a valid Attribute object, or else it will be
+         * nullptr.
+         */
+        const Attribute* attr;
+    };
+
+    /**
+     * Return the package to use when none is specified. This
+     * is the package name of the app being built.
+     */
+    const std::u16string& getDefaultPackage() const;
+
+    /**
+     * Returns a ResourceID if the name is found. The ResourceID
+     * may not be valid if the resource was not assigned an ID.
+     */
+    Maybe<ResourceId> findId(const ResourceName& name);
+
+    /**
+     * Returns an Entry if the name is found. Entry::attr
+     * may be nullptr if the resource is not an attribute.
+     */
+    Maybe<Entry> findAttribute(const ResourceName& name);
+
+    const android::ResTable& getResTable() const;
+
+private:
+    struct CacheEntry {
+        ResourceId id;
+        std::unique_ptr<Attribute> attr;
+    };
+
+    const CacheEntry* buildCacheEntry(const ResourceName& name);
+
+    std::shared_ptr<const ResourceTable> mTable;
+    std::shared_ptr<const android::AssetManager> mSources;
+    std::map<ResourceName, CacheEntry> mCache;
+};
+
+inline const std::u16string& Resolver::getDefaultPackage() const {
+    return mTable->getPackage();
+}
+
+inline const android::ResTable& Resolver::getResTable() const {
+    return mSources->getResources(false);
+}
+
+} // namespace aapt
+
+#endif // AAPT_RESOLVER_H
diff --git a/tools/aapt2/Resource.cpp b/tools/aapt2/Resource.cpp
new file mode 100644
index 0000000..287d8de
--- /dev/null
+++ b/tools/aapt2/Resource.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+#include "Resource.h"
+#include "StringPiece.h"
+
+#include <map>
+#include <string>
+
+namespace aapt {
+
+StringPiece16 toString(ResourceType type) {
+    switch (type) {
+        case ResourceType::kAnim:          return u"anim";
+        case ResourceType::kAnimator:      return u"animator";
+        case ResourceType::kArray:         return u"array";
+        case ResourceType::kAttr:          return u"attr";
+        case ResourceType::kAttrPrivate:   return u"attr";
+        case ResourceType::kBool:          return u"bool";
+        case ResourceType::kColor:         return u"color";
+        case ResourceType::kDimen:         return u"dimen";
+        case ResourceType::kDrawable:      return u"drawable";
+        case ResourceType::kFraction:      return u"fraction";
+        case ResourceType::kId:            return u"id";
+        case ResourceType::kInteger:       return u"integer";
+        case ResourceType::kIntegerArray:  return u"integer-array";
+        case ResourceType::kInterpolator:  return u"interpolator";
+        case ResourceType::kLayout:        return u"layout";
+        case ResourceType::kMenu:          return u"menu";
+        case ResourceType::kMipmap:        return u"mipmap";
+        case ResourceType::kPlurals:       return u"plurals";
+        case ResourceType::kRaw:           return u"raw";
+        case ResourceType::kString:        return u"string";
+        case ResourceType::kStyle:         return u"style";
+        case ResourceType::kStyleable:     return u"styleable";
+        case ResourceType::kTransition:    return u"transition";
+        case ResourceType::kXml:           return u"xml";
+    }
+    return {};
+}
+
+static const std::map<StringPiece16, ResourceType> sResourceTypeMap {
+        { u"anim", ResourceType::kAnim },
+        { u"animator", ResourceType::kAnimator },
+        { u"array", ResourceType::kArray },
+        { u"attr", ResourceType::kAttr },
+        { u"^attr-private", ResourceType::kAttrPrivate },
+        { u"bool", ResourceType::kBool },
+        { u"color", ResourceType::kColor },
+        { u"dimen", ResourceType::kDimen },
+        { u"drawable", ResourceType::kDrawable },
+        { u"fraction", ResourceType::kFraction },
+        { u"id", ResourceType::kId },
+        { u"integer", ResourceType::kInteger },
+        { u"integer-array", ResourceType::kIntegerArray },
+        { u"interpolator", ResourceType::kInterpolator },
+        { u"layout", ResourceType::kLayout },
+        { u"menu", ResourceType::kMenu },
+        { u"mipmap", ResourceType::kMipmap },
+        { u"plurals", ResourceType::kPlurals },
+        { u"raw", ResourceType::kRaw },
+        { u"string", ResourceType::kString },
+        { u"style", ResourceType::kStyle },
+        { u"styleable", ResourceType::kStyleable },
+        { u"transition", ResourceType::kTransition },
+        { u"xml", ResourceType::kXml },
+};
+
+const ResourceType* parseResourceType(const StringPiece16& str) {
+    auto iter = sResourceTypeMap.find(str);
+    if (iter == std::end(sResourceTypeMap)) {
+        return nullptr;
+    }
+    return &iter->second;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
new file mode 100644
index 0000000..3fd678e
--- /dev/null
+++ b/tools/aapt2/Resource.h
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_RESOURCE_H
+#define AAPT_RESOURCE_H
+
+#include "StringPiece.h"
+
+#include <iomanip>
+#include <string>
+#include <tuple>
+
+namespace aapt {
+
+/**
+ * The various types of resource types available. Corresponds
+ * to the 'type' in package:type/entry.
+ */
+enum class ResourceType {
+    kAnim,
+    kAnimator,
+    kArray,
+    kAttr,
+    kAttrPrivate,
+    kBool,
+    kColor,
+    kDimen,
+    kDrawable,
+    kFraction,
+    kId,
+    kInteger,
+    kIntegerArray,
+    kInterpolator,
+    kLayout,
+    kMenu,
+    kMipmap,
+    kPlurals,
+    kRaw,
+    kString,
+    kStyle,
+    kStyleable,
+    kTransition,
+    kXml,
+};
+
+StringPiece16 toString(ResourceType type);
+
+/**
+ * Returns a pointer to a valid ResourceType, or nullptr if
+ * the string was invalid.
+ */
+const ResourceType* parseResourceType(const StringPiece16& str);
+
+/**
+ * A resource's name. This can uniquely identify
+ * a resource in the ResourceTable.
+ */
+struct ResourceName {
+    std::u16string package;
+    ResourceType type;
+    std::u16string entry;
+
+    bool isValid() const;
+    bool operator<(const ResourceName& rhs) const;
+    bool operator==(const ResourceName& rhs) const;
+    bool operator!=(const ResourceName& rhs) const;
+};
+
+/**
+ * Same as ResourceName, but uses StringPieces instead.
+ * Use this if you need to avoid copying and know that
+ * the lifetime of this object is shorter than that
+ * of the original string.
+ */
+struct ResourceNameRef {
+    StringPiece16 package;
+    ResourceType type;
+    StringPiece16 entry;
+
+    ResourceNameRef() = default;
+    ResourceNameRef(const ResourceNameRef&) = default;
+    ResourceNameRef(ResourceNameRef&&) = default;
+    ResourceNameRef(const ResourceName& rhs);
+    ResourceNameRef(const StringPiece16& p, ResourceType t, const StringPiece16& e);
+    ResourceNameRef& operator=(const ResourceName& rhs);
+
+    ResourceName toResourceName() const;
+    bool isValid() const;
+
+    bool operator<(const ResourceNameRef& rhs) const;
+    bool operator==(const ResourceNameRef& rhs) const;
+    bool operator!=(const ResourceNameRef& rhs) const;
+};
+
+/**
+ * A binary identifier representing a resource. Internally it
+ * is a 32bit integer split as follows:
+ *
+ * 0xPPTTEEEE
+ *
+ * PP: 8 bit package identifier. 0x01 is reserved for system
+ *     and 0x7f is reserved for the running app.
+ * TT: 8 bit type identifier. 0x00 is invalid.
+ * EEEE: 16 bit entry identifier.
+ */
+struct ResourceId {
+    uint32_t id;
+
+    ResourceId();
+    ResourceId(const ResourceId& rhs);
+    ResourceId(uint32_t resId);
+    ResourceId(size_t p, size_t t, size_t e);
+
+    bool isValid() const;
+    uint8_t packageId() const;
+    uint8_t typeId() const;
+    uint16_t entryId() const;
+    bool operator<(const ResourceId& rhs) const;
+};
+
+//
+// ResourceId implementation.
+//
+
+inline ResourceId::ResourceId() : id(0) {
+}
+
+inline ResourceId::ResourceId(const ResourceId& rhs) : id(rhs.id) {
+}
+
+inline ResourceId::ResourceId(uint32_t resId) : id(resId) {
+}
+
+inline ResourceId::ResourceId(size_t p, size_t t, size_t e) : id(0) {
+    if (p > std::numeric_limits<uint8_t>::max() ||
+            t > std::numeric_limits<uint8_t>::max() ||
+            e > std::numeric_limits<uint16_t>::max()) {
+        // This will leave the ResourceId in an invalid state.
+        return;
+    }
+
+    id = (static_cast<uint8_t>(p) << 24) |
+         (static_cast<uint8_t>(t) << 16) |
+         static_cast<uint16_t>(e);
+}
+
+inline bool ResourceId::isValid() const {
+    return (id & 0xff000000u) != 0 && (id & 0x00ff0000u) != 0;
+}
+
+inline uint8_t ResourceId::packageId() const {
+    return static_cast<uint8_t>(id >> 24);
+}
+
+inline uint8_t ResourceId::typeId() const {
+    return static_cast<uint8_t>(id >> 16);
+}
+
+inline uint16_t ResourceId::entryId() const {
+    return static_cast<uint16_t>(id);
+}
+
+inline bool ResourceId::operator<(const ResourceId& rhs) const {
+    return id < rhs.id;
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out,
+        const ResourceId& resId) {
+    std::ios_base::fmtflags oldFlags = out.flags();
+    char oldFill = out.fill();
+    out << "0x" << std::internal << std::setfill('0') << std::setw(8)
+        << std::hex << resId.id;
+    out.flags(oldFlags);
+    out.fill(oldFill);
+    return out;
+}
+
+//
+// ResourceType implementation.
+//
+
+inline ::std::ostream& operator<<(::std::ostream& out,
+        const ResourceType& val) {
+    return out << toString(val);
+}
+
+//
+// ResourceName implementation.
+//
+
+inline bool ResourceName::isValid() const {
+    return !package.empty() && !entry.empty();
+}
+
+inline bool ResourceName::operator<(const ResourceName& rhs) const {
+    return std::tie(package, type, entry)
+            < std::tie(rhs.package, rhs.type, rhs.entry);
+}
+
+inline bool ResourceName::operator==(const ResourceName& rhs) const {
+    return std::tie(package, type, entry)
+            == std::tie(rhs.package, rhs.type, rhs.entry);
+}
+
+inline bool ResourceName::operator!=(const ResourceName& rhs) const {
+    return std::tie(package, type, entry)
+            != std::tie(rhs.package, rhs.type, rhs.entry);
+}
+
+//
+// ResourceNameRef implementation.
+//
+
+inline ResourceNameRef::ResourceNameRef(const ResourceName& rhs) :
+        package(rhs.package), type(rhs.type), entry(rhs.entry) {
+}
+
+inline ResourceNameRef::ResourceNameRef(const StringPiece16& p, ResourceType t,
+                                        const StringPiece16& e) :
+        package(p), type(t), entry(e) {
+}
+
+inline ResourceNameRef& ResourceNameRef::operator=(const ResourceName& rhs) {
+    package = rhs.package;
+    type = rhs.type;
+    entry = rhs.entry;
+    return *this;
+}
+
+inline ResourceName ResourceNameRef::toResourceName() const {
+    return { package.toString(), type, entry.toString() };
+}
+
+inline bool ResourceNameRef::isValid() const {
+    return !package.empty() && !entry.empty();
+}
+
+inline bool ResourceNameRef::operator<(const ResourceNameRef& rhs) const {
+    return std::tie(package, type, entry)
+            < std::tie(rhs.package, rhs.type, rhs.entry);
+}
+
+inline bool ResourceNameRef::operator==(const ResourceNameRef& rhs) const {
+    return std::tie(package, type, entry)
+            == std::tie(rhs.package, rhs.type, rhs.entry);
+}
+
+inline bool ResourceNameRef::operator!=(const ResourceNameRef& rhs) const {
+    return std::tie(package, type, entry)
+            != std::tie(rhs.package, rhs.type, rhs.entry);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out,
+        const ResourceNameRef& name) {
+    if (!name.package.empty()) {
+        out << name.package << ":";
+    }
+    return out << name.type << "/" << name.entry;
+}
+
+} // namespace aapt
+
+#endif // AAPT_RESOURCE_H
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
new file mode 100644
index 0000000..d3720c4
--- /dev/null
+++ b/tools/aapt2/ResourceParser.cpp
@@ -0,0 +1,1317 @@
+/*
+ * 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.
+ */
+
+#include "Logger.h"
+#include "ResourceParser.h"
+#include "ResourceValues.h"
+#include "ScopedXmlPullParser.h"
+#include "SourceXmlPullParser.h"
+#include "Util.h"
+#include "XliffXmlPullParser.h"
+
+namespace aapt {
+
+void ResourceParser::extractResourceName(const StringPiece16& str, StringPiece16* outPackage,
+                                         StringPiece16* outType, StringPiece16* outEntry) {
+    const char16_t* start = str.data();
+    const char16_t* end = start + str.size();
+    const char16_t* current = start;
+    while (current != end) {
+        if (outType->size() == 0 && *current == u'/') {
+            outType->assign(start, current - start);
+            start = current + 1;
+        } else if (outPackage->size() == 0 && *current == u':') {
+            outPackage->assign(start, current - start);
+            start = current + 1;
+        }
+        current++;
+    }
+    outEntry->assign(start, end - start);
+}
+
+bool ResourceParser::tryParseReference(const StringPiece16& str, ResourceNameRef* outRef,
+                                       bool* outCreate, bool* outPrivate) {
+    StringPiece16 trimmedStr(util::trimWhitespace(str));
+    if (trimmedStr.empty()) {
+        return false;
+    }
+
+    if (trimmedStr.data()[0] == u'@') {
+        size_t offset = 1;
+        *outCreate = false;
+        if (trimmedStr.data()[1] == u'+') {
+            *outCreate = true;
+            offset += 1;
+        } else if (trimmedStr.data()[1] == u'*') {
+            *outPrivate = true;
+            offset += 1;
+        }
+        StringPiece16 package;
+        StringPiece16 type;
+        StringPiece16 entry;
+        extractResourceName(trimmedStr.substr(offset, trimmedStr.size() - offset),
+                            &package, &type, &entry);
+
+        const ResourceType* parsedType = parseResourceType(type);
+        if (!parsedType) {
+            return false;
+        }
+
+        if (*outCreate && *parsedType != ResourceType::kId) {
+            return false;
+        }
+
+        outRef->package = package;
+        outRef->type = *parsedType;
+        outRef->entry = entry;
+        return true;
+    }
+    return false;
+}
+
+bool ResourceParser::tryParseAttributeReference(const StringPiece16& str,
+                                                ResourceNameRef* outRef) {
+    StringPiece16 trimmedStr(util::trimWhitespace(str));
+    if (trimmedStr.empty()) {
+        return false;
+    }
+
+    if (*trimmedStr.data() == u'?') {
+        StringPiece16 package;
+        StringPiece16 type;
+        StringPiece16 entry;
+        extractResourceName(trimmedStr.substr(1, trimmedStr.size() - 1), &package, &type, &entry);
+
+        if (!type.empty() && type != u"attr") {
+            return false;
+        }
+
+        outRef->package = package;
+        outRef->type = ResourceType::kAttr;
+        outRef->entry = entry;
+        return true;
+    }
+    return false;
+}
+
+std::unique_ptr<Reference> ResourceParser::tryParseReference(const StringPiece16& str,
+                                                             const StringPiece16& defaultPackage,
+                                                             bool* outCreate) {
+    ResourceNameRef ref;
+    bool privateRef = false;
+    if (tryParseReference(str, &ref, outCreate, &privateRef)) {
+        if (ref.package.empty()) {
+            ref.package = defaultPackage;
+        }
+        std::unique_ptr<Reference> value = util::make_unique<Reference>(ref);
+        value->privateReference = privateRef;
+        return value;
+    }
+
+    if (tryParseAttributeReference(str, &ref)) {
+        if (ref.package.empty()) {
+            ref.package = defaultPackage;
+        }
+        *outCreate = false;
+        return util::make_unique<Reference>(ref, Reference::Type::kAttribute);
+    }
+    return {};
+}
+
+std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseNullOrEmpty(const StringPiece16& str) {
+    StringPiece16 trimmedStr(util::trimWhitespace(str));
+    uint32_t data = 0;
+    if (trimmedStr == u"@null") {
+        data = android::Res_value::DATA_NULL_UNDEFINED;
+    } else if (trimmedStr == u"@empty") {
+        data = android::Res_value::DATA_NULL_EMPTY;
+    } else {
+        return {};
+    }
+
+    android::Res_value value = {};
+    value.dataType = android::Res_value::TYPE_NULL;
+    value.data = data;
+    return util::make_unique<BinaryPrimitive>(value);
+}
+
+std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseEnumSymbol(const Attribute& enumAttr,
+                                                                    const StringPiece16& str) {
+    StringPiece16 trimmedStr(util::trimWhitespace(str));
+    for (const auto& entry : enumAttr.symbols) {
+        // Enum symbols are stored as @package:id/symbol resources,
+        // so we need to match against the 'entry' part of the identifier.
+        const ResourceName& enumSymbolResourceName = entry.symbol.name;
+        if (trimmedStr == enumSymbolResourceName.entry) {
+            android::Res_value value = {};
+            value.dataType = android::Res_value::TYPE_INT_DEC;
+            value.data = entry.value;
+            return util::make_unique<BinaryPrimitive>(value);
+        }
+    }
+    return {};
+}
+
+std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseFlagSymbol(const Attribute& flagAttr,
+                                                                    const StringPiece16& str) {
+    android::Res_value flags = {};
+    flags.dataType = android::Res_value::TYPE_INT_DEC;
+
+    for (StringPiece16 part : util::tokenize(str, u'|')) {
+        StringPiece16 trimmedPart = util::trimWhitespace(part);
+
+        bool flagSet = false;
+        for (const auto& entry : flagAttr.symbols) {
+            // Flag symbols are stored as @package:id/symbol resources,
+            // so we need to match against the 'entry' part of the identifier.
+            const ResourceName& flagSymbolResourceName = entry.symbol.name;
+            if (trimmedPart == flagSymbolResourceName.entry) {
+                flags.data |= entry.value;
+                flagSet = true;
+                break;
+            }
+        }
+
+        if (!flagSet) {
+            return {};
+        }
+    }
+    return util::make_unique<BinaryPrimitive>(flags);
+}
+
+static uint32_t parseHex(char16_t c, bool* outError) {
+   if (c >= u'0' && c <= u'9') {
+        return c - u'0';
+    } else if (c >= u'a' && c <= u'f') {
+        return c - u'a' + 0xa;
+    } else if (c >= u'A' && c <= u'F') {
+        return c - u'A' + 0xa;
+    } else {
+        *outError = true;
+        return 0xffffffffu;
+    }
+}
+
+std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseColor(const StringPiece16& str) {
+    StringPiece16 colorStr(util::trimWhitespace(str));
+    const char16_t* start = colorStr.data();
+    const size_t len = colorStr.size();
+    if (len == 0 || start[0] != u'#') {
+        return {};
+    }
+
+    android::Res_value value = {};
+    bool error = false;
+    if (len == 4) {
+        value.dataType = android::Res_value::TYPE_INT_COLOR_RGB4;
+        value.data = 0xff000000u;
+        value.data |= parseHex(start[1], &error) << 20;
+        value.data |= parseHex(start[1], &error) << 16;
+        value.data |= parseHex(start[2], &error) << 12;
+        value.data |= parseHex(start[2], &error) << 8;
+        value.data |= parseHex(start[3], &error) << 4;
+        value.data |= parseHex(start[3], &error);
+    } else if (len == 5) {
+        value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB4;
+        value.data |= parseHex(start[1], &error) << 28;
+        value.data |= parseHex(start[1], &error) << 24;
+        value.data |= parseHex(start[2], &error) << 20;
+        value.data |= parseHex(start[2], &error) << 16;
+        value.data |= parseHex(start[3], &error) << 12;
+        value.data |= parseHex(start[3], &error) << 8;
+        value.data |= parseHex(start[4], &error) << 4;
+        value.data |= parseHex(start[4], &error);
+    } else if (len == 7) {
+        value.dataType = android::Res_value::TYPE_INT_COLOR_RGB8;
+        value.data = 0xff000000u;
+        value.data |= parseHex(start[1], &error) << 20;
+        value.data |= parseHex(start[2], &error) << 16;
+        value.data |= parseHex(start[3], &error) << 12;
+        value.data |= parseHex(start[4], &error) << 8;
+        value.data |= parseHex(start[5], &error) << 4;
+        value.data |= parseHex(start[6], &error);
+    } else if (len == 9) {
+        value.dataType = android::Res_value::TYPE_INT_COLOR_ARGB8;
+        value.data |= parseHex(start[1], &error) << 28;
+        value.data |= parseHex(start[2], &error) << 24;
+        value.data |= parseHex(start[3], &error) << 20;
+        value.data |= parseHex(start[4], &error) << 16;
+        value.data |= parseHex(start[5], &error) << 12;
+        value.data |= parseHex(start[6], &error) << 8;
+        value.data |= parseHex(start[7], &error) << 4;
+        value.data |= parseHex(start[8], &error);
+    } else {
+        return {};
+    }
+    return error ? std::unique_ptr<BinaryPrimitive>() : util::make_unique<BinaryPrimitive>(value);
+}
+
+std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseBool(const StringPiece16& str) {
+    StringPiece16 trimmedStr(util::trimWhitespace(str));
+    uint32_t data = 0;
+    if (trimmedStr == u"true" || trimmedStr == u"TRUE") {
+        data = 1;
+    } else if (trimmedStr != u"false" && trimmedStr != u"FALSE") {
+        return {};
+    }
+    android::Res_value value = {};
+    value.dataType = android::Res_value::TYPE_INT_BOOLEAN;
+    value.data = data;
+    return util::make_unique<BinaryPrimitive>(value);
+}
+
+std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseInt(const StringPiece16& str) {
+    android::Res_value value;
+    if (!android::ResTable::stringToInt(str.data(), str.size(), &value)) {
+        return {};
+    }
+    return util::make_unique<BinaryPrimitive>(value);
+}
+
+std::unique_ptr<BinaryPrimitive> ResourceParser::tryParseFloat(const StringPiece16& str) {
+    android::Res_value value;
+    if (!android::ResTable::stringToFloat(str.data(), str.size(), &value)) {
+        return {};
+    }
+    return util::make_unique<BinaryPrimitive>(value);
+}
+
+uint32_t ResourceParser::androidTypeToAttributeTypeMask(uint16_t type) {
+    switch (type) {
+        case android::Res_value::TYPE_NULL:
+        case android::Res_value::TYPE_REFERENCE:
+        case android::Res_value::TYPE_ATTRIBUTE:
+        case android::Res_value::TYPE_DYNAMIC_REFERENCE:
+            return android::ResTable_map::TYPE_REFERENCE;
+
+        case android::Res_value::TYPE_STRING:
+            return android::ResTable_map::TYPE_STRING;
+
+        case android::Res_value::TYPE_FLOAT:
+            return android::ResTable_map::TYPE_FLOAT;
+
+        case android::Res_value::TYPE_DIMENSION:
+            return android::ResTable_map::TYPE_DIMENSION;
+
+        case android::Res_value::TYPE_FRACTION:
+            return android::ResTable_map::TYPE_FRACTION;
+
+        case android::Res_value::TYPE_INT_DEC:
+        case android::Res_value::TYPE_INT_HEX:
+            return android::ResTable_map::TYPE_INTEGER |
+                    android::ResTable_map::TYPE_ENUM |
+                    android::ResTable_map::TYPE_FLAGS;
+
+        case android::Res_value::TYPE_INT_BOOLEAN:
+            return android::ResTable_map::TYPE_BOOLEAN;
+
+        case android::Res_value::TYPE_INT_COLOR_ARGB8:
+        case android::Res_value::TYPE_INT_COLOR_RGB8:
+        case android::Res_value::TYPE_INT_COLOR_ARGB4:
+        case android::Res_value::TYPE_INT_COLOR_RGB4:
+            return android::ResTable_map::TYPE_COLOR;
+
+        default:
+            return 0;
+    };
+}
+
+std::unique_ptr<Item> ResourceParser::parseItemForAttribute(
+        const StringPiece16& value, uint32_t typeMask, const StringPiece16& defaultPackage,
+        std::function<void(const ResourceName&)> onCreateReference) {
+    std::unique_ptr<BinaryPrimitive> nullOrEmpty = tryParseNullOrEmpty(value);
+    if (nullOrEmpty) {
+        return std::move(nullOrEmpty);
+    }
+
+    bool create = false;
+    std::unique_ptr<Reference> reference = tryParseReference(value, defaultPackage, &create);
+    if (reference) {
+        if (create && onCreateReference) {
+            onCreateReference(reference->name);
+        }
+        return std::move(reference);
+    }
+
+    if (typeMask & android::ResTable_map::TYPE_COLOR) {
+        // Try parsing this as a color.
+        std::unique_ptr<BinaryPrimitive> color = tryParseColor(value);
+        if (color) {
+            return std::move(color);
+        }
+    }
+
+    if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
+        // Try parsing this as a boolean.
+        std::unique_ptr<BinaryPrimitive> boolean = tryParseBool(value);
+        if (boolean) {
+            return std::move(boolean);
+        }
+    }
+
+    if (typeMask & android::ResTable_map::TYPE_INTEGER) {
+        // Try parsing this as an integer.
+        std::unique_ptr<BinaryPrimitive> integer = tryParseInt(value);
+        if (integer) {
+            return std::move(integer);
+        }
+    }
+
+    const uint32_t floatMask = android::ResTable_map::TYPE_FLOAT |
+            android::ResTable_map::TYPE_DIMENSION |
+            android::ResTable_map::TYPE_FRACTION;
+    if (typeMask & floatMask) {
+        // Try parsing this as a float.
+        std::unique_ptr<BinaryPrimitive> floatingPoint = tryParseFloat(value);
+        if (floatingPoint) {
+            if (typeMask & androidTypeToAttributeTypeMask(floatingPoint->value.dataType)) {
+                return std::move(floatingPoint);
+            }
+        }
+    }
+    return {};
+}
+
+/**
+ * We successively try to parse the string as a resource type that the Attribute
+ * allows.
+ */
+std::unique_ptr<Item> ResourceParser::parseItemForAttribute(
+        const StringPiece16& str, const Attribute& attr, const StringPiece16& defaultPackage,
+        std::function<void(const ResourceName&)> onCreateReference) {
+    const uint32_t typeMask = attr.typeMask;
+    std::unique_ptr<Item> value = parseItemForAttribute(str, typeMask, defaultPackage,
+                                                        onCreateReference);
+    if (value) {
+        return value;
+    }
+
+    if (typeMask & android::ResTable_map::TYPE_ENUM) {
+        // Try parsing this as an enum.
+        std::unique_ptr<BinaryPrimitive> enumValue = tryParseEnumSymbol(attr, str);
+        if (enumValue) {
+            return std::move(enumValue);
+        }
+    }
+
+    if (typeMask & android::ResTable_map::TYPE_FLAGS) {
+        // Try parsing this as a flag.
+        std::unique_ptr<BinaryPrimitive> flagValue = tryParseFlagSymbol(attr, str);
+        if (flagValue) {
+            return std::move(flagValue);
+        }
+    }
+    return {};
+}
+
+ResourceParser::ResourceParser(const std::shared_ptr<ResourceTable>& table, const Source& source,
+                               const ConfigDescription& config,
+                               const std::shared_ptr<XmlPullParser>& parser) :
+        mTable(table), mSource(source), mConfig(config), mLogger(source),
+        mParser(std::make_shared<XliffXmlPullParser>(parser)) {
+}
+
+/**
+ * Build a string from XML that converts nested elements into Span objects.
+ */
+bool ResourceParser::flattenXmlSubtree(XmlPullParser* parser, std::u16string* outRawString,
+                                       StyleString* outStyleString) {
+    std::vector<Span> spanStack;
+
+    outRawString->clear();
+    outStyleString->spans.clear();
+    util::StringBuilder builder;
+    size_t depth = 1;
+    while (XmlPullParser::isGoodEvent(parser->next())) {
+        const XmlPullParser::Event event = parser->getEvent();
+        if (event == XmlPullParser::Event::kEndElement) {
+            depth--;
+            if (depth == 0) {
+                break;
+            }
+
+            spanStack.back().lastChar = builder.str().size();
+            outStyleString->spans.push_back(spanStack.back());
+            spanStack.pop_back();
+
+        } else if (event == XmlPullParser::Event::kText) {
+            // TODO(adamlesinski): Verify format strings.
+            outRawString->append(parser->getText());
+            builder.append(parser->getText());
+
+        } else if (event == XmlPullParser::Event::kStartElement) {
+            if (parser->getElementNamespace().size() > 0) {
+                mLogger.warn(parser->getLineNumber())
+                        << "skipping element '"
+                        << parser->getElementName()
+                        << "' with unknown namespace '"
+                        << parser->getElementNamespace()
+                        << "'."
+                        << std::endl;
+                XmlPullParser::skipCurrentElement(parser);
+                continue;
+            }
+            depth++;
+
+            // Build a span object out of the nested element.
+            std::u16string spanName = parser->getElementName();
+            const auto endAttrIter = parser->endAttributes();
+            for (auto attrIter = parser->beginAttributes(); attrIter != endAttrIter; ++attrIter) {
+                spanName += u";";
+                spanName += attrIter->name;
+                spanName += u"=";
+                spanName += attrIter->value;
+            }
+
+            if (builder.str().size() > std::numeric_limits<uint32_t>::max()) {
+                mLogger.error(parser->getLineNumber())
+                        << "style string '"
+                        << builder.str()
+                        << "' is too long."
+                        << std::endl;
+                return false;
+            }
+            spanStack.push_back(Span{ spanName, static_cast<uint32_t>(builder.str().size()) });
+
+        } else if (event == XmlPullParser::Event::kComment) {
+            // Skip
+        } else {
+            mLogger.warn(parser->getLineNumber())
+                    << "unknown event "
+                    << event
+                    << "."
+                    << std::endl;
+        }
+    }
+    assert(spanStack.empty() && "spans haven't been fully processed");
+
+    outStyleString->str = builder.str();
+    return true;
+}
+
+bool ResourceParser::parse() {
+    while (XmlPullParser::isGoodEvent(mParser->next())) {
+        if (mParser->getEvent() != XmlPullParser::Event::kStartElement) {
+            continue;
+        }
+
+        ScopedXmlPullParser parser(mParser.get());
+        if (!parser.getElementNamespace().empty() ||
+                parser.getElementName() != u"resources") {
+            mLogger.error(parser.getLineNumber())
+                    << "root element must be <resources> in the global namespace."
+                    << std::endl;
+            return false;
+        }
+
+        if (!parseResources(&parser)) {
+            return false;
+        }
+    }
+
+    if (mParser->getEvent() == XmlPullParser::Event::kBadDocument) {
+        mLogger.error(mParser->getLineNumber())
+                << mParser->getLastError()
+                << std::endl;
+        return false;
+    }
+    return true;
+}
+
+bool ResourceParser::parseResources(XmlPullParser* parser) {
+    bool success = true;
+
+    std::u16string comment;
+    while (XmlPullParser::isGoodEvent(parser->next())) {
+        const XmlPullParser::Event event = parser->getEvent();
+        if (event == XmlPullParser::Event::kComment) {
+            comment = parser->getComment();
+            continue;
+        }
+
+        if (event == XmlPullParser::Event::kText) {
+            if (!util::trimWhitespace(parser->getText()).empty()) {
+                comment = u"";
+            }
+            continue;
+        }
+
+        if (event != XmlPullParser::Event::kStartElement) {
+            continue;
+        }
+
+        ScopedXmlPullParser childParser(parser);
+
+        if (!childParser.getElementNamespace().empty()) {
+            // Skip unknown namespace.
+            continue;
+        }
+
+        StringPiece16 name = childParser.getElementName();
+        if (name == u"skip" || name == u"eat-comment") {
+            continue;
+        }
+
+        if (name == u"private-symbols") {
+            // Handle differently.
+            mLogger.note(childParser.getLineNumber())
+                    << "got a <private-symbols> tag."
+                    << std::endl;
+            continue;
+        }
+
+        const auto endAttrIter = childParser.endAttributes();
+        auto attrIter = childParser.findAttribute(u"", u"name");
+        if (attrIter == endAttrIter || attrIter->value.empty()) {
+            mLogger.error(childParser.getLineNumber())
+                    << "<" << name << "> tag must have a 'name' attribute."
+                    << std::endl;
+            success = false;
+            continue;
+        }
+
+        // Copy because our iterator will go out of scope when
+        // we parse more XML.
+        std::u16string attributeName = attrIter->value;
+
+        if (name == u"item") {
+            // Items simply have their type encoded in the type attribute.
+            auto typeIter = childParser.findAttribute(u"", u"type");
+            if (typeIter == endAttrIter || typeIter->value.empty()) {
+                mLogger.error(childParser.getLineNumber())
+                        << "<item> must have a 'type' attribute."
+                        << std::endl;
+                success = false;
+                continue;
+            }
+            name = typeIter->value;
+        }
+
+        if (name == u"id") {
+            success &= mTable->addResource(ResourceNameRef{ {}, ResourceType::kId, attributeName },
+                                           {}, mSource.line(childParser.getLineNumber()),
+                                           util::make_unique<Id>());
+        } else if (name == u"string") {
+            success &= parseString(&childParser,
+                                   ResourceNameRef{ {}, ResourceType::kString, attributeName });
+        } else if (name == u"color") {
+            success &= parseColor(&childParser,
+                                  ResourceNameRef{ {}, ResourceType::kColor, attributeName });
+        } else if (name == u"drawable") {
+            success &= parseColor(&childParser,
+                                  ResourceNameRef{ {}, ResourceType::kDrawable, attributeName });
+        } else if (name == u"bool") {
+            success &= parsePrimitive(&childParser,
+                                      ResourceNameRef{ {}, ResourceType::kBool, attributeName });
+        } else if (name == u"integer") {
+            success &= parsePrimitive(
+                    &childParser,
+                    ResourceNameRef{ {}, ResourceType::kInteger, attributeName });
+        } else if (name == u"dimen") {
+            success &= parsePrimitive(&childParser,
+                                      ResourceNameRef{ {}, ResourceType::kDimen, attributeName });
+        } else if (name == u"fraction") {
+//          success &= parsePrimitive(
+//                  &childParser,
+//                  ResourceNameRef{ {}, ResourceType::kFraction, attributeName });
+        } else if (name == u"style") {
+            success &= parseStyle(&childParser,
+                                  ResourceNameRef{ {}, ResourceType::kStyle, attributeName });
+        } else if (name == u"plurals") {
+            success &= parsePlural(&childParser,
+                                   ResourceNameRef{ {}, ResourceType::kPlurals, attributeName });
+        } else if (name == u"array") {
+            success &= parseArray(&childParser,
+                                  ResourceNameRef{ {}, ResourceType::kArray, attributeName },
+                                  android::ResTable_map::TYPE_ANY);
+        } else if (name == u"string-array") {
+            success &= parseArray(&childParser,
+                                  ResourceNameRef{ {}, ResourceType::kArray, attributeName },
+                                  android::ResTable_map::TYPE_STRING);
+        } else if (name == u"integer-array") {
+            success &= parseArray(&childParser,
+                                  ResourceNameRef{ {}, ResourceType::kArray, attributeName },
+                                  android::ResTable_map::TYPE_INTEGER);
+        } else if (name == u"public") {
+            success &= parsePublic(&childParser, attributeName);
+        } else if (name == u"declare-styleable") {
+            success &= parseDeclareStyleable(
+                    &childParser,
+                    ResourceNameRef{ {}, ResourceType::kStyleable, attributeName });
+        } else if (name == u"attr") {
+            success &= parseAttr(&childParser,
+                                 ResourceNameRef{ {}, ResourceType::kAttr, attributeName });
+        } else if (name == u"bag") {
+        } else if (name == u"public-padding") {
+        } else if (name == u"java-symbol") {
+        } else if (name == u"add-resource") {
+       }
+    }
+
+    if (parser->getEvent() == XmlPullParser::Event::kBadDocument) {
+        mLogger.error(parser->getLineNumber())
+                << parser->getLastError()
+                << std::endl;
+        return false;
+    }
+    return success;
+}
+
+
+
+enum {
+    kAllowRawString = true,
+    kNoRawString = false
+};
+
+/**
+ * Reads the entire XML subtree and attempts to parse it as some Item,
+ * with typeMask denoting which items it can be. If allowRawValue is
+ * true, a RawString is returned if the XML couldn't be parsed as
+ * an Item. If allowRawValue is false, nullptr is returned in this
+ * case.
+ */
+std::unique_ptr<Item> ResourceParser::parseXml(XmlPullParser* parser, uint32_t typeMask,
+                                               bool allowRawValue) {
+    const size_t beginXmlLine = parser->getLineNumber();
+
+    std::u16string rawValue;
+    StyleString styleString;
+    if (!flattenXmlSubtree(parser, &rawValue, &styleString)) {
+        return {};
+    }
+
+    StringPool& pool = mTable->getValueStringPool();
+
+    if (!styleString.spans.empty()) {
+        // This can only be a StyledString.
+        return util::make_unique<StyledString>(
+                pool.makeRef(styleString, StringPool::Context{ 1, mConfig }));
+    }
+
+    auto onCreateReference = [&](const ResourceName& name) {
+        mTable->addResource(name, {}, mSource.line(beginXmlLine), util::make_unique<Id>());
+    };
+
+    // Process the raw value.
+    std::unique_ptr<Item> processedItem = parseItemForAttribute(rawValue, typeMask,
+                                                                mTable->getPackage(),
+                                                                onCreateReference);
+    if (processedItem) {
+        return processedItem;
+    }
+
+    // Try making a regular string.
+    if (typeMask & android::ResTable_map::TYPE_STRING) {
+        // Use the trimmed, escaped string.
+        return util::make_unique<String>(
+                pool.makeRef(styleString.str, StringPool::Context{ 1, mConfig }));
+    }
+
+    // We can't parse this so return a RawString if we are allowed.
+    if (allowRawValue) {
+        return util::make_unique<RawString>(
+                pool.makeRef(rawValue, StringPool::Context{ 1, mConfig }));
+    }
+    return {};
+}
+
+bool ResourceParser::parseString(XmlPullParser* parser, const ResourceNameRef& resourceName) {
+    const SourceLine source = mSource.line(parser->getLineNumber());
+
+    // Mark the string as untranslateable if needed.
+    const auto endAttrIter = parser->endAttributes();
+    auto attrIter = parser->findAttribute(u"", u"untranslateable");
+    // bool untranslateable = attrIter != endAttrIter;
+    // TODO(adamlesinski): Do something with this (mark the string).
+
+    // Deal with the product.
+    attrIter = parser->findAttribute(u"", u"product");
+    if (attrIter != endAttrIter) {
+        if (attrIter->value != u"default" && attrIter->value != u"phone") {
+            // TODO(adamlesinski): Match products.
+            return true;
+        }
+    }
+
+    std::unique_ptr<Item> processedItem = parseXml(parser, android::ResTable_map::TYPE_STRING,
+                                                   kNoRawString);
+    if (!processedItem) {
+        mLogger.error(source.line)
+                << "not a valid string."
+                << std::endl;
+        return false;
+    }
+
+    return mTable->addResource(resourceName, mConfig, source, std::move(processedItem));
+}
+
+bool ResourceParser::parseColor(XmlPullParser* parser, const ResourceNameRef& resourceName) {
+    const SourceLine source = mSource.line(parser->getLineNumber());
+
+    std::unique_ptr<Item> item = parseXml(parser, android::ResTable_map::TYPE_COLOR, kNoRawString);
+    if (!item) {
+        mLogger.error(source.line) << "invalid color." << std::endl;
+        return false;
+    }
+    return mTable->addResource(resourceName, mConfig, source, std::move(item));
+}
+
+bool ResourceParser::parsePrimitive(XmlPullParser* parser, const ResourceNameRef& resourceName) {
+    const SourceLine source = mSource.line(parser->getLineNumber());
+
+    uint32_t typeMask = 0;
+    switch (resourceName.type) {
+        case ResourceType::kInteger:
+            typeMask |= android::ResTable_map::TYPE_INTEGER;
+            break;
+
+        case ResourceType::kDimen:
+            typeMask |= android::ResTable_map::TYPE_DIMENSION
+                     | android::ResTable_map::TYPE_FLOAT
+                     | android::ResTable_map::TYPE_FRACTION;
+            break;
+
+        case ResourceType::kBool:
+            typeMask |= android::ResTable_map::TYPE_BOOLEAN;
+            break;
+
+        default:
+            assert(false);
+            break;
+    }
+
+    std::unique_ptr<Item> item = parseXml(parser, typeMask, kNoRawString);
+    if (!item) {
+        mLogger.error(source.line)
+                << "invalid "
+                << resourceName.type
+                << "."
+                << std::endl;
+        return false;
+    }
+
+    return mTable->addResource(resourceName, mConfig, source, std::move(item));
+}
+
+bool ResourceParser::parsePublic(XmlPullParser* parser, const StringPiece16& name) {
+    const SourceLine source = mSource.line(parser->getLineNumber());
+
+    const auto endAttrIter = parser->endAttributes();
+    const auto typeAttrIter = parser->findAttribute(u"", u"type");
+    if (typeAttrIter == endAttrIter || typeAttrIter->value.empty()) {
+        mLogger.error(source.line)
+                << "<public> must have a 'type' attribute."
+                << std::endl;
+        return false;
+    }
+
+    const ResourceType* parsedType = parseResourceType(typeAttrIter->value);
+    if (!parsedType) {
+        mLogger.error(source.line)
+                << "invalid resource type '"
+                << typeAttrIter->value
+                << "' in <public>."
+                << std::endl;
+        return false;
+    }
+
+    ResourceNameRef resourceName { {}, *parsedType, name };
+    ResourceId resourceId;
+
+    const auto idAttrIter = parser->findAttribute(u"", u"id");
+    if (idAttrIter != endAttrIter && !idAttrIter->value.empty()) {
+        android::Res_value val;
+        bool result = android::ResTable::stringToInt(idAttrIter->value.data(),
+                                                     idAttrIter->value.size(), &val);
+        resourceId.id = val.data;
+        if (!result || !resourceId.isValid()) {
+            mLogger.error(source.line)
+                    << "invalid resource ID '"
+                    << idAttrIter->value
+                    << "' in <public>."
+                    << std::endl;
+            return false;
+        }
+    }
+
+    if (*parsedType == ResourceType::kId) {
+        // An ID marked as public is also the definition of an ID.
+        mTable->addResource(resourceName, {}, source, util::make_unique<Id>());
+    }
+
+    return mTable->markPublic(resourceName, resourceId, source);
+}
+
+static uint32_t parseFormatType(const StringPiece16& piece) {
+    if (piece == u"reference")      return android::ResTable_map::TYPE_REFERENCE;
+    else if (piece == u"string")    return android::ResTable_map::TYPE_STRING;
+    else if (piece == u"integer")   return android::ResTable_map::TYPE_INTEGER;
+    else if (piece == u"boolean")   return android::ResTable_map::TYPE_BOOLEAN;
+    else if (piece == u"color")     return android::ResTable_map::TYPE_COLOR;
+    else if (piece == u"float")     return android::ResTable_map::TYPE_FLOAT;
+    else if (piece == u"dimension") return android::ResTable_map::TYPE_DIMENSION;
+    else if (piece == u"fraction")  return android::ResTable_map::TYPE_FRACTION;
+    else if (piece == u"enum")      return android::ResTable_map::TYPE_ENUM;
+    else if (piece == u"flags")     return android::ResTable_map::TYPE_FLAGS;
+    return 0;
+}
+
+static uint32_t parseFormatAttribute(const StringPiece16& str) {
+    uint32_t mask = 0;
+    for (StringPiece16 part : util::tokenize(str, u'|')) {
+        StringPiece16 trimmedPart = util::trimWhitespace(part);
+        uint32_t type = parseFormatType(trimmedPart);
+        if (type == 0) {
+            return 0;
+        }
+        mask |= type;
+    }
+    return mask;
+}
+
+bool ResourceParser::parseAttr(XmlPullParser* parser, const ResourceNameRef& resourceName) {
+    const SourceLine source = mSource.line(parser->getLineNumber());
+    std::unique_ptr<Attribute> attr = parseAttrImpl(parser, resourceName, false);
+    if (!attr) {
+        return false;
+    }
+    return mTable->addResource(resourceName, mConfig, source, std::move(attr));
+}
+
+std::unique_ptr<Attribute> ResourceParser::parseAttrImpl(XmlPullParser* parser,
+                                                         const ResourceNameRef& resourceName,
+                                                         bool weak) {
+    uint32_t typeMask = 0;
+
+    const auto endAttrIter = parser->endAttributes();
+    const auto formatAttrIter = parser->findAttribute(u"", u"format");
+    if (formatAttrIter != endAttrIter) {
+        typeMask = parseFormatAttribute(formatAttrIter->value);
+        if (typeMask == 0) {
+            mLogger.error(parser->getLineNumber())
+                    << "invalid attribute format '"
+                    << formatAttrIter->value
+                    << "'."
+                    << std::endl;
+            return {};
+        }
+    }
+
+    std::vector<Attribute::Symbol> items;
+
+    bool error = false;
+    while (XmlPullParser::isGoodEvent(parser->next())) {
+        if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+            continue;
+        }
+
+        ScopedXmlPullParser childParser(parser);
+
+        const std::u16string& name = childParser.getElementName();
+        if (!childParser.getElementNamespace().empty()
+                || (name != u"flag" && name != u"enum")) {
+            mLogger.error(childParser.getLineNumber())
+                    << "unexpected tag <"
+                    << name
+                    << "> in <attr>."
+                    << std::endl;
+            error = true;
+            continue;
+        }
+
+        if (name == u"enum") {
+            if (typeMask & android::ResTable_map::TYPE_FLAGS) {
+                mLogger.error(childParser.getLineNumber())
+                        << "can not define an <enum>; already defined a <flag>."
+                        << std::endl;
+                error = true;
+                continue;
+            }
+            typeMask |= android::ResTable_map::TYPE_ENUM;
+        } else if (name == u"flag") {
+            if (typeMask & android::ResTable_map::TYPE_ENUM) {
+                mLogger.error(childParser.getLineNumber())
+                        << "can not define a <flag>; already defined an <enum>."
+                        << std::endl;
+                error = true;
+                continue;
+            }
+            typeMask |= android::ResTable_map::TYPE_FLAGS;
+        }
+
+        Attribute::Symbol item;
+        if (parseEnumOrFlagItem(&childParser, name, &item)) {
+            if (!mTable->addResource(item.symbol.name, mConfig,
+                                     mSource.line(childParser.getLineNumber()),
+                                     util::make_unique<Id>())) {
+                error = true;
+            } else {
+                items.push_back(std::move(item));
+            }
+        } else {
+            error = true;
+        }
+    }
+
+    if (error) {
+        return {};
+    }
+
+    std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(weak);
+    attr->symbols.swap(items);
+    attr->typeMask = typeMask ? typeMask : android::ResTable_map::TYPE_ANY;
+    return attr;
+}
+
+bool ResourceParser::parseEnumOrFlagItem(XmlPullParser* parser, const StringPiece16& tag,
+                                         Attribute::Symbol* outSymbol) {
+    const auto attrIterEnd = parser->endAttributes();
+    const auto nameAttrIter = parser->findAttribute(u"", u"name");
+    if (nameAttrIter == attrIterEnd || nameAttrIter->value.empty()) {
+        mLogger.error(parser->getLineNumber())
+                << "no attribute 'name' found for tag <" << tag << ">."
+                << std::endl;
+        return false;
+    }
+
+    const auto valueAttrIter = parser->findAttribute(u"", u"value");
+    if (valueAttrIter == attrIterEnd || valueAttrIter->value.empty()) {
+        mLogger.error(parser->getLineNumber())
+                << "no attribute 'value' found for tag <" << tag << ">."
+                << std::endl;
+        return false;
+    }
+
+    android::Res_value val;
+    if (!android::ResTable::stringToInt(valueAttrIter->value.data(),
+                                        valueAttrIter->value.size(), &val)) {
+        mLogger.error(parser->getLineNumber())
+                << "invalid value '"
+                << valueAttrIter->value
+                << "' for <" << tag << ">; must be an integer."
+                << std::endl;
+        return false;
+    }
+
+    outSymbol->symbol.name = ResourceName {
+            mTable->getPackage(), ResourceType::kId, nameAttrIter->value };
+    outSymbol->value = val.data;
+    return true;
+}
+
+static bool parseXmlAttributeName(StringPiece16 str, ResourceNameRef* outRef) {
+    str = util::trimWhitespace(str);
+    const char16_t* const start = str.data();
+    const char16_t* const end = start + str.size();
+    const char16_t* p = start;
+
+    StringPiece16 package;
+    StringPiece16 name;
+    while (p != end) {
+        if (*p == u':') {
+            package = StringPiece16(start, p - start);
+            name = StringPiece16(p + 1, end - (p + 1));
+            break;
+        }
+        p++;
+    }
+
+    outRef->package = package;
+    outRef->type = ResourceType::kAttr;
+    if (name.size() == 0) {
+        outRef->entry = str;
+    } else {
+        outRef->entry = name;
+    }
+    return true;
+}
+
+bool ResourceParser::parseUntypedItem(XmlPullParser* parser, Style& style) {
+    const auto endAttrIter = parser->endAttributes();
+    const auto nameAttrIter = parser->findAttribute(u"", u"name");
+    if (nameAttrIter == endAttrIter || nameAttrIter->value.empty()) {
+        mLogger.error(parser->getLineNumber())
+                << "<item> must have a 'name' attribute."
+                << std::endl;
+        return false;
+    }
+
+    ResourceNameRef keyRef;
+    if (!parseXmlAttributeName(nameAttrIter->value, &keyRef)) {
+        mLogger.error(parser->getLineNumber())
+                << "invalid attribute name '"
+                << nameAttrIter->value
+                << "'."
+                << std::endl;
+        return false;
+    }
+
+    if (keyRef.package.empty()) {
+        keyRef.package = mTable->getPackage();
+    }
+
+    // Create a copy instead of a reference because we
+    // are about to invalidate keyRef when advancing the parser.
+    ResourceName key = keyRef.toResourceName();
+
+    std::unique_ptr<Item> value = parseXml(parser, 0, kAllowRawString);
+    if (!value) {
+        return false;
+    }
+
+    style.entries.push_back(Style::Entry{ Reference(key), std::move(value) });
+    return true;
+}
+
+bool ResourceParser::parseStyle(XmlPullParser* parser, const ResourceNameRef& resourceName) {
+    const SourceLine source = mSource.line(parser->getLineNumber());
+    std::unique_ptr<Style> style = util::make_unique<Style>();
+
+    const auto endAttrIter = parser->endAttributes();
+    const auto parentAttrIter = parser->findAttribute(u"", u"parent");
+    if (parentAttrIter != endAttrIter) {
+        ResourceNameRef ref;
+        bool create = false;
+        bool privateRef = false;
+        if (tryParseReference(parentAttrIter->value, &ref, &create, &privateRef)) {
+            if (create) {
+                mLogger.error(source.line)
+                        << "parent of style can not be an ID."
+                        << std::endl;
+                return false;
+            }
+            style->parent.name = ref.toResourceName();
+            style->parent.privateReference = privateRef;
+        } else if (tryParseAttributeReference(parentAttrIter->value, &ref)) {
+            style->parent.name = ref.toResourceName();
+        } else {
+            // TODO(adamlesinski): Try parsing without the '@' or '?'.
+            // Also, make sure to check the entry name for weird symbols.
+            style->parent.name = ResourceName {
+                {}, ResourceType::kStyle, parentAttrIter->value
+            };
+        }
+
+        if (style->parent.name.package.empty()) {
+            style->parent.name.package = mTable->getPackage();
+        }
+    }
+
+    bool success = true;
+    while (XmlPullParser::isGoodEvent(parser->next())) {
+        if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+            continue;
+        }
+
+        ScopedXmlPullParser childParser(parser);
+        const std::u16string& name = childParser.getElementName();
+        if (name == u"item") {
+            success &= parseUntypedItem(&childParser, *style);
+        } else {
+            mLogger.error(childParser.getLineNumber())
+                    << "unexpected tag <"
+                    << name
+                    << "> in <style> resource."
+                    << std::endl;
+            success = false;
+        }
+    }
+
+    if (!success) {
+        return false;
+    }
+
+    return mTable->addResource(resourceName, mConfig, source, std::move(style));
+}
+
+bool ResourceParser::parseArray(XmlPullParser* parser, const ResourceNameRef& resourceName,
+                                uint32_t typeMask) {
+    const SourceLine source = mSource.line(parser->getLineNumber());
+    std::unique_ptr<Array> array = util::make_unique<Array>();
+
+    bool error = false;
+    while (XmlPullParser::isGoodEvent(parser->next())) {
+        if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+            continue;
+        }
+
+        ScopedXmlPullParser childParser(parser);
+
+        if (childParser.getElementName() != u"item") {
+            mLogger.error(childParser.getLineNumber())
+                    << "unexpected tag <"
+                    << childParser.getElementName()
+                    << "> in <array> resource."
+                    << std::endl;
+            error = true;
+            continue;
+        }
+
+        std::unique_ptr<Item> item = parseXml(&childParser, typeMask, kNoRawString);
+        if (!item) {
+            error = true;
+            continue;
+        }
+        array->items.emplace_back(std::move(item));
+    }
+
+    if (error) {
+        return false;
+    }
+
+    return mTable->addResource(resourceName, mConfig, source, std::move(array));
+}
+
+bool ResourceParser::parsePlural(XmlPullParser* parser, const ResourceNameRef& resourceName) {
+    const SourceLine source = mSource.line(parser->getLineNumber());
+    std::unique_ptr<Plural> plural = util::make_unique<Plural>();
+
+    bool success = true;
+    while (XmlPullParser::isGoodEvent(parser->next())) {
+        if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+            continue;
+        }
+
+        ScopedXmlPullParser childParser(parser);
+
+        if (!childParser.getElementNamespace().empty() ||
+                childParser.getElementName() != u"item") {
+            success = false;
+            continue;
+        }
+
+        const auto endAttrIter = childParser.endAttributes();
+        auto attrIter = childParser.findAttribute(u"", u"quantity");
+        if (attrIter == endAttrIter || attrIter->value.empty()) {
+            mLogger.error(childParser.getLineNumber())
+                    << "<item> in <plurals> requires attribute 'quantity'."
+                    << std::endl;
+            success = false;
+            continue;
+        }
+
+        StringPiece16 trimmedQuantity = util::trimWhitespace(attrIter->value);
+        size_t index = 0;
+        if (trimmedQuantity == u"zero") {
+            index = Plural::Zero;
+        } else if (trimmedQuantity == u"one") {
+            index = Plural::One;
+        } else if (trimmedQuantity == u"two") {
+            index = Plural::Two;
+        } else if (trimmedQuantity == u"few") {
+            index = Plural::Few;
+        } else if (trimmedQuantity == u"many") {
+            index = Plural::Many;
+        } else if (trimmedQuantity == u"other") {
+            index = Plural::Other;
+        } else {
+            mLogger.error(childParser.getLineNumber())
+                    << "<item> in <plural> has invalid value '"
+                    << trimmedQuantity
+                    << "' for attribute 'quantity'."
+                    << std::endl;
+            success = false;
+            continue;
+        }
+
+        if (plural->values[index]) {
+            mLogger.error(childParser.getLineNumber())
+                    << "duplicate quantity '"
+                    << trimmedQuantity
+                    << "'."
+                    << std::endl;
+            success = false;
+            continue;
+        }
+
+        if (!(plural->values[index] = parseXml(&childParser, android::ResTable_map::TYPE_STRING,
+                                               kNoRawString))) {
+            success = false;
+        }
+    }
+
+    if (!success) {
+        return false;
+    }
+
+    return mTable->addResource(resourceName, mConfig, source, std::move(plural));
+}
+
+bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser,
+                                           const ResourceNameRef& resourceName) {
+    const SourceLine source = mSource.line(parser->getLineNumber());
+    std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
+
+    bool success = true;
+    while (XmlPullParser::isGoodEvent(parser->next())) {
+        if (parser->getEvent() != XmlPullParser::Event::kStartElement) {
+            continue;
+        }
+
+        ScopedXmlPullParser childParser(parser);
+
+        const std::u16string& elementName = childParser.getElementName();
+        if (elementName == u"attr") {
+            const auto endAttrIter = childParser.endAttributes();
+            auto attrIter = childParser.findAttribute(u"", u"name");
+            if (attrIter == endAttrIter || attrIter->value.empty()) {
+                mLogger.error(childParser.getLineNumber())
+                        << "<attr> tag must have a 'name' attribute."
+                        << std::endl;
+                success = false;
+                continue;
+            }
+
+            // Copy because our iterator will be invalidated.
+            std::u16string attrName = attrIter->value;
+
+            ResourceNameRef attrResourceName = {
+                    mTable->getPackage(),
+                    ResourceType::kAttr,
+                    attrName
+            };
+
+            std::unique_ptr<Attribute> attr = parseAttrImpl(&childParser, attrResourceName, true);
+            if (!attr) {
+                success = false;
+                continue;
+            }
+
+            styleable->entries.emplace_back(attrResourceName);
+
+            success &= mTable->addResource(attrResourceName, mConfig,
+                                           mSource.line(childParser.getLineNumber()),
+                                           std::move(attr));
+
+        } else if (elementName != u"eat-comment" && elementName != u"skip") {
+            mLogger.error(childParser.getLineNumber())
+                    << "<"
+                    << elementName
+                    << "> is not allowed inside <declare-styleable>."
+                    << std::endl;
+            success = false;
+        }
+    }
+
+    if (!success) {
+        return false;
+    }
+
+    return mTable->addResource(resourceName, mConfig, source, std::move(styleable));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
new file mode 100644
index 0000000..96bba4f
--- /dev/null
+++ b/tools/aapt2/ResourceParser.h
@@ -0,0 +1,188 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_RESOURCE_PARSER_H
+#define AAPT_RESOURCE_PARSER_H
+
+#include "ConfigDescription.h"
+#include "Logger.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "StringPiece.h"
+#include "StringPool.h"
+#include "XmlPullParser.h"
+
+#include <istream>
+#include <memory>
+
+namespace aapt {
+
+/*
+ * Parses an XML file for resources and adds them to a ResourceTable.
+ */
+class ResourceParser {
+public:
+    /*
+     * Extracts the package, type, and name from a string of the format:
+     *
+     *      [package:]type/name
+     *
+     * where the package can be empty. Validation must be performed on each
+     * individual extracted piece to verify that the pieces are valid.
+     */
+    static void extractResourceName(const StringPiece16& str, StringPiece16* outPackage,
+                                    StringPiece16* outType, StringPiece16* outEntry);
+
+    /*
+     * Returns true if the string was parsed as a reference (@[+][package:]type/name), with
+     * `outReference` set to the parsed reference.
+     *
+     * If '+' was present in the reference, `outCreate` is set to true.
+     * If '*' was present in the reference, `outPrivate` is set to true.
+     */
+    static bool tryParseReference(const StringPiece16& str, ResourceNameRef* outReference,
+                                  bool* outCreate, bool* outPrivate);
+
+    /*
+     * Returns true if the string was parsed as an attribute reference (?[package:]type/name),
+     * with `outReference` set to the parsed reference.
+     */
+    static bool tryParseAttributeReference(const StringPiece16& str,
+                                           ResourceNameRef* outReference);
+
+    /*
+     * Returns a Reference object if the string was parsed as a resource or attribute reference,
+     * ( @[+][package:]type/name | ?[package:]type/name )
+     * assigning defaultPackage if the package was not present in the string, and setting
+     * outCreate to true if the '+' was present in the string.
+     */
+    static std::unique_ptr<Reference> tryParseReference(const StringPiece16& str,
+                                                        const StringPiece16& defaultPackage,
+                                                        bool* outCreate);
+
+    /*
+     * Returns a BinaryPrimitve object representing @null or @empty if the string was parsed
+     * as one.
+     */
+    static std::unique_ptr<BinaryPrimitive> tryParseNullOrEmpty(const StringPiece16& str);
+
+    /*
+     * Returns a BinaryPrimitve object representing a color if the string was parsed
+     * as one.
+     */
+    static std::unique_ptr<BinaryPrimitive> tryParseColor(const StringPiece16& str);
+
+    /*
+     * Returns a BinaryPrimitve object representing a boolean if the string was parsed
+     * as one.
+     */
+    static std::unique_ptr<BinaryPrimitive> tryParseBool(const StringPiece16& str);
+
+    /*
+     * Returns a BinaryPrimitve object representing an integer if the string was parsed
+     * as one.
+     */
+    static std::unique_ptr<BinaryPrimitive> tryParseInt(const StringPiece16& str);
+
+    /*
+     * Returns a BinaryPrimitve object representing a floating point number
+     * (float, dimension, etc) if the string was parsed as one.
+     */
+    static std::unique_ptr<BinaryPrimitive> tryParseFloat(const StringPiece16& str);
+
+    /*
+     * Returns a BinaryPrimitve object representing an enum symbol if the string was parsed
+     * as one.
+     */
+    static std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute& enumAttr,
+                                                               const StringPiece16& str);
+
+    /*
+     * Returns a BinaryPrimitve object representing a flag symbol if the string was parsed
+     * as one.
+     */
+    static std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute& enumAttr,
+                                                               const StringPiece16& str);
+
+    /*
+     * Try to convert a string to an Item for the given attribute. The attribute will
+     * restrict what values the string can be converted to.
+     * The defaultPackage is used when the string is a reference with no defined package.
+     * The callback function onCreateReference is called when the parsed item is a
+     * reference to an ID that must be created (@+id/foo).
+     */
+    static std::unique_ptr<Item> parseItemForAttribute(
+            const StringPiece16& value, const Attribute& attr, const StringPiece16& defaultPackage,
+            std::function<void(const ResourceName&)> onCreateReference = {});
+
+    static std::unique_ptr<Item> parseItemForAttribute(
+            const StringPiece16& value, uint32_t typeMask, const StringPiece16& defaultPackage,
+            std::function<void(const ResourceName&)> onCreateReference = {});
+
+    static uint32_t androidTypeToAttributeTypeMask(uint16_t type);
+
+    ResourceParser(const std::shared_ptr<ResourceTable>& table, const Source& source,
+                   const ConfigDescription& config, const std::shared_ptr<XmlPullParser>& parser);
+
+    ResourceParser(const ResourceParser&) = delete; // No copy.
+
+    bool parse();
+
+private:
+    /*
+     * Parses the XML subtree as a StyleString (flattened XML representation for strings
+     * with formatting). If successful, `outStyleString`
+     * contains the escaped and whitespace trimmed text, while `outRawString`
+     * contains the unescaped text. Returns true on success.
+     */
+    bool flattenXmlSubtree(XmlPullParser* parser, std::u16string* outRawString,\
+                           StyleString* outStyleString);
+
+    /*
+     * Parses the XML subtree and converts it to an Item. The type of Item that can be
+     * parsed is denoted by the `typeMask`. If `allowRawValue` is true and the subtree
+     * can not be parsed as a regular Item, then a RawString is returned. Otherwise
+     * this returns nullptr.
+     */
+    std::unique_ptr<Item> parseXml(XmlPullParser* parser, uint32_t typeMask, bool allowRawValue);
+
+    bool parseResources(XmlPullParser* parser);
+    bool parseString(XmlPullParser* parser, const ResourceNameRef& resourceName);
+    bool parseColor(XmlPullParser* parser, const ResourceNameRef& resourceName);
+    bool parsePrimitive(XmlPullParser* parser, const ResourceNameRef& resourceName);
+    bool parsePublic(XmlPullParser* parser, const StringPiece16& name);
+    bool parseAttr(XmlPullParser* parser, const ResourceNameRef& resourceName);
+    std::unique_ptr<Attribute> parseAttrImpl(XmlPullParser* parser,
+                                             const ResourceNameRef& resourceName,
+                                             bool weak);
+    bool parseEnumOrFlagItem(XmlPullParser* parser, const StringPiece16& tag,
+                             Attribute::Symbol* outSymbol);
+    bool parseStyle(XmlPullParser* parser, const ResourceNameRef& resourceName);
+    bool parseUntypedItem(XmlPullParser* parser, Style& style);
+    bool parseDeclareStyleable(XmlPullParser* parser, const ResourceNameRef& resourceName);
+    bool parseArray(XmlPullParser* parser, const ResourceNameRef& resourceName, uint32_t typeMask);
+    bool parsePlural(XmlPullParser* parser, const ResourceNameRef& resourceName);
+
+    std::shared_ptr<ResourceTable> mTable;
+    Source mSource;
+    ConfigDescription mConfig;
+    SourceLogger mLogger;
+    std::shared_ptr<XmlPullParser> mParser;
+};
+
+} // namespace aapt
+
+#endif // AAPT_RESOURCE_PARSER_H
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
new file mode 100644
index 0000000..5afbaf4
--- /dev/null
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -0,0 +1,399 @@
+/*
+ * 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.
+ */
+
+#include "ResourceParser.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "SourceXmlPullParser.h"
+
+#include <gtest/gtest.h>
+#include <sstream>
+#include <string>
+
+namespace aapt {
+
+constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+
+TEST(ResourceParserReferenceTest, ParseReferenceWithNoPackage) {
+    ResourceNameRef expected = { {}, ResourceType::kColor, u"foo" };
+    ResourceNameRef actual;
+    bool create = false;
+    bool privateRef = false;
+    EXPECT_TRUE(ResourceParser::tryParseReference(u"@color/foo", &actual, &create, &privateRef));
+    EXPECT_EQ(expected, actual);
+    EXPECT_FALSE(create);
+    EXPECT_FALSE(privateRef);
+}
+
+TEST(ResourceParserReferenceTest, ParseReferenceWithPackage) {
+    ResourceNameRef expected = { u"android", ResourceType::kColor, u"foo" };
+    ResourceNameRef actual;
+    bool create = false;
+    bool privateRef = false;
+    EXPECT_TRUE(ResourceParser::tryParseReference(u"@android:color/foo", &actual, &create,
+                                                  &privateRef));
+    EXPECT_EQ(expected, actual);
+    EXPECT_FALSE(create);
+    EXPECT_FALSE(privateRef);
+}
+
+TEST(ResourceParserReferenceTest, ParseReferenceWithSurroundingWhitespace) {
+    ResourceNameRef expected = { u"android", ResourceType::kColor, u"foo" };
+    ResourceNameRef actual;
+    bool create = false;
+    bool privateRef = false;
+    EXPECT_TRUE(ResourceParser::tryParseReference(u"\t @android:color/foo\n \n\t", &actual,
+                                                  &create, &privateRef));
+    EXPECT_EQ(expected, actual);
+    EXPECT_FALSE(create);
+    EXPECT_FALSE(privateRef);
+}
+
+TEST(ResourceParserReferenceTest, ParseAutoCreateIdReference) {
+    ResourceNameRef expected = { u"android", ResourceType::kId, u"foo" };
+    ResourceNameRef actual;
+    bool create = false;
+    bool privateRef = false;
+    EXPECT_TRUE(ResourceParser::tryParseReference(u"@+android:id/foo", &actual, &create,
+                                                  &privateRef));
+    EXPECT_EQ(expected, actual);
+    EXPECT_TRUE(create);
+    EXPECT_FALSE(privateRef);
+}
+
+TEST(ResourceParserReferenceTest, ParsePrivateReference) {
+    ResourceNameRef expected = { u"android", ResourceType::kId, u"foo" };
+    ResourceNameRef actual;
+    bool create = false;
+    bool privateRef = false;
+    EXPECT_TRUE(ResourceParser::tryParseReference(u"@*android:id/foo", &actual, &create,
+                                                  &privateRef));
+    EXPECT_EQ(expected, actual);
+    EXPECT_FALSE(create);
+    EXPECT_TRUE(privateRef);
+}
+
+TEST(ResourceParserReferenceTest, FailToParseAutoCreateNonIdReference) {
+    bool create = false;
+    bool privateRef = false;
+    ResourceNameRef actual;
+    EXPECT_FALSE(ResourceParser::tryParseReference(u"@+android:color/foo", &actual, &create,
+                                                   &privateRef));
+}
+
+struct ResourceParserTest : public ::testing::Test {
+    virtual void SetUp() override {
+        mTable = std::make_shared<ResourceTable>();
+        mTable->setPackage(u"android");
+    }
+
+    ::testing::AssertionResult testParse(std::istream& in) {
+        std::stringstream input(kXmlPreamble);
+        input << "<resources>" << std::endl
+              << in.rdbuf() << std::endl
+              << "</resources>" << std::endl;
+        ResourceParser parser(mTable, Source{ "test" }, {},
+                              std::make_shared<SourceXmlPullParser>(input));
+        if (parser.parse()) {
+            return ::testing::AssertionSuccess();
+        }
+        return ::testing::AssertionFailure();
+    }
+
+    template <typename T>
+    const T* findResource(const ResourceNameRef& name, const ConfigDescription& config) {
+        using std::begin;
+        using std::end;
+
+        const ResourceTableType* type;
+        const ResourceEntry* entry;
+        std::tie(type, entry) = mTable->findResource(name);
+        if (!type || !entry) {
+            return nullptr;
+        }
+
+        for (const auto& configValue : entry->values) {
+            if (configValue.config == config) {
+                return dynamic_cast<const T*>(configValue.value.get());
+            }
+        }
+        return nullptr;
+    }
+
+    template <typename T>
+    const T* findResource(const ResourceNameRef& name) {
+        return findResource<T>(name, {});
+    }
+
+    std::shared_ptr<ResourceTable> mTable;
+};
+
+TEST_F(ResourceParserTest, FailToParseWithNoRootResourcesElement) {
+    std::stringstream input(kXmlPreamble);
+    input << "<attr name=\"foo\"/>" << std::endl;
+    ResourceParser parser(mTable, {}, {}, std::make_shared<SourceXmlPullParser>(input));
+    ASSERT_FALSE(parser.parse());
+}
+
+TEST_F(ResourceParserTest, ParseQuotedString) {
+    std::stringstream input("<string name=\"foo\">   \"  hey there \" </string>");
+    ASSERT_TRUE(testParse(input));
+
+    const String* str = findResource<String>(ResourceName{
+            u"android", ResourceType::kString, u"foo"});
+    ASSERT_NE(nullptr, str);
+    EXPECT_EQ(std::u16string(u"  hey there "), *str->value);
+}
+
+TEST_F(ResourceParserTest, ParseEscapedString) {
+    std::stringstream input("<string name=\"foo\">\\?123</string>");
+    ASSERT_TRUE(testParse(input));
+
+    const String* str = findResource<String>(ResourceName{
+            u"android", ResourceType::kString, u"foo" });
+    ASSERT_NE(nullptr, str);
+    EXPECT_EQ(std::u16string(u"?123"), *str->value);
+}
+
+TEST_F(ResourceParserTest, ParseAttr) {
+    std::stringstream input;
+    input << "<attr name=\"foo\" format=\"string\"/>" << std::endl
+          << "<attr name=\"bar\"/>" << std::endl;
+    ASSERT_TRUE(testParse(input));
+
+    const Attribute* attr = findResource<Attribute>(ResourceName{
+            u"android", ResourceType::kAttr, u"foo"});
+    EXPECT_NE(nullptr, attr);
+    EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
+
+    attr = findResource<Attribute>(ResourceName{
+            u"android", ResourceType::kAttr, u"bar"});
+    EXPECT_NE(nullptr, attr);
+    EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_ANY), attr->typeMask);
+}
+
+TEST_F(ResourceParserTest, ParseUseAndDeclOfAttr) {
+    std::stringstream input;
+    input << "<declare-styleable name=\"Styleable\">" << std::endl
+          << "  <attr name=\"foo\" />" << std::endl
+          << "</declare-styleable>" << std::endl
+          << "<attr name=\"foo\" format=\"string\"/>" << std::endl;
+    ASSERT_TRUE(testParse(input));
+
+    const Attribute* attr = findResource<Attribute>(ResourceName{
+            u"android", ResourceType::kAttr, u"foo"});
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_STRING), attr->typeMask);
+}
+
+TEST_F(ResourceParserTest, ParseDoubleUseOfAttr) {
+    std::stringstream input;
+    input << "<declare-styleable name=\"Theme\">" << std::endl
+          << "  <attr name=\"foo\" />" << std::endl
+          << "</declare-styleable>" << std::endl
+          << "<declare-styleable name=\"Window\">" << std::endl
+          << "  <attr name=\"foo\" format=\"boolean\"/>" << std::endl
+          << "</declare-styleable>" << std::endl;
+
+    ASSERT_TRUE(testParse(input));
+
+    const Attribute* attr = findResource<Attribute>(ResourceName{
+            u"android", ResourceType::kAttr, u"foo"});
+    ASSERT_NE(nullptr, attr);
+    EXPECT_EQ(uint32_t(android::ResTable_map::TYPE_BOOLEAN), attr->typeMask);
+}
+
+TEST_F(ResourceParserTest, ParseEnumAttr) {
+    std::stringstream input;
+    input << "<attr name=\"foo\">" << std::endl
+          << "  <enum name=\"bar\" value=\"0\"/>" << std::endl
+          << "  <enum name=\"bat\" value=\"1\"/>" << std::endl
+          << "  <enum name=\"baz\" value=\"2\"/>" << std::endl
+          << "</attr>" << std::endl;
+    ASSERT_TRUE(testParse(input));
+
+    const Attribute* enumAttr = findResource<Attribute>(ResourceName{
+            u"android", ResourceType::kAttr, u"foo"});
+    ASSERT_NE(enumAttr, nullptr);
+    EXPECT_EQ(enumAttr->typeMask, android::ResTable_map::TYPE_ENUM);
+    ASSERT_EQ(enumAttr->symbols.size(), 3u);
+
+    EXPECT_EQ(enumAttr->symbols[0].symbol.name.entry, u"bar");
+    EXPECT_EQ(enumAttr->symbols[0].value, 0u);
+
+    EXPECT_EQ(enumAttr->symbols[1].symbol.name.entry, u"bat");
+    EXPECT_EQ(enumAttr->symbols[1].value, 1u);
+
+    EXPECT_EQ(enumAttr->symbols[2].symbol.name.entry, u"baz");
+    EXPECT_EQ(enumAttr->symbols[2].value, 2u);
+}
+
+TEST_F(ResourceParserTest, ParseFlagAttr) {
+    std::stringstream input;
+    input << "<attr name=\"foo\">" << std::endl
+          << "  <flag name=\"bar\" value=\"0\"/>" << std::endl
+          << "  <flag name=\"bat\" value=\"1\"/>" << std::endl
+          << "  <flag name=\"baz\" value=\"2\"/>" << std::endl
+          << "</attr>" << std::endl;
+    ASSERT_TRUE(testParse(input));
+
+    const Attribute* flagAttr = findResource<Attribute>(ResourceName{
+            u"android", ResourceType::kAttr, u"foo"});
+    ASSERT_NE(flagAttr, nullptr);
+    EXPECT_EQ(flagAttr->typeMask, android::ResTable_map::TYPE_FLAGS);
+    ASSERT_EQ(flagAttr->symbols.size(), 3u);
+
+    EXPECT_EQ(flagAttr->symbols[0].symbol.name.entry, u"bar");
+    EXPECT_EQ(flagAttr->symbols[0].value, 0u);
+
+    EXPECT_EQ(flagAttr->symbols[1].symbol.name.entry, u"bat");
+    EXPECT_EQ(flagAttr->symbols[1].value, 1u);
+
+    EXPECT_EQ(flagAttr->symbols[2].symbol.name.entry, u"baz");
+    EXPECT_EQ(flagAttr->symbols[2].value, 2u);
+
+    std::unique_ptr<BinaryPrimitive> flagValue =
+            ResourceParser::tryParseFlagSymbol(*flagAttr, u"baz|bat");
+    ASSERT_NE(flagValue, nullptr);
+    EXPECT_EQ(flagValue->value.data, 1u | 2u);
+}
+
+TEST_F(ResourceParserTest, FailToParseEnumAttrWithNonUniqueKeys) {
+    std::stringstream input;
+    input << "<attr name=\"foo\">" << std::endl
+          << "  <enum name=\"bar\" value=\"0\"/>" << std::endl
+          << "  <enum name=\"bat\" value=\"1\"/>" << std::endl
+          << "  <enum name=\"bat\" value=\"2\"/>" << std::endl
+          << "</attr>" << std::endl;
+    ASSERT_FALSE(testParse(input));
+}
+
+TEST_F(ResourceParserTest, ParseStyle) {
+    std::stringstream input;
+    input << "<style name=\"foo\" parent=\"fu\">" << std::endl
+          << "  <item name=\"bar\">#ffffffff</item>" << std::endl
+          << "  <item name=\"bat\">@string/hey</item>" << std::endl
+          << "  <item name=\"baz\"><b>hey</b></item>" << std::endl
+          << "</style>" << std::endl;
+    ASSERT_TRUE(testParse(input));
+
+    const Style* style = findResource<Style>(ResourceName{
+            u"android", ResourceType::kStyle, u"foo"});
+    ASSERT_NE(style, nullptr);
+    EXPECT_EQ(ResourceNameRef(u"android", ResourceType::kStyle, u"fu"), style->parent.name);
+    ASSERT_EQ(style->entries.size(), 3u);
+
+    EXPECT_EQ(style->entries[0].key.name,
+              (ResourceName{ u"android", ResourceType::kAttr, u"bar" }));
+    EXPECT_EQ(style->entries[1].key.name,
+              (ResourceName{ u"android", ResourceType::kAttr, u"bat" }));
+    EXPECT_EQ(style->entries[2].key.name,
+              (ResourceName{ u"android", ResourceType::kAttr, u"baz" }));
+}
+
+TEST_F(ResourceParserTest, ParseAutoGeneratedIdReference) {
+    std::stringstream input;
+    input << "<string name=\"foo\">@+id/bar</string>" << std::endl;
+    ASSERT_TRUE(testParse(input));
+
+    const Id* id = findResource<Id>(ResourceName{ u"android", ResourceType::kId, u"bar"});
+    ASSERT_NE(id, nullptr);
+}
+
+TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) {
+    std::stringstream input;
+    input << "<declare-styleable name=\"foo\">" << std::endl
+          << "  <attr name=\"bar\" />" << std::endl
+          << "  <attr name=\"bat\" format=\"string|reference\"/>" << std::endl
+          << "</declare-styleable>" << std::endl;
+    ASSERT_TRUE(testParse(input));
+
+    const Attribute* attr = findResource<Attribute>(ResourceName{
+            u"android", ResourceType::kAttr, u"bar"});
+    ASSERT_NE(attr, nullptr);
+    EXPECT_TRUE(attr->isWeak());
+
+    attr = findResource<Attribute>(ResourceName{ u"android", ResourceType::kAttr, u"bat"});
+    ASSERT_NE(attr, nullptr);
+    EXPECT_TRUE(attr->isWeak());
+
+    const Styleable* styleable = findResource<Styleable>(ResourceName{
+            u"android", ResourceType::kStyleable, u"foo" });
+    ASSERT_NE(styleable, nullptr);
+    ASSERT_EQ(2u, styleable->entries.size());
+
+    EXPECT_EQ((ResourceName{u"android", ResourceType::kAttr, u"bar"}), styleable->entries[0].name);
+    EXPECT_EQ((ResourceName{u"android", ResourceType::kAttr, u"bat"}), styleable->entries[1].name);
+}
+
+TEST_F(ResourceParserTest, ParseArray) {
+    std::stringstream input;
+    input << "<array name=\"foo\">" << std::endl
+          << "  <item>@string/ref</item>" << std::endl
+          << "  <item>hey</item>" << std::endl
+          << "  <item>23</item>" << std::endl
+          << "</array>" << std::endl;
+    ASSERT_TRUE(testParse(input));
+
+    const Array* array = findResource<Array>(ResourceName{
+            u"android", ResourceType::kArray, u"foo" });
+    ASSERT_NE(array, nullptr);
+    ASSERT_EQ(3u, array->items.size());
+
+    EXPECT_NE(nullptr, dynamic_cast<const Reference*>(array->items[0].get()));
+    EXPECT_NE(nullptr, dynamic_cast<const String*>(array->items[1].get()));
+    EXPECT_NE(nullptr, dynamic_cast<const BinaryPrimitive*>(array->items[2].get()));
+}
+
+TEST_F(ResourceParserTest, ParsePlural) {
+    std::stringstream input;
+    input << "<plurals name=\"foo\">" << std::endl
+          << "  <item quantity=\"other\">apples</item>" << std::endl
+          << "  <item quantity=\"one\">apple</item>" << std::endl
+          << "</plurals>" << std::endl
+          << std::endl;
+    ASSERT_TRUE(testParse(input));
+}
+
+TEST_F(ResourceParserTest, ParseCommentsWithResource) {
+    std::stringstream input;
+    input << "<!-- This is a comment -->" << std::endl
+          << "<string name=\"foo\">Hi</string>" << std::endl;
+    ASSERT_TRUE(testParse(input));
+
+    const ResourceTableType* type;
+    const ResourceEntry* entry;
+    std::tie(type, entry) = mTable->findResource(ResourceName{
+            u"android", ResourceType::kString, u"foo"});
+    ASSERT_NE(type, nullptr);
+    ASSERT_NE(entry, nullptr);
+    ASSERT_FALSE(entry->values.empty());
+    EXPECT_EQ(entry->values.front().comment, u"This is a comment");
+}
+
+/*
+ * Declaring an ID as public should not require a separate definition
+ * (as an ID has no value).
+ */
+TEST_F(ResourceParserTest, ParsePublicIdAsDefinition) {
+    std::stringstream input("<public type=\"id\" name=\"foo\"/>");
+    ASSERT_TRUE(testParse(input));
+
+    const Id* id = findResource<Id>(ResourceName{ u"android", ResourceType::kId, u"foo" });
+    ASSERT_NE(nullptr, id);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
new file mode 100644
index 0000000..0b3dd78
--- /dev/null
+++ b/tools/aapt2/ResourceTable.cpp
@@ -0,0 +1,334 @@
+/*
+ * 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.
+ */
+
+#include "ConfigDescription.h"
+#include "Logger.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "Util.h"
+
+#include <algorithm>
+#include <androidfw/ResourceTypes.h>
+#include <memory>
+#include <string>
+#include <tuple>
+
+namespace aapt {
+
+static bool compareConfigs(const ResourceConfigValue& lhs, const ConfigDescription& rhs) {
+    return lhs.config < rhs;
+}
+
+static bool lessThanType(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) {
+    return lhs->type < rhs;
+}
+
+static bool lessThanEntry(const std::unique_ptr<ResourceEntry>& lhs, const StringPiece16& rhs) {
+    return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
+}
+
+ResourceTable::ResourceTable() : mPackageId(kUnsetPackageId) {
+}
+
+std::unique_ptr<ResourceTableType>& ResourceTable::findOrCreateType(ResourceType type) {
+    auto last = mTypes.end();
+    auto iter = std::lower_bound(mTypes.begin(), last, type, lessThanType);
+    if (iter != last) {
+        if ((*iter)->type == type) {
+            return *iter;
+        }
+    }
+    return *mTypes.emplace(iter, new ResourceTableType{ type });
+}
+
+std::unique_ptr<ResourceEntry>& ResourceTable::findOrCreateEntry(
+        std::unique_ptr<ResourceTableType>& type, const StringPiece16& name) {
+    auto last = type->entries.end();
+    auto iter = std::lower_bound(type->entries.begin(), last, name, lessThanEntry);
+    if (iter != last) {
+        if (name == (*iter)->name) {
+            return *iter;
+        }
+    }
+    return *type->entries.emplace(iter, new ResourceEntry{ name });
+}
+
+struct IsAttributeVisitor : ConstValueVisitor {
+    bool isAttribute = false;
+
+    void visit(const Attribute&, ValueVisitorArgs&) override {
+        isAttribute = true;
+    }
+
+    operator bool() {
+        return isAttribute;
+    }
+};
+
+/**
+ * The default handler for collisions. A return value of -1 means keep the
+ * existing value, 0 means fail, and +1 means take the incoming value.
+ */
+static int defaultCollisionHandler(const Value& existing, const Value& incoming) {
+    IsAttributeVisitor existingIsAttr, incomingIsAttr;
+    existing.accept(existingIsAttr, {});
+    incoming.accept(incomingIsAttr, {});
+
+    if (!incomingIsAttr) {
+        if (incoming.isWeak()) {
+            // We're trying to add a weak resource but a resource
+            // already exists. Keep the existing.
+            return -1;
+        } else if (existing.isWeak()) {
+            // Override the weak resource with the new strong resource.
+            return 1;
+        }
+        // The existing and incoming values are strong, this is an error
+        // if the values are not both attributes.
+        return 0;
+    }
+
+    if (!existingIsAttr) {
+        if (existing.isWeak()) {
+            // The existing value is not an attribute and it is weak,
+            // so take the incoming attribute value.
+            return 1;
+        }
+        // The existing value is not an attribute and it is strong,
+        // so the incoming attribute value is an error.
+        return 0;
+    }
+
+    //
+    // Attribute specific handling. At this point we know both
+    // values are attributes. Since we can declare and define
+    // attributes all-over, we do special handling to see
+    // which definition sticks.
+    //
+    const Attribute& existingAttr = static_cast<const Attribute&>(existing);
+    const Attribute& incomingAttr = static_cast<const Attribute&>(incoming);
+    if (existingAttr.typeMask == incomingAttr.typeMask) {
+        // The two attributes are both DECLs, but they are plain attributes
+        // with the same formats.
+        // Keep the strongest one.
+        return existingAttr.isWeak() ? 1 : -1;
+    }
+
+    if (existingAttr.isWeak() && existingAttr.typeMask == android::ResTable_map::TYPE_ANY) {
+        // Any incoming attribute is better than this.
+        return 1;
+    }
+
+    if (incomingAttr.isWeak() && incomingAttr.typeMask == android::ResTable_map::TYPE_ANY) {
+        // The incoming attribute may be a USE instead of a DECL.
+        // Keep the existing attribute.
+        return -1;
+    }
+    return 0;
+}
+
+static constexpr const char16_t* kValidNameChars = u"._-";
+
+bool ResourceTable::addResource(const ResourceNameRef& name, const ResourceId resId,
+        const ConfigDescription& config, const SourceLine& source,
+        std::unique_ptr<Value> value) {
+    if (!name.package.empty() && name.package != mPackage) {
+        Logger::error(source)
+                << "resource '"
+                << name
+                << "' has incompatible package. Must be '"
+                << mPackage
+                << "'."
+                << std::endl;
+        return false;
+    }
+
+    auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, kValidNameChars);
+    if (badCharIter != name.entry.end()) {
+        Logger::error(source)
+                << "resource '"
+                << name
+                << "' has invalid entry name '"
+                << name.entry
+                << "'. Invalid character '"
+                << *badCharIter
+                << "'."
+                << std::endl;
+        return false;
+    }
+
+    std::unique_ptr<ResourceTableType>& type = findOrCreateType(name.type);
+    if (resId.isValid() && type->typeId != ResourceTableType::kUnsetTypeId &&
+            type->typeId != resId.typeId()) {
+        Logger::error(source)
+                << "trying to add resource '"
+                << name
+                << "' with ID "
+                << resId
+                << " but type '"
+                << type->type
+                << "' already has ID "
+                << std::hex << type->typeId << std::dec
+                << "."
+                << std::endl;
+        return false;
+    }
+
+    std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, name.entry);
+    if (resId.isValid() && entry->entryId != ResourceEntry::kUnsetEntryId &&
+            entry->entryId != resId.entryId()) {
+        Logger::error(source)
+                << "trying to add resource '"
+                << name
+                << "' with ID "
+                << resId
+                << " but resource already has ID "
+                << ResourceId(mPackageId, type->typeId, entry->entryId)
+                << "."
+                << std::endl;
+        return false;
+    }
+
+    const auto endIter = std::end(entry->values);
+    auto iter = std::lower_bound(std::begin(entry->values), endIter, config, compareConfigs);
+    if (iter == endIter || iter->config != config) {
+        // This resource did not exist before, add it.
+        entry->values.insert(iter, ResourceConfigValue{ config, source, {}, std::move(value) });
+    } else {
+        int collisionResult = defaultCollisionHandler(*iter->value, *value);
+        if (collisionResult > 0) {
+            // Take the incoming value.
+            *iter = ResourceConfigValue{ config, source, {}, std::move(value) };
+        } else if (collisionResult == 0) {
+            Logger::error(source)
+                    << "duplicate value for resource '" << name << "' "
+                    << "with config '" << iter->config << "'."
+                    << std::endl;
+
+            Logger::error(iter->source)
+                    << "resource previously defined here."
+                    << std::endl;
+            return false;
+        }
+    }
+
+    if (resId.isValid()) {
+        type->typeId = resId.typeId();
+        entry->entryId = resId.entryId();
+    }
+    return true;
+}
+
+bool ResourceTable::addResource(const ResourceNameRef& name, const ConfigDescription& config,
+                                const SourceLine& source, std::unique_ptr<Value> value) {
+    return addResource(name, ResourceId{}, config, source, std::move(value));
+}
+
+bool ResourceTable::markPublic(const ResourceNameRef& name, const ResourceId resId,
+                               const SourceLine& source) {
+    if (!name.package.empty() && name.package != mPackage) {
+        Logger::error(source)
+                << "resource '"
+                << name
+                << "' has incompatible package. Must be '"
+                << mPackage
+                << "'."
+            << std::endl;
+        return false;
+    }
+
+    auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, kValidNameChars);
+    if (badCharIter != name.entry.end()) {
+        Logger::error(source)
+                << "resource '"
+                << name
+                << "' has invalid entry name '"
+                << name.entry
+                << "'. Invalid character '"
+                << *badCharIter
+                << "'."
+                << std::endl;
+        return false;
+    }
+
+    std::unique_ptr<ResourceTableType>& type = findOrCreateType(name.type);
+    if (resId.isValid() && type->typeId != ResourceTableType::kUnsetTypeId &&
+            type->typeId != resId.typeId()) {
+        Logger::error(source)
+                << "trying to make resource '"
+                << name
+                << "' public with ID "
+                << resId
+                << " but type '"
+                << type->type
+                << "' already has ID "
+                << std::hex << type->typeId << std::dec
+                << "."
+                << std::endl;
+        return false;
+    }
+
+    std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, name.entry);
+    if (resId.isValid() && entry->entryId != ResourceEntry::kUnsetEntryId &&
+            entry->entryId != resId.entryId()) {
+        Logger::error(source)
+                << "trying to make resource '"
+                << name
+                << "' public with ID "
+                << resId
+                << " but resource already has ID "
+                << ResourceId(mPackageId, type->typeId, entry->entryId)
+                << "."
+                << std::endl;
+        return false;
+    }
+
+    type->publicStatus.isPublic = true;
+    entry->publicStatus.isPublic = true;
+
+    if (resId.isValid()) {
+        type->typeId = resId.typeId();
+        entry->entryId = resId.entryId();
+    }
+
+    if (entry->values.empty()) {
+        entry->values.push_back(ResourceConfigValue{ {}, source, {},
+                                    util::make_unique<Sentinel>() });
+    }
+    return true;
+}
+
+std::tuple<const ResourceTableType*, const ResourceEntry*>
+ResourceTable::findResource(const ResourceNameRef& name) const {
+    if (name.package != mPackage) {
+        return {nullptr, nullptr};
+    }
+
+    auto iter = std::lower_bound(mTypes.begin(), mTypes.end(), name.type, lessThanType);
+    if (iter == mTypes.end() || (*iter)->type != name.type) {
+        return {nullptr, nullptr};
+    }
+
+    const std::unique_ptr<ResourceTableType>& type = *iter;
+    auto iter2 = std::lower_bound(type->entries.begin(), type->entries.end(), name.entry,
+                                  lessThanEntry);
+    if (iter2 == type->entries.end() || name.entry != (*iter2)->name) {
+        return {nullptr, nullptr};
+    }
+    return {iter->get(), iter2->get()};
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
new file mode 100644
index 0000000..57b5213
--- /dev/null
+++ b/tools/aapt2/ResourceTable.h
@@ -0,0 +1,254 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_RESOURCE_TABLE_H
+#define AAPT_RESOURCE_TABLE_H
+
+#include "ConfigDescription.h"
+#include "Resource.h"
+#include "ResourceValues.h"
+#include "Source.h"
+#include "StringPool.h"
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <vector>
+
+namespace aapt {
+
+/**
+ * The Public status of a resource.
+ */
+struct Public {
+    bool isPublic = false;
+    std::u16string comment;
+};
+
+/**
+ * The resource value for a specific configuration.
+ */
+struct ResourceConfigValue {
+    ConfigDescription config;
+    SourceLine source;
+    std::u16string comment;
+    std::unique_ptr<Value> value;
+};
+
+/**
+ * Represents a resource entry, which may have
+ * varying values for each defined configuration.
+ */
+struct ResourceEntry {
+    enum {
+        kUnsetEntryId = 0xffffffffu
+    };
+
+    /**
+     * The name of the resource. Immutable, as
+     * this determines the order of this resource
+     * when doing lookups.
+     */
+    const std::u16string name;
+
+    /**
+     * The entry ID for this resource.
+     */
+    size_t entryId;
+
+    /**
+     * Whether this resource is public (and must maintain the same
+     * entry ID across builds).
+     */
+    Public publicStatus;
+
+    /**
+     * The resource's values for each configuration.
+     */
+    std::vector<ResourceConfigValue> values;
+
+    inline ResourceEntry(const StringPiece16& _name);
+    inline ResourceEntry(const ResourceEntry* rhs);
+};
+
+/**
+ * Represents a resource type, which holds entries defined
+ * for this type.
+ */
+struct ResourceTableType {
+    enum {
+        kUnsetTypeId = 0xffffffffu
+    };
+
+    /**
+     * The logical type of resource (string, drawable, layout, etc.).
+     */
+    const ResourceType type;
+
+    /**
+     * The type ID for this resource.
+     */
+    size_t typeId;
+
+    /**
+     * Whether this type is public (and must maintain the same
+     * type ID across builds).
+     */
+    Public publicStatus;
+
+    /**
+     * List of resources for this type.
+     */
+    std::vector<std::unique_ptr<ResourceEntry>> entries;
+
+    ResourceTableType(const ResourceType _type);
+    ResourceTableType(const ResourceTableType* rhs);
+};
+
+/**
+ * The container and index for all resources defined for an app. This gets
+ * flattened into a binary resource table (resources.arsc).
+ */
+class ResourceTable {
+public:
+    using iterator = std::vector<std::unique_ptr<ResourceTableType>>::iterator;
+    using const_iterator = std::vector<std::unique_ptr<ResourceTableType>>::const_iterator;
+
+    enum {
+        kUnsetPackageId = 0xffffffff
+    };
+
+    ResourceTable();
+
+    size_t getPackageId() const;
+    void setPackageId(size_t packageId);
+
+    const std::u16string& getPackage() const;
+    void setPackage(const StringPiece16& package);
+
+    bool addResource(const ResourceNameRef& name, const ConfigDescription& config,
+                     const SourceLine& source, std::unique_ptr<Value> value);
+
+    bool addResource(const ResourceNameRef& name, const ResourceId resId,
+                     const ConfigDescription& config, const SourceLine& source,
+                     std::unique_ptr<Value> value);
+
+    bool markPublic(const ResourceNameRef& name, const ResourceId resId, const SourceLine& source);
+
+    /**
+     * Returns the string pool used by this ResourceTable.
+     * Values that reference strings should use this pool to create
+     * their strings.
+     */
+    StringPool& getValueStringPool();
+    const StringPool& getValueStringPool() const;
+
+    std::tuple<const ResourceTableType*, const ResourceEntry*>
+    findResource(const ResourceNameRef& name) const;
+
+    iterator begin();
+    iterator end();
+    const_iterator begin() const;
+    const_iterator end() const;
+
+private:
+    std::unique_ptr<ResourceTableType>& findOrCreateType(ResourceType type);
+    std::unique_ptr<ResourceEntry>& findOrCreateEntry(std::unique_ptr<ResourceTableType>& type,
+                                                      const StringPiece16& name);
+
+    std::u16string mPackage;
+    size_t mPackageId;
+
+    // StringPool must come before mTypes so that it is destroyed after.
+    // When StringPool references are destroyed (as they will be when mTypes
+    // is destroyed), they decrement a refCount, which would cause invalid
+    // memory access if the pool was already destroyed.
+    StringPool mValuePool;
+
+    std::vector<std::unique_ptr<ResourceTableType>> mTypes;
+};
+
+//
+// ResourceEntry implementation.
+//
+
+inline ResourceEntry::ResourceEntry(const StringPiece16& _name) :
+        name(_name.toString()), entryId(kUnsetEntryId) {
+}
+
+inline ResourceEntry::ResourceEntry(const ResourceEntry* rhs) :
+        name(rhs->name), entryId(rhs->entryId), publicStatus(rhs->publicStatus) {
+}
+
+//
+// ResourceTableType implementation.
+//
+
+inline ResourceTableType::ResourceTableType(const ResourceType _type) :
+        type(_type), typeId(kUnsetTypeId) {
+}
+
+inline ResourceTableType::ResourceTableType(const ResourceTableType* rhs) :
+        type(rhs->type), typeId(rhs->typeId), publicStatus(rhs->publicStatus) {
+}
+
+//
+// ResourceTable implementation.
+//
+
+inline StringPool& ResourceTable::getValueStringPool() {
+    return mValuePool;
+}
+
+inline const StringPool& ResourceTable::getValueStringPool() const {
+    return mValuePool;
+}
+
+inline ResourceTable::iterator ResourceTable::begin() {
+    return mTypes.begin();
+}
+
+inline ResourceTable::iterator ResourceTable::end() {
+    return mTypes.end();
+}
+
+inline ResourceTable::const_iterator ResourceTable::begin() const {
+    return mTypes.begin();
+}
+
+inline ResourceTable::const_iterator ResourceTable::end() const {
+    return mTypes.end();
+}
+
+inline const std::u16string& ResourceTable::getPackage() const {
+    return mPackage;
+}
+
+inline size_t ResourceTable::getPackageId() const {
+    return mPackageId;
+}
+
+inline void ResourceTable::setPackage(const StringPiece16& package) {
+    mPackage = package.toString();
+}
+
+inline void ResourceTable::setPackageId(size_t packageId) {
+    mPackageId = packageId;
+}
+
+} // namespace aapt
+
+#endif // AAPT_RESOURCE_TABLE_H
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
new file mode 100644
index 0000000..785ea15
--- /dev/null
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -0,0 +1,228 @@
+/*
+ * 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.
+ */
+
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "Util.h"
+
+#include <algorithm>
+#include <gtest/gtest.h>
+#include <ostream>
+#include <string>
+
+namespace aapt {
+
+struct TestValue : public Value {
+    std::u16string value;
+
+    TestValue(StringPiece16 str) : value(str.toString()) {
+    }
+
+    TestValue* clone() const override {
+        return new TestValue(value);
+    }
+
+    void print(std::ostream& out) const override {
+        out << "(test) " << value;
+    }
+
+    virtual void accept(ValueVisitor&, ValueVisitorArgs&&) override {}
+    virtual void accept(ConstValueVisitor&, ValueVisitorArgs&&) const override {}
+};
+
+struct TestWeakValue : public Value {
+    bool isWeak() const override {
+        return true;
+    }
+
+    TestWeakValue* clone() const override {
+        return new TestWeakValue();
+    }
+
+    void print(std::ostream& out) const override {
+        out << "(test) [weak]";
+    }
+
+    virtual void accept(ValueVisitor&, ValueVisitorArgs&&) override {}
+    virtual void accept(ConstValueVisitor&, ValueVisitorArgs&&) const override {}
+};
+
+TEST(ResourceTableTest, FailToAddResourceWithBadName) {
+    ResourceTable table;
+    table.setPackage(u"android");
+
+    EXPECT_FALSE(table.addResource(
+            ResourceNameRef{ u"android", ResourceType::kId, u"hey,there" },
+            {}, SourceLine{ "test.xml", 21 },
+            util::make_unique<TestValue>(u"rawValue")));
+
+    EXPECT_FALSE(table.addResource(
+            ResourceNameRef{ u"android", ResourceType::kId, u"hey:there" },
+            {}, SourceLine{ "test.xml", 21 },
+            util::make_unique<TestValue>(u"rawValue")));
+}
+
+TEST(ResourceTableTest, AddOneResource) {
+    const std::u16string kAndroidPackage = u"android";
+
+    ResourceTable table;
+    table.setPackage(kAndroidPackage);
+
+    const ResourceName name = { kAndroidPackage, ResourceType::kAttr, u"id" };
+
+    EXPECT_TRUE(table.addResource(name, {}, SourceLine{ "test/path/file.xml", 23 },
+                                  util::make_unique<TestValue>(u"rawValue")));
+
+    const ResourceTableType* type;
+    const ResourceEntry* entry;
+    std::tie(type, entry) = table.findResource(name);
+    ASSERT_NE(nullptr, type);
+    ASSERT_NE(nullptr, entry);
+    EXPECT_EQ(name.entry, entry->name);
+
+    ASSERT_NE(std::end(entry->values),
+              std::find_if(std::begin(entry->values), std::end(entry->values),
+                      [](const ResourceConfigValue& val) -> bool {
+                          return val.config == ConfigDescription{};
+                      }));
+}
+
+TEST(ResourceTableTest, AddMultipleResources) {
+    const std::u16string kAndroidPackage = u"android";
+    ResourceTable table;
+    table.setPackage(kAndroidPackage);
+
+    ConfigDescription config;
+    ConfigDescription languageConfig;
+    memcpy(languageConfig.language, "pl", sizeof(languageConfig.language));
+
+    EXPECT_TRUE(table.addResource(
+            ResourceName{ kAndroidPackage, ResourceType::kAttr, u"layout_width" },
+            config, SourceLine{ "test/path/file.xml", 10 },
+            util::make_unique<TestValue>(u"rawValue")));
+
+    EXPECT_TRUE(table.addResource(
+            ResourceName{ kAndroidPackage, ResourceType::kAttr, u"id" },
+            config, SourceLine{ "test/path/file.xml", 12 },
+            util::make_unique<TestValue>(u"rawValue")));
+
+    EXPECT_TRUE(table.addResource(
+            ResourceName{ kAndroidPackage, ResourceType::kString, u"ok" },
+            config, SourceLine{ "test/path/file.xml", 14 },
+            util::make_unique<TestValue>(u"Ok")));
+
+    EXPECT_TRUE(table.addResource(
+            ResourceName{ kAndroidPackage, ResourceType::kString, u"ok" },
+            languageConfig, SourceLine{ "test/path/file.xml", 20 },
+            util::make_unique<TestValue>(u"Tak")));
+
+    const auto endTypeIter = std::end(table);
+    auto typeIter = std::begin(table);
+
+    ASSERT_NE(endTypeIter, typeIter);
+    EXPECT_EQ(ResourceType::kAttr, (*typeIter)->type);
+
+    {
+        const std::unique_ptr<ResourceTableType>& type = *typeIter;
+        const auto endEntryIter = std::end(type->entries);
+        auto entryIter = std::begin(type->entries);
+        ASSERT_NE(endEntryIter, entryIter);
+        EXPECT_EQ(std::u16string(u"id"), (*entryIter)->name);
+
+        ++entryIter;
+        ASSERT_NE(endEntryIter, entryIter);
+        EXPECT_EQ(std::u16string(u"layout_width"), (*entryIter)->name);
+
+        ++entryIter;
+        ASSERT_EQ(endEntryIter, entryIter);
+    }
+
+    ++typeIter;
+    ASSERT_NE(endTypeIter, typeIter);
+    EXPECT_EQ(ResourceType::kString, (*typeIter)->type);
+
+    {
+        const std::unique_ptr<ResourceTableType>& type = *typeIter;
+        const auto endEntryIter = std::end(type->entries);
+        auto entryIter = std::begin(type->entries);
+        ASSERT_NE(endEntryIter, entryIter);
+        EXPECT_EQ(std::u16string(u"ok"), (*entryIter)->name);
+
+        {
+            const std::unique_ptr<ResourceEntry>& entry = *entryIter;
+            const auto endConfigIter = std::end(entry->values);
+            auto configIter = std::begin(entry->values);
+
+            ASSERT_NE(endConfigIter, configIter);
+            EXPECT_EQ(config, configIter->config);
+            const TestValue* value =
+                    dynamic_cast<const TestValue*>(configIter->value.get());
+            ASSERT_NE(nullptr, value);
+            EXPECT_EQ(std::u16string(u"Ok"), value->value);
+
+            ++configIter;
+            ASSERT_NE(endConfigIter, configIter);
+            EXPECT_EQ(languageConfig, configIter->config);
+            EXPECT_NE(nullptr, configIter->value);
+
+            value = dynamic_cast<const TestValue*>(configIter->value.get());
+            ASSERT_NE(nullptr, value);
+            EXPECT_EQ(std::u16string(u"Tak"), value->value);
+
+            ++configIter;
+            EXPECT_EQ(endConfigIter, configIter);
+        }
+
+        ++entryIter;
+        ASSERT_EQ(endEntryIter, entryIter);
+    }
+
+    ++typeIter;
+    EXPECT_EQ(endTypeIter, typeIter);
+}
+
+TEST(ResourceTableTest, OverrideWeakResourceValue) {
+    const std::u16string kAndroid = u"android";
+
+    ResourceTable table;
+    table.setPackage(kAndroid);
+    table.setPackageId(0x01);
+
+    ASSERT_TRUE(table.addResource(
+            ResourceName{ kAndroid, ResourceType::kAttr, u"foo" },
+            {}, {}, util::make_unique<TestWeakValue>()));
+
+    const ResourceTableType* type;
+    const ResourceEntry* entry;
+    std::tie(type, entry) = table.findResource(
+            ResourceNameRef{ kAndroid, ResourceType::kAttr, u"foo" });
+    ASSERT_NE(nullptr, type);
+    ASSERT_NE(nullptr, entry);
+    ASSERT_EQ(entry->values.size(), 1u);
+    EXPECT_TRUE(entry->values.front().value->isWeak());
+
+    ASSERT_TRUE(table.addResource(ResourceName{ kAndroid, ResourceType::kAttr, u"foo" }, {}, {},
+                                  util::make_unique<TestValue>(u"bar")));
+
+    std::tie(type, entry) = table.findResource(
+            ResourceNameRef{ kAndroid, ResourceType::kAttr, u"foo" });
+    ASSERT_NE(nullptr, type);
+    ASSERT_NE(nullptr, entry);
+    ASSERT_EQ(entry->values.size(), 1u);
+    EXPECT_FALSE(entry->values.front().value->isWeak());
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ResourceTypeExtensions.h b/tools/aapt2/ResourceTypeExtensions.h
new file mode 100644
index 0000000..60e225e
--- /dev/null
+++ b/tools/aapt2/ResourceTypeExtensions.h
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_RESOURCE_TYPE_EXTENSIONS_H
+#define AAPT_RESOURCE_TYPE_EXTENSIONS_H
+
+#include <androidfw/ResourceTypes.h>
+
+namespace aapt {
+
+/**
+ * New android::ResChunk_header types defined
+ * for AAPT to use.
+ *
+ * TODO(adamlesinski): Consider reserving these
+ * enums in androidfw/ResourceTypes.h to avoid
+ * future collisions.
+ */
+enum {
+    /**
+     * A chunk that holds the string pool
+     * for source entries (path/to/source:line).
+     */
+    RES_TABLE_SOURCE_POOL_TYPE = 0x000e,
+
+    /**
+     * A chunk holding names of externally
+     * defined symbols and offsets to where
+     * they are referenced in the table.
+     */
+    RES_TABLE_SYMBOL_TABLE_TYPE = 0x000f,
+};
+
+/**
+ * New resource types that are meant to only be used
+ * by AAPT and will not end up on the device.
+ */
+struct ExtendedTypes {
+    enum {
+        /**
+         * A sentinel value used when a resource is defined as
+         * public but it has no defined value yet. If we don't
+         * flatten it with some value, we will lose its name.
+         */
+        TYPE_SENTINEL = 0xff,
+
+        /**
+         * A raw string value that hasn't had its escape sequences
+         * processed nor whitespace removed.
+         */
+        TYPE_RAW_STRING = 0xfe
+    };
+};
+
+/**
+ * A chunk with type RES_TABLE_SYMBOL_TABLE_TYPE.
+ * Following the header are count number of SymbolTable_entry
+ * structures, followed by an android::ResStringPool_header.
+ */
+struct SymbolTable_header {
+    android::ResChunk_header header;
+
+    /**
+     * Number of SymbolTable_entry structures following
+     * this header.
+     */
+    uint32_t count;
+};
+
+struct SymbolTable_entry {
+    /**
+     * Offset from the beginning of the resource table
+     * where the symbol entry is referenced.
+     */
+    uint32_t offset;
+
+    /**
+     * The index into the string pool where the name of this
+     * symbol exists.
+     */
+    uint32_t stringIndex;
+};
+
+/**
+ * A structure representing the source of a resourc entry.
+ * Appears after an android::ResTable_entry or android::ResTable_map_entry.
+ *
+ * TODO(adamlesinski): This causes some issues when runtime code checks
+ * the size of an android::ResTable_entry. It assumes it is an
+ * android::ResTable_map_entry if the size is bigger than an android::ResTable_entry
+ * which may not be true if this structure is present.
+ */
+struct ResTable_entry_source {
+    /**
+     * Index into the source string pool.
+     */
+    uint32_t pathIndex;
+
+    /**
+     * Line number this resource was defined on.
+     */
+    uint32_t line;
+};
+
+} // namespace aapt
+
+#endif // AAPT_RESOURCE_TYPE_EXTENSIONS_H
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
new file mode 100644
index 0000000..60ef1a8
--- /dev/null
+++ b/tools/aapt2/ResourceValues.cpp
@@ -0,0 +1,447 @@
+/*
+ * 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.
+ */
+
+#include "Resource.h"
+#include "ResourceTypeExtensions.h"
+#include "ResourceValues.h"
+#include "Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <limits>
+
+namespace aapt {
+
+bool Value::isItem() const {
+    return false;
+}
+
+bool Value::isWeak() const {
+    return false;
+}
+
+bool Item::isItem() const {
+    return true;
+}
+
+RawString::RawString(const StringPool::Ref& ref) : value(ref) {
+}
+
+RawString* RawString::clone() const {
+    return new RawString(value);
+}
+
+bool RawString::flatten(android::Res_value& outValue) const {
+    outValue.dataType = ExtendedTypes::TYPE_RAW_STRING;
+    outValue.data = static_cast<uint32_t>(value.getIndex());
+    return true;
+}
+
+void RawString::print(std::ostream& out) const {
+    out << "(raw string) " << *value;
+}
+
+Reference::Reference() : referenceType(Reference::Type::kResource) {
+}
+
+Reference::Reference(const ResourceNameRef& n, Type t) :
+        name(n.toResourceName()), referenceType(t) {
+}
+
+Reference::Reference(const ResourceId& i, Type type) : id(i), referenceType(type) {
+}
+
+bool Reference::flatten(android::Res_value& outValue) const {
+    outValue.dataType = (referenceType == Reference::Type::kResource)
+        ? android::Res_value::TYPE_REFERENCE
+        : android::Res_value::TYPE_ATTRIBUTE;
+    outValue.data = id.id;
+    return true;
+}
+
+Reference* Reference::clone() const {
+    Reference* ref = new Reference();
+    ref->referenceType = referenceType;
+    ref->name = name;
+    ref->id = id;
+    return ref;
+}
+
+void Reference::print(std::ostream& out) const {
+    out << "(reference) ";
+    if (referenceType == Reference::Type::kResource) {
+        out << "@";
+    } else {
+        out << "?";
+    }
+
+    if (name.isValid()) {
+        out << name;
+    }
+
+    if (id.isValid() || Res_INTERNALID(id.id)) {
+        out << " " << id;
+    }
+}
+
+bool Id::isWeak() const {
+    return true;
+}
+
+bool Id::flatten(android::Res_value& out) const {
+    out.dataType = android::Res_value::TYPE_NULL;
+    out.data = android::Res_value::DATA_NULL_UNDEFINED;
+    return true;
+}
+
+Id* Id::clone() const {
+    return new Id();
+}
+
+void Id::print(std::ostream& out) const {
+    out << "(id)";
+}
+
+String::String(const StringPool::Ref& ref) : value(ref) {
+}
+
+bool String::flatten(android::Res_value& outValue) const {
+    // Verify that our StringPool index is within encodeable limits.
+    if (value.getIndex() > std::numeric_limits<uint32_t>::max()) {
+        return false;
+    }
+
+    outValue.dataType = android::Res_value::TYPE_STRING;
+    outValue.data = static_cast<uint32_t>(value.getIndex());
+    return true;
+}
+
+String* String::clone() const {
+    return new String(value);
+}
+
+void String::print(std::ostream& out) const {
+    out << "(string) \"" << *value << "\"";
+}
+
+StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref) {
+}
+
+bool StyledString::flatten(android::Res_value& outValue) const {
+    if (value.getIndex() > std::numeric_limits<uint32_t>::max()) {
+        return false;
+    }
+
+    outValue.dataType = android::Res_value::TYPE_STRING;
+    outValue.data = static_cast<uint32_t>(value.getIndex());
+    return true;
+}
+
+StyledString* StyledString::clone() const {
+    return new StyledString(value);
+}
+
+void StyledString::print(std::ostream& out) const {
+    out << "(styled string) \"" << *value->str << "\"";
+}
+
+FileReference::FileReference(const StringPool::Ref& _path) : path(_path) {
+}
+
+bool FileReference::flatten(android::Res_value& outValue) const {
+    if (path.getIndex() > std::numeric_limits<uint32_t>::max()) {
+        return false;
+    }
+
+    outValue.dataType = android::Res_value::TYPE_STRING;
+    outValue.data = static_cast<uint32_t>(path.getIndex());
+    return true;
+}
+
+FileReference* FileReference::clone() const {
+    return new FileReference(path);
+}
+
+void FileReference::print(std::ostream& out) const {
+    out << "(file) " << *path;
+}
+
+BinaryPrimitive::BinaryPrimitive(const android::Res_value& val) : value(val) {
+}
+
+bool BinaryPrimitive::flatten(android::Res_value& outValue) const {
+    outValue = value;
+    return true;
+}
+
+BinaryPrimitive* BinaryPrimitive::clone() const {
+    return new BinaryPrimitive(value);
+}
+
+void BinaryPrimitive::print(std::ostream& out) const {
+    switch (value.dataType) {
+        case android::Res_value::TYPE_NULL:
+            out << "(null)";
+            break;
+        case android::Res_value::TYPE_INT_DEC:
+            out << "(integer) " << value.data;
+            break;
+        case android::Res_value::TYPE_INT_HEX:
+            out << "(integer) " << std::hex << value.data << std::dec;
+            break;
+        case android::Res_value::TYPE_INT_BOOLEAN:
+            out << "(boolean) " << (value.data != 0 ? "true" : "false");
+            break;
+        case android::Res_value::TYPE_INT_COLOR_ARGB8:
+        case android::Res_value::TYPE_INT_COLOR_RGB8:
+        case android::Res_value::TYPE_INT_COLOR_ARGB4:
+        case android::Res_value::TYPE_INT_COLOR_RGB4:
+            out << "(color) #" << std::hex << value.data << std::dec;
+            break;
+        default:
+            out << "(unknown 0x" << std::hex << (int) value.dataType << ") 0x"
+                << std::hex << value.data << std::dec;
+            break;
+    }
+}
+
+bool Sentinel::isWeak() const {
+    return true;
+}
+
+bool Sentinel::flatten(android::Res_value& outValue) const {
+    outValue.dataType = ExtendedTypes::TYPE_SENTINEL;
+    outValue.data = 0;
+    return true;
+}
+
+Sentinel* Sentinel::clone() const {
+    return new Sentinel();
+}
+
+void Sentinel::print(std::ostream& out) const {
+    out << "(sentinel)";
+    return;
+}
+
+Attribute::Attribute(bool w, uint32_t t) : weak(w), typeMask(t) {
+}
+
+bool Attribute::isWeak() const {
+    return weak;
+}
+
+Attribute* Attribute::clone() const {
+    Attribute* attr = new Attribute(weak);
+    attr->typeMask = typeMask;
+    std::copy(symbols.begin(), symbols.end(), std::back_inserter(attr->symbols));
+    return attr;
+}
+
+void Attribute::print(std::ostream& out) const {
+    out << "(attr)";
+    if (typeMask == android::ResTable_map::TYPE_ANY) {
+        out << " any";
+        return;
+    }
+
+    bool set = false;
+    if ((typeMask & android::ResTable_map::TYPE_REFERENCE) != 0) {
+        if (!set) {
+            out << " ";
+            set = true;
+        } else {
+            out << "|";
+        }
+        out << "reference";
+    }
+
+    if ((typeMask & android::ResTable_map::TYPE_STRING) != 0) {
+        if (!set) {
+            out << " ";
+            set = true;
+        } else {
+            out << "|";
+        }
+        out << "string";
+    }
+
+    if ((typeMask & android::ResTable_map::TYPE_INTEGER) != 0) {
+        if (!set) {
+            out << " ";
+            set = true;
+        } else {
+            out << "|";
+        }
+        out << "integer";
+    }
+
+    if ((typeMask & android::ResTable_map::TYPE_BOOLEAN) != 0) {
+        if (!set) {
+            out << " ";
+            set = true;
+        } else {
+            out << "|";
+        }
+        out << "boolean";
+    }
+
+    if ((typeMask & android::ResTable_map::TYPE_COLOR) != 0) {
+        if (!set) {
+            out << " ";
+            set = true;
+        } else {
+            out << "|";
+        }
+        out << "color";
+    }
+
+    if ((typeMask & android::ResTable_map::TYPE_FLOAT) != 0) {
+        if (!set) {
+            out << " ";
+            set = true;
+        } else {
+            out << "|";
+        }
+        out << "float";
+    }
+
+    if ((typeMask & android::ResTable_map::TYPE_DIMENSION) != 0) {
+        if (!set) {
+            out << " ";
+            set = true;
+        } else {
+            out << "|";
+        }
+        out << "dimension";
+    }
+
+    if ((typeMask & android::ResTable_map::TYPE_FRACTION) != 0) {
+        if (!set) {
+            out << " ";
+            set = true;
+        } else {
+            out << "|";
+        }
+        out << "fraction";
+    }
+
+    if ((typeMask & android::ResTable_map::TYPE_ENUM) != 0) {
+        if (!set) {
+            out << " ";
+            set = true;
+        } else {
+            out << "|";
+        }
+        out << "enum";
+    }
+
+    if ((typeMask & android::ResTable_map::TYPE_FLAGS) != 0) {
+        if (!set) {
+            out << " ";
+            set = true;
+        } else {
+            out << "|";
+        }
+        out << "flags";
+    }
+
+    out << " ["
+        << util::joiner(symbols.begin(), symbols.end(), ", ")
+        << "]";
+
+    if (weak) {
+        out << " [weak]";
+    }
+}
+
+static ::std::ostream& operator<<(::std::ostream& out, const Attribute::Symbol& s) {
+    return out << s.symbol.name.entry << "=" << s.value;
+}
+
+Style* Style::clone() const {
+    Style* style = new Style();
+    style->parent = parent;
+    for (auto& entry : entries) {
+        style->entries.push_back(Entry{
+                entry.key,
+                std::unique_ptr<Item>(entry.value->clone())
+        });
+    }
+    return style;
+}
+
+void Style::print(std::ostream& out) const {
+    out << "(style) ";
+    if (!parent.name.entry.empty()) {
+        out << parent.name;
+    }
+    out << " ["
+        << util::joiner(entries.begin(), entries.end(), ", ")
+        << "]";
+}
+
+static ::std::ostream& operator<<(::std::ostream& out, const Style::Entry& value) {
+    out << value.key.name << " = ";
+    value.value->print(out);
+    return out;
+}
+
+Array* Array::clone() const {
+    Array* array = new Array();
+    for (auto& item : items) {
+        array->items.emplace_back(std::unique_ptr<Item>(item->clone()));
+    }
+    return array;
+}
+
+void Array::print(std::ostream& out) const {
+    out << "(array) ["
+        << util::joiner(items.begin(), items.end(), ", ")
+        << "]";
+}
+
+Plural* Plural::clone() const {
+    Plural* p = new Plural();
+    const size_t count = values.size();
+    for (size_t i = 0; i < count; i++) {
+        if (values[i]) {
+            p->values[i] = std::unique_ptr<Item>(values[i]->clone());
+        }
+    }
+    return p;
+}
+
+void Plural::print(std::ostream& out) const {
+    out << "(plural)";
+}
+
+static ::std::ostream& operator<<(::std::ostream& out, const std::unique_ptr<Item>& item) {
+    return out << *item;
+}
+
+Styleable* Styleable::clone() const {
+    Styleable* styleable = new Styleable();
+    std::copy(entries.begin(), entries.end(), std::back_inserter(styleable->entries));
+    return styleable;
+}
+
+void Styleable::print(std::ostream& out) const {
+    out << "(styleable) " << " ["
+        << util::joiner(entries.begin(), entries.end(), ", ")
+        << "]";
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
new file mode 100644
index 0000000..f25bcf0
--- /dev/null
+++ b/tools/aapt2/ResourceValues.h
@@ -0,0 +1,456 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_RESOURCE_VALUES_H
+#define AAPT_RESOURCE_VALUES_H
+
+#include "Resource.h"
+#include "StringPool.h"
+
+#include <array>
+#include <androidfw/ResourceTypes.h>
+#include <ostream>
+#include <vector>
+
+namespace aapt {
+
+struct ValueVisitor;
+struct ConstValueVisitor;
+struct ValueVisitorArgs;
+
+/**
+ * A resource value. This is an all-encompassing representation
+ * of Item and Map and their subclasses. The way to do
+ * type specific operations is to check the Value's type() and
+ * cast it to the appropriate subclass. This isn't super clean,
+ * but it is the simplest strategy.
+ */
+struct Value {
+    /**
+     * Whether or not this is an Item.
+     */
+    virtual bool isItem() const;
+
+    /**
+     * Whether this value is weak and can be overriden without
+     * warning or error. Default for base class is false.
+     */
+    virtual bool isWeak() const;
+
+    /**
+     * Calls the appropriate overload of ValueVisitor.
+     */
+    virtual void accept(ValueVisitor& visitor, ValueVisitorArgs&& args) = 0;
+
+    /**
+     * Const version of accept().
+     */
+    virtual void accept(ConstValueVisitor& visitor, ValueVisitorArgs&& args) const = 0;
+
+    /**
+     * Clone the value.
+     */
+    virtual Value* clone() const = 0;
+
+    /**
+     * Human readable printout of this value.
+     */
+    virtual void print(std::ostream& out) const = 0;
+};
+
+/**
+ * Inherit from this to get visitor accepting implementations for free.
+ */
+template <typename Derived>
+struct BaseValue : public Value {
+    virtual void accept(ValueVisitor& visitor, ValueVisitorArgs&& args) override;
+    virtual void accept(ConstValueVisitor& visitor, ValueVisitorArgs&& args) const override;
+};
+
+/**
+ * A resource item with a single value. This maps to android::ResTable_entry.
+ */
+struct Item : public Value {
+    /**
+     * An Item is, of course, an Item.
+     */
+    virtual bool isItem() const override;
+
+    /**
+     * Clone the Item.
+     */
+    virtual Item* clone() const override = 0;
+
+    /**
+     * Fills in an android::Res_value structure with this Item's binary representation.
+     * Returns false if an error ocurred.
+     */
+    virtual bool flatten(android::Res_value& outValue) const = 0;
+};
+
+/**
+ * Inherit from this to get visitor accepting implementations for free.
+ */
+template <typename Derived>
+struct BaseItem : public Item {
+    virtual void accept(ValueVisitor& visitor, ValueVisitorArgs&& args) override;
+    virtual void accept(ConstValueVisitor& visitor, ValueVisitorArgs&& args) const override;
+};
+
+/**
+ * A reference to another resource. This maps to android::Res_value::TYPE_REFERENCE.
+ *
+ * A reference can be symbolic (with the name set to a valid resource name) or be
+ * numeric (the id is set to a valid resource ID).
+ */
+struct Reference : public BaseItem<Reference> {
+    enum class Type {
+        kResource,
+        kAttribute,
+    };
+
+    ResourceName name;
+    ResourceId id;
+    Reference::Type referenceType;
+    bool privateReference = false;
+
+    Reference();
+    Reference(const ResourceNameRef& n, Type type = Type::kResource);
+    Reference(const ResourceId& i, Type type = Type::kResource);
+
+    bool flatten(android::Res_value& outValue) const override;
+    Reference* clone() const override;
+    void print(std::ostream& out) const override;
+};
+
+/**
+ * An ID resource. Has no real value, just a place holder.
+ */
+struct Id : public BaseItem<Id> {
+    bool isWeak() const override;
+    bool flatten(android::Res_value& out) const override;
+    Id* clone() const override;
+    void print(std::ostream& out) const override;
+};
+
+/**
+ * A raw, unprocessed string. This may contain quotations,
+ * escape sequences, and whitespace. This shall *NOT*
+ * end up in the final resource table.
+ */
+struct RawString : public BaseItem<RawString> {
+    StringPool::Ref value;
+
+    RawString(const StringPool::Ref& ref);
+
+    bool flatten(android::Res_value& outValue) const override;
+    RawString* clone() const override;
+    void print(std::ostream& out) const override;
+};
+
+struct String : public BaseItem<String> {
+    StringPool::Ref value;
+
+    String(const StringPool::Ref& ref);
+
+    bool flatten(android::Res_value& outValue) const override;
+    String* clone() const override;
+    void print(std::ostream& out) const override;
+};
+
+struct StyledString : public BaseItem<StyledString> {
+    StringPool::StyleRef value;
+
+    StyledString(const StringPool::StyleRef& ref);
+
+    bool flatten(android::Res_value& outValue) const override;
+    StyledString* clone() const override;
+    void print(std::ostream& out) const override;
+};
+
+struct FileReference : public BaseItem<FileReference> {
+    StringPool::Ref path;
+
+    FileReference() = default;
+    FileReference(const StringPool::Ref& path);
+
+    bool flatten(android::Res_value& outValue) const override;
+    FileReference* clone() const override;
+    void print(std::ostream& out) const override;
+};
+
+/**
+ * Represents any other android::Res_value.
+ */
+struct BinaryPrimitive : public BaseItem<BinaryPrimitive> {
+    android::Res_value value;
+
+    BinaryPrimitive() = default;
+    BinaryPrimitive(const android::Res_value& val);
+
+    bool flatten(android::Res_value& outValue) const override;
+    BinaryPrimitive* clone() const override;
+    void print(::std::ostream& out) const override;
+};
+
+/**
+ * Sentinel value that should be ignored in the final output.
+ * Mainly used as a placeholder for public entries with no
+ * values defined yet.
+ */
+struct Sentinel : public BaseItem<Sentinel> {
+    bool isWeak() const override;
+    bool flatten(android::Res_value& outValue) const override;
+    Sentinel* clone() const override;
+    void print(::std::ostream& out) const override;
+};
+
+struct Attribute : public BaseValue<Attribute> {
+    struct Symbol {
+        Reference symbol;
+        uint32_t value;
+    };
+
+    bool weak;
+    uint32_t typeMask;
+    uint32_t minInt;
+    uint32_t maxInt;
+    std::vector<Symbol> symbols;
+
+    Attribute(bool w, uint32_t t = 0u);
+
+    bool isWeak() const override;
+    virtual Attribute* clone() const override;
+    virtual void print(std::ostream& out) const override;
+};
+
+struct Style : public BaseValue<Style> {
+    struct Entry {
+        Reference key;
+        std::unique_ptr<Item> value;
+    };
+
+    Reference parent;
+    std::vector<Entry> entries;
+
+    Style* clone() const override;
+    void print(std::ostream& out) const override;
+};
+
+struct Array : public BaseValue<Array> {
+    std::vector<std::unique_ptr<Item>> items;
+
+    Array* clone() const override;
+    void print(std::ostream& out) const override;
+};
+
+struct Plural : public BaseValue<Plural> {
+    enum {
+        Zero = 0,
+        One,
+        Two,
+        Few,
+        Many,
+        Other,
+        Count
+    };
+
+    std::array<std::unique_ptr<Item>, Count> values;
+
+    Plural* clone() const override;
+    void print(std::ostream& out) const override;
+};
+
+struct Styleable : public BaseValue<Styleable> {
+    std::vector<Reference> entries;
+
+    Styleable* clone() const override;
+    void print(std::ostream& out) const override;
+};
+
+/**
+ * Stream operator for printing Value objects.
+ */
+inline ::std::ostream& operator<<(::std::ostream& out, const Value& value) {
+    value.print(out);
+    return out;
+}
+
+/**
+ * The argument object that gets passed through the value
+ * back to the ValueVisitor. Subclasses of ValueVisitor should
+ * subclass ValueVisitorArgs to contain the data they need
+ * to operate.
+ */
+struct ValueVisitorArgs {};
+
+/**
+ * Visits a value and runs the appropriate method based on its type.
+ */
+struct ValueVisitor {
+    virtual void visit(Reference& reference, ValueVisitorArgs& args) {
+        visitItem(reference, args);
+    }
+
+    virtual void visit(RawString& string, ValueVisitorArgs& args) {
+        visitItem(string, args);
+    }
+
+    virtual void visit(String& string, ValueVisitorArgs& args) {
+        visitItem(string, args);
+    }
+
+    virtual void visit(StyledString& string, ValueVisitorArgs& args) {
+        visitItem(string, args);
+    }
+
+    virtual void visit(FileReference& file, ValueVisitorArgs& args) {
+        visitItem(file, args);
+    }
+
+    virtual void visit(Id& id, ValueVisitorArgs& args) {
+        visitItem(id, args);
+    }
+
+    virtual void visit(BinaryPrimitive& primitive, ValueVisitorArgs& args) {
+        visitItem(primitive, args);
+    }
+
+    virtual void visit(Sentinel& sentinel, ValueVisitorArgs& args) {
+        visitItem(sentinel, args);
+    }
+
+    virtual void visit(Attribute& attr, ValueVisitorArgs& args) {}
+    virtual void visit(Style& style, ValueVisitorArgs& args) {}
+    virtual void visit(Array& array, ValueVisitorArgs& args) {}
+    virtual void visit(Plural& array, ValueVisitorArgs& args) {}
+    virtual void visit(Styleable& styleable, ValueVisitorArgs& args) {}
+
+    virtual void visitItem(Item& item, ValueVisitorArgs& args) {}
+};
+
+/**
+ * Const version of ValueVisitor.
+ */
+struct ConstValueVisitor {
+    virtual void visit(const Reference& reference, ValueVisitorArgs& args) {
+        visitItem(reference, args);
+    }
+
+    virtual void visit(const RawString& string, ValueVisitorArgs& args) {
+        visitItem(string, args);
+    }
+
+    virtual void visit(const String& string, ValueVisitorArgs& args) {
+        visitItem(string, args);
+    }
+
+    virtual void visit(const StyledString& string, ValueVisitorArgs& args) {
+        visitItem(string, args);
+    }
+
+    virtual void visit(const FileReference& file, ValueVisitorArgs& args) {
+        visitItem(file, args);
+    }
+
+    virtual void visit(const Id& id, ValueVisitorArgs& args) {
+        visitItem(id, args);
+    }
+
+    virtual void visit(const BinaryPrimitive& primitive, ValueVisitorArgs& args) {
+        visitItem(primitive, args);
+    }
+
+    virtual void visit(const Sentinel& sentinel, ValueVisitorArgs& args) {
+        visitItem(sentinel, args);
+    }
+
+    virtual void visit(const Attribute& attr, ValueVisitorArgs& args) {}
+    virtual void visit(const Style& style, ValueVisitorArgs& args) {}
+    virtual void visit(const Array& array, ValueVisitorArgs& args) {}
+    virtual void visit(const Plural& array, ValueVisitorArgs& args) {}
+    virtual void visit(const Styleable& styleable, ValueVisitorArgs& args) {}
+
+    virtual void visitItem(const Item& item, ValueVisitorArgs& args) {}
+};
+
+/**
+ * Convenience Visitor that forwards a specific type to a function.
+ * Args are not used as the function can bind variables. Do not use
+ * directly, use the wrapper visitFunc() method.
+ */
+template <typename T, typename TFunc>
+struct ValueVisitorFunc : ValueVisitor {
+    TFunc func;
+
+    ValueVisitorFunc(TFunc f) : func(f) {
+    }
+
+    void visit(T& value, ValueVisitorArgs&) override {
+        func(value);
+    }
+};
+
+/**
+ * Const version of ValueVisitorFunc.
+ */
+template <typename T, typename TFunc>
+struct ConstValueVisitorFunc : ConstValueVisitor {
+    TFunc func;
+
+    ConstValueVisitorFunc(TFunc f) : func(f) {
+    }
+
+    void visit(const T& value, ValueVisitorArgs&) override {
+        func(value);
+    }
+};
+
+template <typename T, typename TFunc>
+void visitFunc(Value& value, TFunc f) {
+    ValueVisitorFunc<T, TFunc> visitor(f);
+    value.accept(visitor, ValueVisitorArgs{});
+}
+
+template <typename T, typename TFunc>
+void visitFunc(const Value& value, TFunc f) {
+    ConstValueVisitorFunc<T, TFunc> visitor(f);
+    value.accept(visitor, ValueVisitorArgs{});
+}
+
+template <typename Derived>
+void BaseValue<Derived>::accept(ValueVisitor& visitor, ValueVisitorArgs&& args) {
+    visitor.visit(static_cast<Derived&>(*this), args);
+}
+
+template <typename Derived>
+void BaseValue<Derived>::accept(ConstValueVisitor& visitor, ValueVisitorArgs&& args) const {
+    visitor.visit(static_cast<const Derived&>(*this), args);
+}
+
+template <typename Derived>
+void BaseItem<Derived>::accept(ValueVisitor& visitor, ValueVisitorArgs&& args) {
+    visitor.visit(static_cast<Derived&>(*this), args);
+}
+
+template <typename Derived>
+void BaseItem<Derived>::accept(ConstValueVisitor& visitor, ValueVisitorArgs&& args) const {
+    visitor.visit(static_cast<const Derived&>(*this), args);
+}
+
+} // namespace aapt
+
+#endif // AAPT_RESOURCE_VALUES_H
diff --git a/tools/aapt2/Resource_test.cpp b/tools/aapt2/Resource_test.cpp
new file mode 100644
index 0000000..d957999
--- /dev/null
+++ b/tools/aapt2/Resource_test.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "Resource.h"
+
+namespace aapt {
+
+TEST(ResourceTypeTest, ParseResourceTypes) {
+    const ResourceType* type = parseResourceType(u"anim");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kAnim);
+
+    type = parseResourceType(u"animator");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kAnimator);
+
+    type = parseResourceType(u"array");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kArray);
+
+    type = parseResourceType(u"attr");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kAttr);
+
+    type = parseResourceType(u"^attr-private");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kAttrPrivate);
+
+    type = parseResourceType(u"bool");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kBool);
+
+    type = parseResourceType(u"color");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kColor);
+
+    type = parseResourceType(u"dimen");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kDimen);
+
+    type = parseResourceType(u"drawable");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kDrawable);
+
+    type = parseResourceType(u"fraction");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kFraction);
+
+    type = parseResourceType(u"id");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kId);
+
+    type = parseResourceType(u"integer");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kInteger);
+
+    type = parseResourceType(u"integer-array");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kIntegerArray);
+
+    type = parseResourceType(u"interpolator");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kInterpolator);
+
+    type = parseResourceType(u"layout");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kLayout);
+
+    type = parseResourceType(u"menu");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kMenu);
+
+    type = parseResourceType(u"mipmap");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kMipmap);
+
+    type = parseResourceType(u"plurals");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kPlurals);
+
+    type = parseResourceType(u"raw");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kRaw);
+
+    type = parseResourceType(u"string");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kString);
+
+    type = parseResourceType(u"style");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kStyle);
+
+    type = parseResourceType(u"transition");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kTransition);
+
+    type = parseResourceType(u"xml");
+    ASSERT_NE(type, nullptr);
+    EXPECT_EQ(*type, ResourceType::kXml);
+
+    type = parseResourceType(u"blahaha");
+    EXPECT_EQ(type, nullptr);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ScopedXmlPullParser.cpp b/tools/aapt2/ScopedXmlPullParser.cpp
new file mode 100644
index 0000000..d9ae72c
--- /dev/null
+++ b/tools/aapt2/ScopedXmlPullParser.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+#include "ScopedXmlPullParser.h"
+
+#include <string>
+
+namespace aapt {
+
+ScopedXmlPullParser::ScopedXmlPullParser(XmlPullParser* parser) :
+        mParser(parser), mDepth(parser->getDepth()), mDone(false) {
+}
+
+ScopedXmlPullParser::~ScopedXmlPullParser() {
+    while (isGoodEvent(next()));
+}
+
+XmlPullParser::Event ScopedXmlPullParser::next() {
+    if (mDone) {
+        return Event::kEndDocument;
+    }
+
+    const Event event = mParser->next();
+    if (mParser->getDepth() <= mDepth) {
+        mDone = true;
+    }
+    return event;
+}
+
+XmlPullParser::Event ScopedXmlPullParser::getEvent() const {
+    return mParser->getEvent();
+}
+
+const std::string& ScopedXmlPullParser::getLastError() const {
+    return mParser->getLastError();
+}
+
+const std::u16string& ScopedXmlPullParser::getComment() const {
+    return mParser->getComment();
+}
+
+size_t ScopedXmlPullParser::getLineNumber() const {
+    return mParser->getLineNumber();
+}
+
+size_t ScopedXmlPullParser::getDepth() const {
+    const size_t depth = mParser->getDepth();
+    if (depth < mDepth) {
+        return 0;
+    }
+    return depth - mDepth;
+}
+
+const std::u16string& ScopedXmlPullParser::getText() const {
+    return mParser->getText();
+}
+
+const std::u16string& ScopedXmlPullParser::getNamespacePrefix() const {
+    return mParser->getNamespacePrefix();
+}
+
+const std::u16string& ScopedXmlPullParser::getNamespaceUri() const {
+    return mParser->getNamespaceUri();
+}
+
+const std::u16string& ScopedXmlPullParser::getElementNamespace() const {
+    return mParser->getElementNamespace();
+}
+
+const std::u16string& ScopedXmlPullParser::getElementName() const {
+    return mParser->getElementName();
+}
+
+size_t ScopedXmlPullParser::getAttributeCount() const {
+    return mParser->getAttributeCount();
+}
+
+XmlPullParser::const_iterator ScopedXmlPullParser::beginAttributes() const {
+    return mParser->beginAttributes();
+}
+
+XmlPullParser::const_iterator ScopedXmlPullParser::endAttributes() const {
+    return mParser->endAttributes();
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/ScopedXmlPullParser.h b/tools/aapt2/ScopedXmlPullParser.h
new file mode 100644
index 0000000..e660499
--- /dev/null
+++ b/tools/aapt2/ScopedXmlPullParser.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_SCOPED_XML_PULL_PARSER_H
+#define AAPT_SCOPED_XML_PULL_PARSER_H
+
+#include "XmlPullParser.h"
+
+#include <string>
+
+namespace aapt {
+
+/**
+ * An XmlPullParser that will not read past the depth
+ * of the underlying parser. When this parser is destroyed,
+ * it moves the underlying parser to the same depth it
+ * started with.
+ *
+ * You can write code like this:
+ *
+ *   while (XmlPullParser::isGoodEvent(parser.next())) {
+ *     if (parser.getEvent() != XmlPullParser::Event::StartElement) {
+ *       continue;
+ *     }
+ *
+ *     ScopedXmlPullParser scoped(parser);
+ *     if (parser.getElementName() == u"id") {
+ *       // do work.
+ *     } else {
+ *       // do nothing, as all the sub elements will be skipped
+ *       // when scoped goes out of scope.
+ *     }
+ *   }
+ */
+class ScopedXmlPullParser : public XmlPullParser {
+public:
+    ScopedXmlPullParser(XmlPullParser* parser);
+    ScopedXmlPullParser(const ScopedXmlPullParser&) = delete;
+    ScopedXmlPullParser& operator=(const ScopedXmlPullParser&) = delete;
+    ~ScopedXmlPullParser();
+
+    Event getEvent() const;
+    const std::string& getLastError() const;
+    Event next();
+
+    const std::u16string& getComment() const;
+    size_t getLineNumber() const;
+    size_t getDepth() const;
+
+    const std::u16string& getText() const;
+
+    const std::u16string& getNamespacePrefix() const;
+    const std::u16string& getNamespaceUri() const;
+
+    const std::u16string& getElementNamespace() const;
+    const std::u16string& getElementName() const;
+
+    const_iterator beginAttributes() const;
+    const_iterator endAttributes() const;
+    size_t getAttributeCount() const;
+
+private:
+    XmlPullParser* mParser;
+    size_t mDepth;
+    bool mDone;
+};
+
+} // namespace aapt
+
+#endif // AAPT_SCOPED_XML_PULL_PARSER_H
diff --git a/tools/aapt2/ScopedXmlPullParser_test.cpp b/tools/aapt2/ScopedXmlPullParser_test.cpp
new file mode 100644
index 0000000..342f305
--- /dev/null
+++ b/tools/aapt2/ScopedXmlPullParser_test.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+#include "ScopedXmlPullParser.h"
+#include "SourceXmlPullParser.h"
+
+#include <gtest/gtest.h>
+#include <sstream>
+#include <string>
+
+namespace aapt {
+
+TEST(ScopedXmlPullParserTest, StopIteratingAtNoNZeroDepth) {
+    std::stringstream input;
+    input << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl
+          << "<resources><string></string></resources>" << std::endl;
+
+    SourceXmlPullParser sourceParser(input);
+    EXPECT_EQ(XmlPullParser::Event::kStartElement, sourceParser.next());
+    EXPECT_EQ(std::u16string(u"resources"), sourceParser.getElementName());
+
+    EXPECT_EQ(XmlPullParser::Event::kStartElement, sourceParser.next());
+    EXPECT_EQ(std::u16string(u"string"), sourceParser.getElementName());
+
+    {
+        ScopedXmlPullParser scopedParser(&sourceParser);
+        EXPECT_EQ(XmlPullParser::Event::kEndElement, scopedParser.next());
+        EXPECT_EQ(std::u16string(u"string"), sourceParser.getElementName());
+
+        EXPECT_EQ(XmlPullParser::Event::kEndDocument, scopedParser.next());
+    }
+
+    EXPECT_EQ(XmlPullParser::Event::kEndElement, sourceParser.next());
+    EXPECT_EQ(std::u16string(u"resources"), sourceParser.getElementName());
+
+    EXPECT_EQ(XmlPullParser::Event::kEndDocument, sourceParser.next());
+}
+
+TEST(ScopedXmlPullParserTest, FinishCurrentElementOnDestruction) {
+    std::stringstream input;
+    input << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl
+          << "<resources><string></string></resources>" << std::endl;
+
+    SourceXmlPullParser sourceParser(input);
+    EXPECT_EQ(XmlPullParser::Event::kStartElement, sourceParser.next());
+    EXPECT_EQ(std::u16string(u"resources"), sourceParser.getElementName());
+
+    EXPECT_EQ(XmlPullParser::Event::kStartElement, sourceParser.next());
+    EXPECT_EQ(std::u16string(u"string"), sourceParser.getElementName());
+
+    {
+        ScopedXmlPullParser scopedParser(&sourceParser);
+        EXPECT_EQ(std::u16string(u"string"), sourceParser.getElementName());
+    }
+
+    EXPECT_EQ(XmlPullParser::Event::kEndElement, sourceParser.next());
+    EXPECT_EQ(std::u16string(u"resources"), sourceParser.getElementName());
+
+    EXPECT_EQ(XmlPullParser::Event::kEndDocument, sourceParser.next());
+}
+
+TEST(ScopedXmlPullParserTest, NestedParsersOperateCorrectly) {
+    std::stringstream input;
+    input << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl
+          << "<resources><string><foo></foo></string></resources>" << std::endl;
+
+    SourceXmlPullParser sourceParser(input);
+    EXPECT_EQ(XmlPullParser::Event::kStartElement, sourceParser.next());
+    EXPECT_EQ(std::u16string(u"resources"), sourceParser.getElementName());
+
+    EXPECT_EQ(XmlPullParser::Event::kStartElement, sourceParser.next());
+    EXPECT_EQ(std::u16string(u"string"), sourceParser.getElementName());
+
+    {
+        ScopedXmlPullParser scopedParser(&sourceParser);
+        EXPECT_EQ(std::u16string(u"string"), scopedParser.getElementName());
+        while (XmlPullParser::isGoodEvent(scopedParser.next())) {
+            if (scopedParser.getEvent() != XmlPullParser::Event::kStartElement) {
+                continue;
+            }
+
+            ScopedXmlPullParser subScopedParser(&scopedParser);
+            EXPECT_EQ(std::u16string(u"foo"), subScopedParser.getElementName());
+        }
+    }
+
+    EXPECT_EQ(XmlPullParser::Event::kEndElement, sourceParser.next());
+    EXPECT_EQ(std::u16string(u"resources"), sourceParser.getElementName());
+
+    EXPECT_EQ(XmlPullParser::Event::kEndDocument, sourceParser.next());
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
new file mode 100644
index 0000000..3f156a6
--- /dev/null
+++ b/tools/aapt2/SdkConstants.cpp
@@ -0,0 +1,693 @@
+/*
+ * 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.
+ */
+
+#include <string>
+#include <unordered_map>
+
+namespace aapt {
+
+static const std::unordered_map<std::u16string, size_t> sAttrMap = {
+    { u"marqueeRepeatLimit", 2 },
+    { u"windowNoDisplay", 3 },
+    { u"backgroundDimEnabled", 3 },
+    { u"inputType", 3 },
+    { u"isDefault", 3 },
+    { u"windowDisablePreview", 3 },
+    { u"privateImeOptions", 3 },
+    { u"editorExtras", 3 },
+    { u"settingsActivity", 3 },
+    { u"fastScrollEnabled", 3 },
+    { u"reqTouchScreen", 3 },
+    { u"reqKeyboardType", 3 },
+    { u"reqHardKeyboard", 3 },
+    { u"reqNavigation", 3 },
+    { u"windowSoftInputMode", 3 },
+    { u"imeFullscreenBackground", 3 },
+    { u"noHistory", 3 },
+    { u"headerDividersEnabled", 3 },
+    { u"footerDividersEnabled", 3 },
+    { u"candidatesTextStyleSpans", 3 },
+    { u"smoothScrollbar", 3 },
+    { u"reqFiveWayNav", 3 },
+    { u"keyBackground", 3 },
+    { u"keyTextSize", 3 },
+    { u"labelTextSize", 3 },
+    { u"keyTextColor", 3 },
+    { u"keyPreviewLayout", 3 },
+    { u"keyPreviewOffset", 3 },
+    { u"keyPreviewHeight", 3 },
+    { u"verticalCorrection", 3 },
+    { u"popupLayout", 3 },
+    { u"state_long_pressable", 3 },
+    { u"keyWidth", 3 },
+    { u"keyHeight", 3 },
+    { u"horizontalGap", 3 },
+    { u"verticalGap", 3 },
+    { u"rowEdgeFlags", 3 },
+    { u"codes", 3 },
+    { u"popupKeyboard", 3 },
+    { u"popupCharacters", 3 },
+    { u"keyEdgeFlags", 3 },
+    { u"isModifier", 3 },
+    { u"isSticky", 3 },
+    { u"isRepeatable", 3 },
+    { u"iconPreview", 3 },
+    { u"keyOutputText", 3 },
+    { u"keyLabel", 3 },
+    { u"keyIcon", 3 },
+    { u"keyboardMode", 3 },
+    { u"isScrollContainer", 3 },
+    { u"fillEnabled", 3 },
+    { u"updatePeriodMillis", 3 },
+    { u"initialLayout", 3 },
+    { u"voiceSearchMode", 3 },
+    { u"voiceLanguageModel", 3 },
+    { u"voicePromptText", 3 },
+    { u"voiceLanguage", 3 },
+    { u"voiceMaxResults", 3 },
+    { u"bottomOffset", 3 },
+    { u"topOffset", 3 },
+    { u"allowSingleTap", 3 },
+    { u"handle", 3 },
+    { u"content", 3 },
+    { u"animateOnClick", 3 },
+    { u"configure", 3 },
+    { u"hapticFeedbackEnabled", 3 },
+    { u"innerRadius", 3 },
+    { u"thickness", 3 },
+    { u"sharedUserLabel", 3 },
+    { u"dropDownWidth", 3 },
+    { u"dropDownAnchor", 3 },
+    { u"imeOptions", 3 },
+    { u"imeActionLabel", 3 },
+    { u"imeActionId", 3 },
+    { u"imeExtractEnterAnimation", 3 },
+    { u"imeExtractExitAnimation", 3 },
+    { u"tension", 4 },
+    { u"extraTension", 4 },
+    { u"anyDensity", 4 },
+    { u"searchSuggestThreshold", 4 },
+    { u"includeInGlobalSearch", 4 },
+    { u"onClick", 4 },
+    { u"targetSdkVersion", 4 },
+    { u"maxSdkVersion", 4 },
+    { u"testOnly", 4 },
+    { u"contentDescription", 4 },
+    { u"gestureStrokeWidth", 4 },
+    { u"gestureColor", 4 },
+    { u"uncertainGestureColor", 4 },
+    { u"fadeOffset", 4 },
+    { u"fadeDuration", 4 },
+    { u"gestureStrokeType", 4 },
+    { u"gestureStrokeLengthThreshold", 4 },
+    { u"gestureStrokeSquarenessThreshold", 4 },
+    { u"gestureStrokeAngleThreshold", 4 },
+    { u"eventsInterceptionEnabled", 4 },
+    { u"fadeEnabled", 4 },
+    { u"backupAgent", 4 },
+    { u"allowBackup", 4 },
+    { u"glEsVersion", 4 },
+    { u"queryAfterZeroResults", 4 },
+    { u"dropDownHeight", 4 },
+    { u"smallScreens", 4 },
+    { u"normalScreens", 4 },
+    { u"largeScreens", 4 },
+    { u"progressBarStyleInverse", 4 },
+    { u"progressBarStyleSmallInverse", 4 },
+    { u"progressBarStyleLargeInverse", 4 },
+    { u"searchSettingsDescription", 4 },
+    { u"textColorPrimaryInverseDisableOnly", 4 },
+    { u"autoUrlDetect", 4 },
+    { u"resizeable", 4 },
+    { u"required", 5 },
+    { u"accountType", 5 },
+    { u"contentAuthority", 5 },
+    { u"userVisible", 5 },
+    { u"windowShowWallpaper", 5 },
+    { u"wallpaperOpenEnterAnimation", 5 },
+    { u"wallpaperOpenExitAnimation", 5 },
+    { u"wallpaperCloseEnterAnimation", 5 },
+    { u"wallpaperCloseExitAnimation", 5 },
+    { u"wallpaperIntraOpenEnterAnimation", 5 },
+    { u"wallpaperIntraOpenExitAnimation", 5 },
+    { u"wallpaperIntraCloseEnterAnimation", 5 },
+    { u"wallpaperIntraCloseExitAnimation", 5 },
+    { u"supportsUploading", 5 },
+    { u"killAfterRestore", 5 },
+    { u"restoreNeedsApplication", 5 },
+    { u"smallIcon", 5 },
+    { u"accountPreferences", 5 },
+    { u"textAppearanceSearchResultSubtitle", 5 },
+    { u"textAppearanceSearchResultTitle", 5 },
+    { u"summaryColumn", 5 },
+    { u"detailColumn", 5 },
+    { u"detailSocialSummary", 5 },
+    { u"thumbnail", 5 },
+    { u"detachWallpaper", 5 },
+    { u"finishOnCloseSystemDialogs", 5 },
+    { u"scrollbarFadeDuration", 5 },
+    { u"scrollbarDefaultDelayBeforeFade", 5 },
+    { u"fadeScrollbars", 5 },
+    { u"colorBackgroundCacheHint", 5 },
+    { u"dropDownHorizontalOffset", 5 },
+    { u"dropDownVerticalOffset", 5 },
+    { u"quickContactBadgeStyleWindowSmall", 6 },
+    { u"quickContactBadgeStyleWindowMedium", 6 },
+    { u"quickContactBadgeStyleWindowLarge", 6 },
+    { u"quickContactBadgeStyleSmallWindowSmall", 6 },
+    { u"quickContactBadgeStyleSmallWindowMedium", 6 },
+    { u"quickContactBadgeStyleSmallWindowLarge", 6 },
+    { u"author", 7 },
+    { u"autoStart", 7 },
+    { u"expandableListViewWhiteStyle", 8 },
+    { u"installLocation", 8 },
+    { u"vmSafeMode", 8 },
+    { u"webTextViewStyle", 8 },
+    { u"restoreAnyVersion", 8 },
+    { u"tabStripLeft", 8 },
+    { u"tabStripRight", 8 },
+    { u"tabStripEnabled", 8 },
+    { u"logo", 9 },
+    { u"xlargeScreens", 9 },
+    { u"immersive", 9 },
+    { u"overScrollMode", 9 },
+    { u"overScrollHeader", 9 },
+    { u"overScrollFooter", 9 },
+    { u"filterTouchesWhenObscured", 9 },
+    { u"textSelectHandleLeft", 9 },
+    { u"textSelectHandleRight", 9 },
+    { u"textSelectHandle", 9 },
+    { u"textSelectHandleWindowStyle", 9 },
+    { u"popupAnimationStyle", 9 },
+    { u"screenSize", 9 },
+    { u"screenDensity", 9 },
+    { u"allContactsName", 11 },
+    { u"windowActionBar", 11 },
+    { u"actionBarStyle", 11 },
+    { u"navigationMode", 11 },
+    { u"displayOptions", 11 },
+    { u"subtitle", 11 },
+    { u"customNavigationLayout", 11 },
+    { u"hardwareAccelerated", 11 },
+    { u"measureWithLargestChild", 11 },
+    { u"animateFirstView", 11 },
+    { u"dropDownSpinnerStyle", 11 },
+    { u"actionDropDownStyle", 11 },
+    { u"actionButtonStyle", 11 },
+    { u"showAsAction", 11 },
+    { u"previewImage", 11 },
+    { u"actionModeBackground", 11 },
+    { u"actionModeCloseDrawable", 11 },
+    { u"windowActionModeOverlay", 11 },
+    { u"valueFrom", 11 },
+    { u"valueTo", 11 },
+    { u"valueType", 11 },
+    { u"propertyName", 11 },
+    { u"ordering", 11 },
+    { u"fragment", 11 },
+    { u"windowActionBarOverlay", 11 },
+    { u"fragmentOpenEnterAnimation", 11 },
+    { u"fragmentOpenExitAnimation", 11 },
+    { u"fragmentCloseEnterAnimation", 11 },
+    { u"fragmentCloseExitAnimation", 11 },
+    { u"fragmentFadeEnterAnimation", 11 },
+    { u"fragmentFadeExitAnimation", 11 },
+    { u"actionBarSize", 11 },
+    { u"imeSubtypeLocale", 11 },
+    { u"imeSubtypeMode", 11 },
+    { u"imeSubtypeExtraValue", 11 },
+    { u"splitMotionEvents", 11 },
+    { u"listChoiceBackgroundIndicator", 11 },
+    { u"spinnerMode", 11 },
+    { u"animateLayoutChanges", 11 },
+    { u"actionBarTabStyle", 11 },
+    { u"actionBarTabBarStyle", 11 },
+    { u"actionBarTabTextStyle", 11 },
+    { u"actionOverflowButtonStyle", 11 },
+    { u"actionModeCloseButtonStyle", 11 },
+    { u"titleTextStyle", 11 },
+    { u"subtitleTextStyle", 11 },
+    { u"iconifiedByDefault", 11 },
+    { u"actionLayout", 11 },
+    { u"actionViewClass", 11 },
+    { u"activatedBackgroundIndicator", 11 },
+    { u"state_activated", 11 },
+    { u"listPopupWindowStyle", 11 },
+    { u"popupMenuStyle", 11 },
+    { u"textAppearanceLargePopupMenu", 11 },
+    { u"textAppearanceSmallPopupMenu", 11 },
+    { u"breadCrumbTitle", 11 },
+    { u"breadCrumbShortTitle", 11 },
+    { u"listDividerAlertDialog", 11 },
+    { u"textColorAlertDialogListItem", 11 },
+    { u"loopViews", 11 },
+    { u"dialogTheme", 11 },
+    { u"alertDialogTheme", 11 },
+    { u"dividerVertical", 11 },
+    { u"homeAsUpIndicator", 11 },
+    { u"enterFadeDuration", 11 },
+    { u"exitFadeDuration", 11 },
+    { u"selectableItemBackground", 11 },
+    { u"autoAdvanceViewId", 11 },
+    { u"useIntrinsicSizeAsMinimum", 11 },
+    { u"actionModeCutDrawable", 11 },
+    { u"actionModeCopyDrawable", 11 },
+    { u"actionModePasteDrawable", 11 },
+    { u"textEditPasteWindowLayout", 11 },
+    { u"textEditNoPasteWindowLayout", 11 },
+    { u"textIsSelectable", 11 },
+    { u"windowEnableSplitTouch", 11 },
+    { u"indeterminateProgressStyle", 11 },
+    { u"progressBarPadding", 11 },
+    { u"animationResolution", 11 },
+    { u"state_accelerated", 11 },
+    { u"baseline", 11 },
+    { u"homeLayout", 11 },
+    { u"opacity", 11 },
+    { u"alpha", 11 },
+    { u"transformPivotX", 11 },
+    { u"transformPivotY", 11 },
+    { u"translationX", 11 },
+    { u"translationY", 11 },
+    { u"scaleX", 11 },
+    { u"scaleY", 11 },
+    { u"rotation", 11 },
+    { u"rotationX", 11 },
+    { u"rotationY", 11 },
+    { u"showDividers", 11 },
+    { u"dividerPadding", 11 },
+    { u"borderlessButtonStyle", 11 },
+    { u"dividerHorizontal", 11 },
+    { u"itemPadding", 11 },
+    { u"buttonBarStyle", 11 },
+    { u"buttonBarButtonStyle", 11 },
+    { u"segmentedButtonStyle", 11 },
+    { u"staticWallpaperPreview", 11 },
+    { u"allowParallelSyncs", 11 },
+    { u"isAlwaysSyncable", 11 },
+    { u"verticalScrollbarPosition", 11 },
+    { u"fastScrollAlwaysVisible", 11 },
+    { u"fastScrollThumbDrawable", 11 },
+    { u"fastScrollPreviewBackgroundLeft", 11 },
+    { u"fastScrollPreviewBackgroundRight", 11 },
+    { u"fastScrollTrackDrawable", 11 },
+    { u"fastScrollOverlayPosition", 11 },
+    { u"customTokens", 11 },
+    { u"nextFocusForward", 11 },
+    { u"firstDayOfWeek", 11 },
+    { u"showWeekNumber", 11 },
+    { u"minDate", 11 },
+    { u"maxDate", 11 },
+    { u"shownWeekCount", 11 },
+    { u"selectedWeekBackgroundColor", 11 },
+    { u"focusedMonthDateColor", 11 },
+    { u"unfocusedMonthDateColor", 11 },
+    { u"weekNumberColor", 11 },
+    { u"weekSeparatorLineColor", 11 },
+    { u"selectedDateVerticalBar", 11 },
+    { u"weekDayTextAppearance", 11 },
+    { u"dateTextAppearance", 11 },
+    { u"solidColor", 11 },
+    { u"spinnersShown", 11 },
+    { u"calendarViewShown", 11 },
+    { u"state_multiline", 11 },
+    { u"detailsElementBackground", 11 },
+    { u"textColorHighlightInverse", 11 },
+    { u"textColorLinkInverse", 11 },
+    { u"editTextColor", 11 },
+    { u"editTextBackground", 11 },
+    { u"horizontalScrollViewStyle", 11 },
+    { u"layerType", 11 },
+    { u"alertDialogIcon", 11 },
+    { u"windowMinWidthMajor", 11 },
+    { u"windowMinWidthMinor", 11 },
+    { u"queryHint", 11 },
+    { u"fastScrollTextColor", 11 },
+    { u"largeHeap", 11 },
+    { u"windowCloseOnTouchOutside", 11 },
+    { u"datePickerStyle", 11 },
+    { u"calendarViewStyle", 11 },
+    { u"textEditSidePasteWindowLayout", 11 },
+    { u"textEditSideNoPasteWindowLayout", 11 },
+    { u"actionMenuTextAppearance", 11 },
+    { u"actionMenuTextColor", 11 },
+    { u"textCursorDrawable", 12 },
+    { u"resizeMode", 12 },
+    { u"requiresSmallestWidthDp", 12 },
+    { u"compatibleWidthLimitDp", 12 },
+    { u"largestWidthLimitDp", 12 },
+    { u"state_hovered", 13 },
+    { u"state_drag_can_accept", 13 },
+    { u"state_drag_hovered", 13 },
+    { u"stopWithTask", 13 },
+    { u"switchTextOn", 13 },
+    { u"switchTextOff", 13 },
+    { u"switchPreferenceStyle", 13 },
+    { u"switchTextAppearance", 13 },
+    { u"track", 13 },
+    { u"switchMinWidth", 13 },
+    { u"switchPadding", 13 },
+    { u"thumbTextPadding", 13 },
+    { u"textSuggestionsWindowStyle", 13 },
+    { u"textEditSuggestionItemLayout", 13 },
+    { u"rowCount", 13 },
+    { u"rowOrderPreserved", 13 },
+    { u"columnCount", 13 },
+    { u"columnOrderPreserved", 13 },
+    { u"useDefaultMargins", 13 },
+    { u"alignmentMode", 13 },
+    { u"layout_row", 13 },
+    { u"layout_rowSpan", 13 },
+    { u"layout_columnSpan", 13 },
+    { u"actionModeSelectAllDrawable", 13 },
+    { u"isAuxiliary", 13 },
+    { u"accessibilityEventTypes", 13 },
+    { u"packageNames", 13 },
+    { u"accessibilityFeedbackType", 13 },
+    { u"notificationTimeout", 13 },
+    { u"accessibilityFlags", 13 },
+    { u"canRetrieveWindowContent", 13 },
+    { u"listPreferredItemHeightLarge", 13 },
+    { u"listPreferredItemHeightSmall", 13 },
+    { u"actionBarSplitStyle", 13 },
+    { u"actionProviderClass", 13 },
+    { u"backgroundStacked", 13 },
+    { u"backgroundSplit", 13 },
+    { u"textAllCaps", 13 },
+    { u"colorPressedHighlight", 13 },
+    { u"colorLongPressedHighlight", 13 },
+    { u"colorFocusedHighlight", 13 },
+    { u"colorActivatedHighlight", 13 },
+    { u"colorMultiSelectHighlight", 13 },
+    { u"drawableStart", 13 },
+    { u"drawableEnd", 13 },
+    { u"actionModeStyle", 13 },
+    { u"minResizeWidth", 13 },
+    { u"minResizeHeight", 13 },
+    { u"actionBarWidgetTheme", 13 },
+    { u"uiOptions", 13 },
+    { u"subtypeLocale", 13 },
+    { u"subtypeExtraValue", 13 },
+    { u"actionBarDivider", 13 },
+    { u"actionBarItemBackground", 13 },
+    { u"actionModeSplitBackground", 13 },
+    { u"textAppearanceListItem", 13 },
+    { u"textAppearanceListItemSmall", 13 },
+    { u"targetDescriptions", 13 },
+    { u"directionDescriptions", 13 },
+    { u"overridesImplicitlyEnabledSubtype", 13 },
+    { u"listPreferredItemPaddingLeft", 13 },
+    { u"listPreferredItemPaddingRight", 13 },
+    { u"requiresFadingEdge", 13 },
+    { u"publicKey", 13 },
+    { u"parentActivityName", 16 },
+    { u"isolatedProcess", 16 },
+    { u"importantForAccessibility", 16 },
+    { u"keyboardLayout", 16 },
+    { u"fontFamily", 16 },
+    { u"mediaRouteButtonStyle", 16 },
+    { u"mediaRouteTypes", 16 },
+    { u"supportsRtl", 17 },
+    { u"textDirection", 17 },
+    { u"textAlignment", 17 },
+    { u"layoutDirection", 17 },
+    { u"paddingStart", 17 },
+    { u"paddingEnd", 17 },
+    { u"layout_marginStart", 17 },
+    { u"layout_marginEnd", 17 },
+    { u"layout_toStartOf", 17 },
+    { u"layout_toEndOf", 17 },
+    { u"layout_alignStart", 17 },
+    { u"layout_alignEnd", 17 },
+    { u"layout_alignParentStart", 17 },
+    { u"layout_alignParentEnd", 17 },
+    { u"listPreferredItemPaddingStart", 17 },
+    { u"listPreferredItemPaddingEnd", 17 },
+    { u"singleUser", 17 },
+    { u"presentationTheme", 17 },
+    { u"subtypeId", 17 },
+    { u"initialKeyguardLayout", 17 },
+    { u"widgetCategory", 17 },
+    { u"permissionGroupFlags", 17 },
+    { u"labelFor", 17 },
+    { u"permissionFlags", 17 },
+    { u"checkedTextViewStyle", 17 },
+    { u"showOnLockScreen", 17 },
+    { u"format12Hour", 17 },
+    { u"format24Hour", 17 },
+    { u"timeZone", 17 },
+    { u"mipMap", 18 },
+    { u"mirrorForRtl", 18 },
+    { u"windowOverscan", 18 },
+    { u"requiredForAllUsers", 18 },
+    { u"indicatorStart", 18 },
+    { u"indicatorEnd", 18 },
+    { u"childIndicatorStart", 18 },
+    { u"childIndicatorEnd", 18 },
+    { u"restrictedAccountType", 18 },
+    { u"requiredAccountType", 18 },
+    { u"canRequestTouchExplorationMode", 18 },
+    { u"canRequestEnhancedWebAccessibility", 18 },
+    { u"canRequestFilterKeyEvents", 18 },
+    { u"layoutMode", 18 },
+    { u"keySet", 19 },
+    { u"targetId", 19 },
+    { u"fromScene", 19 },
+    { u"toScene", 19 },
+    { u"transition", 19 },
+    { u"transitionOrdering", 19 },
+    { u"fadingMode", 19 },
+    { u"startDelay", 19 },
+    { u"ssp", 19 },
+    { u"sspPrefix", 19 },
+    { u"sspPattern", 19 },
+    { u"addPrintersActivity", 19 },
+    { u"vendor", 19 },
+    { u"category", 19 },
+    { u"isAsciiCapable", 19 },
+    { u"autoMirrored", 19 },
+    { u"supportsSwitchingToNextInputMethod", 19 },
+    { u"requireDeviceUnlock", 19 },
+    { u"apduServiceBanner", 19 },
+    { u"accessibilityLiveRegion", 19 },
+    { u"windowTranslucentStatus", 19 },
+    { u"windowTranslucentNavigation", 19 },
+    { u"advancedPrintOptionsActivity", 19 },
+    { u"banner", 20 },
+    { u"windowSwipeToDismiss", 20 },
+    { u"isGame", 20 },
+    { u"allowEmbedded", 20 },
+    { u"setupActivity", 20 },
+    { u"fastScrollStyle", 21 },
+    { u"windowContentTransitions", 21 },
+    { u"windowContentTransitionManager", 21 },
+    { u"translationZ", 21 },
+    { u"tintMode", 21 },
+    { u"controlX1", 21 },
+    { u"controlY1", 21 },
+    { u"controlX2", 21 },
+    { u"controlY2", 21 },
+    { u"transitionName", 21 },
+    { u"transitionGroup", 21 },
+    { u"viewportWidth", 21 },
+    { u"viewportHeight", 21 },
+    { u"fillColor", 21 },
+    { u"pathData", 21 },
+    { u"strokeColor", 21 },
+    { u"strokeWidth", 21 },
+    { u"trimPathStart", 21 },
+    { u"trimPathEnd", 21 },
+    { u"trimPathOffset", 21 },
+    { u"strokeLineCap", 21 },
+    { u"strokeLineJoin", 21 },
+    { u"strokeMiterLimit", 21 },
+    { u"colorControlNormal", 21 },
+    { u"colorControlActivated", 21 },
+    { u"colorButtonNormal", 21 },
+    { u"colorControlHighlight", 21 },
+    { u"persistableMode", 21 },
+    { u"titleTextAppearance", 21 },
+    { u"subtitleTextAppearance", 21 },
+    { u"slideEdge", 21 },
+    { u"actionBarTheme", 21 },
+    { u"textAppearanceListItemSecondary", 21 },
+    { u"colorPrimary", 21 },
+    { u"colorPrimaryDark", 21 },
+    { u"colorAccent", 21 },
+    { u"nestedScrollingEnabled", 21 },
+    { u"windowEnterTransition", 21 },
+    { u"windowExitTransition", 21 },
+    { u"windowSharedElementEnterTransition", 21 },
+    { u"windowSharedElementExitTransition", 21 },
+    { u"windowAllowReturnTransitionOverlap", 21 },
+    { u"windowAllowEnterTransitionOverlap", 21 },
+    { u"sessionService", 21 },
+    { u"stackViewStyle", 21 },
+    { u"switchStyle", 21 },
+    { u"elevation", 21 },
+    { u"excludeId", 21 },
+    { u"excludeClass", 21 },
+    { u"hideOnContentScroll", 21 },
+    { u"actionOverflowMenuStyle", 21 },
+    { u"documentLaunchMode", 21 },
+    { u"maxRecents", 21 },
+    { u"autoRemoveFromRecents", 21 },
+    { u"stateListAnimator", 21 },
+    { u"toId", 21 },
+    { u"fromId", 21 },
+    { u"reversible", 21 },
+    { u"splitTrack", 21 },
+    { u"targetName", 21 },
+    { u"excludeName", 21 },
+    { u"matchOrder", 21 },
+    { u"windowDrawsSystemBarBackgrounds", 21 },
+    { u"statusBarColor", 21 },
+    { u"navigationBarColor", 21 },
+    { u"contentInsetStart", 21 },
+    { u"contentInsetEnd", 21 },
+    { u"contentInsetLeft", 21 },
+    { u"contentInsetRight", 21 },
+    { u"paddingMode", 21 },
+    { u"layout_rowWeight", 21 },
+    { u"layout_columnWeight", 21 },
+    { u"translateX", 21 },
+    { u"translateY", 21 },
+    { u"selectableItemBackgroundBorderless", 21 },
+    { u"elegantTextHeight", 21 },
+    { u"searchKeyphraseId", 21 },
+    { u"searchKeyphrase", 21 },
+    { u"searchKeyphraseSupportedLocales", 21 },
+    { u"windowTransitionBackgroundFadeDuration", 21 },
+    { u"overlapAnchor", 21 },
+    { u"progressTint", 21 },
+    { u"progressTintMode", 21 },
+    { u"progressBackgroundTint", 21 },
+    { u"progressBackgroundTintMode", 21 },
+    { u"secondaryProgressTint", 21 },
+    { u"secondaryProgressTintMode", 21 },
+    { u"indeterminateTint", 21 },
+    { u"indeterminateTintMode", 21 },
+    { u"backgroundTint", 21 },
+    { u"backgroundTintMode", 21 },
+    { u"foregroundTint", 21 },
+    { u"foregroundTintMode", 21 },
+    { u"buttonTint", 21 },
+    { u"buttonTintMode", 21 },
+    { u"thumbTint", 21 },
+    { u"thumbTintMode", 21 },
+    { u"fullBackupOnly", 21 },
+    { u"propertyXName", 21 },
+    { u"propertyYName", 21 },
+    { u"relinquishTaskIdentity", 21 },
+    { u"tileModeX", 21 },
+    { u"tileModeY", 21 },
+    { u"actionModeShareDrawable", 21 },
+    { u"actionModeFindDrawable", 21 },
+    { u"actionModeWebSearchDrawable", 21 },
+    { u"transitionVisibilityMode", 21 },
+    { u"minimumHorizontalAngle", 21 },
+    { u"minimumVerticalAngle", 21 },
+    { u"maximumAngle", 21 },
+    { u"searchViewStyle", 21 },
+    { u"closeIcon", 21 },
+    { u"goIcon", 21 },
+    { u"searchIcon", 21 },
+    { u"voiceIcon", 21 },
+    { u"commitIcon", 21 },
+    { u"suggestionRowLayout", 21 },
+    { u"queryBackground", 21 },
+    { u"submitBackground", 21 },
+    { u"buttonBarPositiveButtonStyle", 21 },
+    { u"buttonBarNeutralButtonStyle", 21 },
+    { u"buttonBarNegativeButtonStyle", 21 },
+    { u"popupElevation", 21 },
+    { u"actionBarPopupTheme", 21 },
+    { u"multiArch", 21 },
+    { u"touchscreenBlocksFocus", 21 },
+    { u"windowElevation", 21 },
+    { u"launchTaskBehindTargetAnimation", 21 },
+    { u"launchTaskBehindSourceAnimation", 21 },
+    { u"restrictionType", 21 },
+    { u"dayOfWeekBackground", 21 },
+    { u"dayOfWeekTextAppearance", 21 },
+    { u"headerMonthTextAppearance", 21 },
+    { u"headerDayOfMonthTextAppearance", 21 },
+    { u"headerYearTextAppearance", 21 },
+    { u"yearListItemTextAppearance", 21 },
+    { u"yearListSelectorColor", 21 },
+    { u"calendarTextColor", 21 },
+    { u"recognitionService", 21 },
+    { u"timePickerStyle", 21 },
+    { u"timePickerDialogTheme", 21 },
+    { u"headerTimeTextAppearance", 21 },
+    { u"headerAmPmTextAppearance", 21 },
+    { u"numbersTextColor", 21 },
+    { u"numbersBackgroundColor", 21 },
+    { u"numbersSelectorColor", 21 },
+    { u"amPmTextColor", 21 },
+    { u"amPmBackgroundColor", 21 },
+    { u"searchKeyphraseRecognitionFlags", 21 },
+    { u"checkMarkTint", 21 },
+    { u"checkMarkTintMode", 21 },
+    { u"popupTheme", 21 },
+    { u"toolbarStyle", 21 },
+    { u"windowClipToOutline", 21 },
+    { u"datePickerDialogTheme", 21 },
+    { u"showText", 21 },
+    { u"windowReturnTransition", 21 },
+    { u"windowReenterTransition", 21 },
+    { u"windowSharedElementReturnTransition", 21 },
+    { u"windowSharedElementReenterTransition", 21 },
+    { u"resumeWhilePausing", 21 },
+    { u"datePickerMode", 21 },
+    { u"timePickerMode", 21 },
+    { u"inset", 21 },
+    { u"letterSpacing", 21 },
+    { u"fontFeatureSettings", 21 },
+    { u"outlineProvider", 21 },
+    { u"contentAgeHint", 21 },
+    { u"country", 21 },
+    { u"windowSharedElementsUseOverlay", 21 },
+    { u"reparent", 21 },
+    { u"reparentWithOverlay", 21 },
+    { u"ambientShadowAlpha", 21 },
+    { u"spotShadowAlpha", 21 },
+    { u"navigationIcon", 21 },
+    { u"navigationContentDescription", 21 },
+    { u"fragmentExitTransition", 21 },
+    { u"fragmentEnterTransition", 21 },
+    { u"fragmentSharedElementEnterTransition", 21 },
+    { u"fragmentReturnTransition", 21 },
+    { u"fragmentSharedElementReturnTransition", 21 },
+    { u"fragmentReenterTransition", 21 },
+    { u"fragmentAllowEnterTransitionOverlap", 21 },
+    { u"fragmentAllowReturnTransitionOverlap", 21 },
+    { u"patternPathData", 21 },
+    { u"strokeAlpha", 21 },
+    { u"fillAlpha", 21 },
+    { u"windowActivityTransitions", 21 },
+    { u"colorEdgeEffect", 21 }
+};
+
+size_t findAttributeSdkLevel(const std::u16string& name) {
+    auto iter = sAttrMap.find(name);
+    if (iter != sAttrMap.end()) {
+        return iter->second;
+    }
+    return 0;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
new file mode 100644
index 0000000..469c355
--- /dev/null
+++ b/tools/aapt2/SdkConstants.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_SDK_CONSTANTS_H
+#define AAPT_SDK_CONSTANTS_H
+
+#include "Resource.h"
+
+#include <string>
+
+namespace aapt {
+
+enum {
+    SDK_CUPCAKE = 3,
+    SDK_DONUT = 4,
+    SDK_ECLAIR = 5,
+    SDK_ECLAIR_0_1 = 6,
+    SDK_ECLAIR_MR1 = 7,
+    SDK_FROYO = 8,
+    SDK_GINGERBREAD = 9,
+    SDK_GINGERBREAD_MR1 = 10,
+    SDK_HONEYCOMB = 11,
+    SDK_HONEYCOMB_MR1 = 12,
+    SDK_HONEYCOMB_MR2 = 13,
+    SDK_ICE_CREAM_SANDWICH = 14,
+    SDK_ICE_CREAM_SANDWICH_MR1 = 15,
+    SDK_JELLY_BEAN = 16,
+    SDK_JELLY_BEAN_MR1 = 17,
+    SDK_JELLY_BEAN_MR2 = 18,
+    SDK_KITKAT = 19,
+    SDK_KITKAT_WATCH = 20,
+    SDK_LOLLIPOP = 21,
+    SDK_LOLLIPOP_MR1 = 22,
+};
+
+size_t findAttributeSdkLevel(const std::u16string& name);
+
+} // namespace aapt
+
+#endif // AAPT_SDK_CONSTANTS_H
diff --git a/tools/aapt2/Source.h b/tools/aapt2/Source.h
new file mode 100644
index 0000000..10c75aa
--- /dev/null
+++ b/tools/aapt2/Source.h
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_SOURCE_H
+#define AAPT_SOURCE_H
+
+#include <ostream>
+#include <string>
+
+namespace aapt {
+
+struct SourceLineColumn;
+struct SourceLine;
+
+/**
+ * Represents a file on disk. Used for logging and
+ * showing errors.
+ */
+struct Source {
+    std::string path;
+
+    inline SourceLine line(size_t line) const;
+};
+
+/**
+ * Represents a file on disk and a line number in that file.
+ * Used for logging and showing errors.
+ */
+struct SourceLine {
+    std::string path;
+    size_t line;
+
+    inline SourceLineColumn column(size_t column) const;
+};
+
+/**
+ * Represents a file on disk and a line:column number in that file.
+ * Used for logging and showing errors.
+ */
+struct SourceLineColumn {
+    std::string path;
+    size_t line;
+    size_t column;
+};
+
+//
+// Implementations
+//
+
+SourceLine Source::line(size_t line) const {
+    return SourceLine{ path, line };
+}
+
+SourceLineColumn SourceLine::column(size_t column) const {
+    return SourceLineColumn{ path, line, column };
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out, const Source& source) {
+    return out << source.path;
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out, const SourceLine& source) {
+    return out << source.path << ":" << source.line;
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out, const SourceLineColumn& source) {
+    return out << source.path << ":" << source.line << ":" << source.column;
+}
+
+} // namespace aapt
+
+#endif // AAPT_SOURCE_H
diff --git a/tools/aapt2/SourceXmlPullParser.cpp b/tools/aapt2/SourceXmlPullParser.cpp
new file mode 100644
index 0000000..cb6a3c0
--- /dev/null
+++ b/tools/aapt2/SourceXmlPullParser.cpp
@@ -0,0 +1,248 @@
+/*
+ * 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.
+ */
+
+#include <iostream>
+#include <string>
+
+#include "SourceXmlPullParser.h"
+#include "Util.h"
+
+namespace aapt {
+
+constexpr char kXmlNamespaceSep = 1;
+
+SourceXmlPullParser::SourceXmlPullParser(std::istream& in) : mIn(in), mEmpty(), mDepth(0) {
+    mParser = XML_ParserCreateNS(nullptr, kXmlNamespaceSep);
+    XML_SetUserData(mParser, this);
+    XML_SetElementHandler(mParser, startElementHandler, endElementHandler);
+    XML_SetNamespaceDeclHandler(mParser, startNamespaceHandler, endNamespaceHandler);
+    XML_SetCharacterDataHandler(mParser, characterDataHandler);
+    XML_SetCommentHandler(mParser, commentDataHandler);
+    mEventQueue.push(EventData{ Event::kStartDocument, 0, mDepth++ });
+}
+
+SourceXmlPullParser::~SourceXmlPullParser() {
+    XML_ParserFree(mParser);
+}
+
+SourceXmlPullParser::Event SourceXmlPullParser::next() {
+    const Event currentEvent = getEvent();
+    if (currentEvent == Event::kBadDocument || currentEvent == Event::kEndDocument) {
+        return currentEvent;
+    }
+
+    mEventQueue.pop();
+    while (mEventQueue.empty()) {
+        mIn.read(mBuffer, sizeof(mBuffer) / sizeof(*mBuffer));
+
+        const bool done = mIn.eof();
+        if (mIn.bad() && !done) {
+            mLastError = strerror(errno);
+            mEventQueue.push(EventData{ Event::kBadDocument });
+            continue;
+        }
+
+        if (XML_Parse(mParser, mBuffer, mIn.gcount(), done) == XML_STATUS_ERROR) {
+            mLastError = XML_ErrorString(XML_GetErrorCode(mParser));
+            mEventQueue.push(EventData{ Event::kBadDocument });
+            continue;
+        }
+
+        if (done) {
+            mEventQueue.push(EventData{ Event::kEndDocument, 0, 0 });
+        }
+    }
+
+    return getEvent();
+}
+
+SourceXmlPullParser::Event SourceXmlPullParser::getEvent() const {
+    return mEventQueue.front().event;
+}
+
+const std::string& SourceXmlPullParser::getLastError() const {
+    return mLastError;
+}
+
+const std::u16string& SourceXmlPullParser::getComment() const {
+    return mEventQueue.front().comment;
+}
+
+size_t SourceXmlPullParser::getLineNumber() const {
+    return mEventQueue.front().lineNumber;
+}
+
+size_t SourceXmlPullParser::getDepth() const {
+    return mEventQueue.front().depth;
+}
+
+const std::u16string& SourceXmlPullParser::getText() const {
+    if (getEvent() != Event::kText) {
+        return mEmpty;
+    }
+    return mEventQueue.front().data1;
+}
+
+const std::u16string& SourceXmlPullParser::getNamespacePrefix() const {
+    const Event currentEvent = getEvent();
+    if (currentEvent != Event::kStartNamespace && currentEvent != Event::kEndNamespace) {
+        return mEmpty;
+    }
+    return mEventQueue.front().data1;
+}
+
+const std::u16string& SourceXmlPullParser::getNamespaceUri() const {
+    const Event currentEvent = getEvent();
+    if (currentEvent != Event::kStartNamespace && currentEvent != Event::kEndNamespace) {
+        return mEmpty;
+    }
+    return mEventQueue.front().data2;
+}
+
+const std::u16string& SourceXmlPullParser::getElementNamespace() const {
+    const Event currentEvent = getEvent();
+    if (currentEvent != Event::kStartElement && currentEvent != Event::kEndElement) {
+        return mEmpty;
+    }
+    return mEventQueue.front().data1;
+}
+
+const std::u16string& SourceXmlPullParser::getElementName() const {
+    const Event currentEvent = getEvent();
+    if (currentEvent != Event::kStartElement && currentEvent != Event::kEndElement) {
+        return mEmpty;
+    }
+    return mEventQueue.front().data2;
+}
+
+XmlPullParser::const_iterator SourceXmlPullParser::beginAttributes() const {
+    return mEventQueue.front().attributes.begin();
+}
+
+XmlPullParser::const_iterator SourceXmlPullParser::endAttributes() const {
+    return mEventQueue.front().attributes.end();
+}
+
+size_t SourceXmlPullParser::getAttributeCount() const {
+    if (getEvent() != Event::kStartElement) {
+        return 0;
+    }
+    return mEventQueue.front().attributes.size();
+}
+
+/**
+ * Extracts the namespace and name of an expanded element or attribute name.
+ */
+static void splitName(const char* name, std::u16string& outNs, std::u16string& outName) {
+    const char* p = name;
+    while (*p != 0 && *p != kXmlNamespaceSep) {
+        p++;
+    }
+
+    if (*p == 0) {
+        outNs = std::u16string();
+        outName = util::utf8ToUtf16(name);
+    } else {
+        outNs = util::utf8ToUtf16(StringPiece(name, (p - name)));
+        outName = util::utf8ToUtf16(p + 1);
+    }
+}
+
+void XMLCALL SourceXmlPullParser::startNamespaceHandler(void* userData, const char* prefix,
+        const char* uri) {
+    SourceXmlPullParser* parser = reinterpret_cast<SourceXmlPullParser*>(userData);
+    std::u16string namespaceUri = uri != nullptr ? util::utf8ToUtf16(uri) : std::u16string();
+    parser->mNamespaceUris.push(namespaceUri);
+    parser->mEventQueue.push(EventData{
+            Event::kStartNamespace,
+            XML_GetCurrentLineNumber(parser->mParser),
+            parser->mDepth++,
+            prefix != nullptr ? util::utf8ToUtf16(prefix) : std::u16string(),
+            namespaceUri
+    });
+}
+
+void XMLCALL SourceXmlPullParser::startElementHandler(void* userData, const char* name,
+        const char** attrs) {
+    SourceXmlPullParser* parser = reinterpret_cast<SourceXmlPullParser*>(userData);
+
+    EventData data = {
+            Event::kStartElement, XML_GetCurrentLineNumber(parser->mParser), parser->mDepth++
+    };
+    splitName(name, data.data1, data.data2);
+
+    while (*attrs) {
+        Attribute attribute;
+        splitName(*attrs++, attribute.namespaceUri, attribute.name);
+        attribute.value = util::utf8ToUtf16(*attrs++);
+
+        // Insert in sorted order.
+        auto iter = std::lower_bound(data.attributes.begin(), data.attributes.end(), attribute);
+        data.attributes.insert(iter, std::move(attribute));
+    }
+
+    // Move the structure into the queue (no copy).
+    parser->mEventQueue.push(std::move(data));
+}
+
+void XMLCALL SourceXmlPullParser::characterDataHandler(void* userData, const char* s, int len) {
+    SourceXmlPullParser* parser = reinterpret_cast<SourceXmlPullParser*>(userData);
+
+    parser->mEventQueue.push(EventData{
+            Event::kText,
+            XML_GetCurrentLineNumber(parser->mParser),
+            parser->mDepth,
+            util::utf8ToUtf16(StringPiece(s, len))
+    });
+}
+
+void XMLCALL SourceXmlPullParser::endElementHandler(void* userData, const char* name) {
+    SourceXmlPullParser* parser = reinterpret_cast<SourceXmlPullParser*>(userData);
+
+    EventData data = {
+            Event::kEndElement, XML_GetCurrentLineNumber(parser->mParser), --(parser->mDepth)
+    };
+    splitName(name, data.data1, data.data2);
+
+    // Move the data into the queue (no copy).
+    parser->mEventQueue.push(std::move(data));
+}
+
+void XMLCALL SourceXmlPullParser::endNamespaceHandler(void* userData, const char* prefix) {
+    SourceXmlPullParser* parser = reinterpret_cast<SourceXmlPullParser*>(userData);
+
+    parser->mEventQueue.push(EventData{
+            Event::kEndNamespace,
+            XML_GetCurrentLineNumber(parser->mParser),
+            --(parser->mDepth),
+            prefix != nullptr ? util::utf8ToUtf16(prefix) : std::u16string(),
+            parser->mNamespaceUris.top()
+    });
+    parser->mNamespaceUris.pop();
+}
+
+void XMLCALL SourceXmlPullParser::commentDataHandler(void* userData, const char* comment) {
+    SourceXmlPullParser* parser = reinterpret_cast<SourceXmlPullParser*>(userData);
+
+    parser->mEventQueue.push(EventData{
+            Event::kComment,
+            XML_GetCurrentLineNumber(parser->mParser),
+            parser->mDepth,
+            util::utf8ToUtf16(comment)
+    });
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/SourceXmlPullParser.h b/tools/aapt2/SourceXmlPullParser.h
new file mode 100644
index 0000000..ba904ba
--- /dev/null
+++ b/tools/aapt2/SourceXmlPullParser.h
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_SOURCE_XML_PULL_PARSER_H
+#define AAPT_SOURCE_XML_PULL_PARSER_H
+
+#include <istream>
+#include <libexpat/expat.h>
+#include <queue>
+#include <stack>
+#include <string>
+#include <vector>
+
+#include "XmlPullParser.h"
+
+namespace aapt {
+
+class SourceXmlPullParser : public XmlPullParser {
+public:
+    SourceXmlPullParser(std::istream& in);
+    SourceXmlPullParser(const SourceXmlPullParser& rhs) = delete;
+    ~SourceXmlPullParser();
+
+    Event getEvent() const;
+    const std::string& getLastError() const;
+    Event next();
+
+    const std::u16string& getComment() const;
+    size_t getLineNumber() const;
+    size_t getDepth() const;
+
+    const std::u16string& getText() const;
+
+    const std::u16string& getNamespacePrefix() const;
+    const std::u16string& getNamespaceUri() const;
+
+    const std::u16string& getElementNamespace() const;
+    const std::u16string& getElementName() const;
+
+    const_iterator beginAttributes() const;
+    const_iterator endAttributes() const;
+    size_t getAttributeCount() const;
+
+private:
+    static void XMLCALL startNamespaceHandler(void* userData, const char* prefix, const char* uri);
+    static void XMLCALL startElementHandler(void* userData, const char* name, const char** attrs);
+    static void XMLCALL characterDataHandler(void* userData, const char* s, int len);
+    static void XMLCALL endElementHandler(void* userData, const char* name);
+    static void XMLCALL endNamespaceHandler(void* userData, const char* prefix);
+    static void XMLCALL commentDataHandler(void* userData, const char* comment);
+
+    struct EventData {
+        Event event;
+        size_t lineNumber;
+        size_t depth;
+        std::u16string data1;
+        std::u16string data2;
+        std::u16string comment;
+        std::vector<Attribute> attributes;
+    };
+
+    std::istream& mIn;
+    XML_Parser mParser;
+    char mBuffer[16384];
+    std::queue<EventData> mEventQueue;
+    std::string mLastError;
+    const std::u16string mEmpty;
+    size_t mDepth;
+    std::stack<std::u16string> mNamespaceUris;
+};
+
+} // namespace aapt
+
+#endif // AAPT_SOURCE_XML_PULL_PARSER_H
diff --git a/tools/aapt2/StringPiece.h b/tools/aapt2/StringPiece.h
new file mode 100644
index 0000000..e2a1597
--- /dev/null
+++ b/tools/aapt2/StringPiece.h
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_STRING_PIECE_H
+#define AAPT_STRING_PIECE_H
+
+#include <ostream>
+#include <string>
+#include <utils/String8.h>
+#include <utils/Unicode.h>
+
+namespace aapt {
+
+/**
+ * Read only wrapper around basic C strings.
+ * Prevents excessive copying.
+ *
+ * WARNING: When creating from std::basic_string<>, moving the original
+ * std::basic_string<> will invalidate the data held in a BasicStringPiece<>.
+ * BasicStringPiece<> should only be used transitively.
+ */
+template <typename TChar>
+class BasicStringPiece {
+public:
+    using const_iterator = const TChar*;
+
+    BasicStringPiece();
+    BasicStringPiece(const BasicStringPiece<TChar>& str);
+    BasicStringPiece(const std::basic_string<TChar>& str);
+    BasicStringPiece(const TChar* str);
+    BasicStringPiece(const TChar* str, size_t len);
+
+    BasicStringPiece<TChar>& operator=(const BasicStringPiece<TChar>& rhs);
+    BasicStringPiece<TChar>& assign(const TChar* str, size_t len);
+
+    BasicStringPiece<TChar> substr(size_t start, size_t len) const;
+    BasicStringPiece<TChar> substr(BasicStringPiece<TChar>::const_iterator begin,
+                                   BasicStringPiece<TChar>::const_iterator end) const;
+
+    const TChar* data() const;
+    size_t length() const;
+    size_t size() const;
+    bool empty() const;
+    std::basic_string<TChar> toString() const;
+
+    int compare(const BasicStringPiece<TChar>& rhs) const;
+    bool operator<(const BasicStringPiece<TChar>& rhs) const;
+    bool operator>(const BasicStringPiece<TChar>& rhs) const;
+    bool operator==(const BasicStringPiece<TChar>& rhs) const;
+    bool operator!=(const BasicStringPiece<TChar>& rhs) const;
+
+    const_iterator begin() const;
+    const_iterator end() const;
+
+private:
+    const TChar* mData;
+    size_t mLength;
+};
+
+using StringPiece = BasicStringPiece<char>;
+using StringPiece16 = BasicStringPiece<char16_t>;
+
+//
+// BasicStringPiece implementation.
+//
+
+template <typename TChar>
+inline BasicStringPiece<TChar>::BasicStringPiece() : mData(nullptr) , mLength(0) {
+}
+
+template <typename TChar>
+inline BasicStringPiece<TChar>::BasicStringPiece(const BasicStringPiece<TChar>& str) :
+        mData(str.mData), mLength(str.mLength) {
+}
+
+template <typename TChar>
+inline BasicStringPiece<TChar>::BasicStringPiece(const std::basic_string<TChar>& str) :
+        mData(str.data()), mLength(str.length()) {
+}
+
+template <>
+inline BasicStringPiece<char>::BasicStringPiece(const char* str) :
+        mData(str), mLength(str != nullptr ? strlen(str) : 0) {
+}
+
+template <>
+inline BasicStringPiece<char16_t>::BasicStringPiece(const char16_t* str) :
+        mData(str), mLength(str != nullptr ? strlen16(str) : 0) {
+}
+
+template <typename TChar>
+inline BasicStringPiece<TChar>::BasicStringPiece(const TChar* str, size_t len) :
+        mData(str), mLength(len) {
+}
+
+template <typename TChar>
+inline BasicStringPiece<TChar>& BasicStringPiece<TChar>::operator=(
+        const BasicStringPiece<TChar>& rhs) {
+    mData = rhs.mData;
+    mLength = rhs.mLength;
+    return *this;
+}
+
+template <typename TChar>
+inline BasicStringPiece<TChar>& BasicStringPiece<TChar>::assign(const TChar* str, size_t len) {
+    mData = str;
+    mLength = len;
+    return *this;
+}
+
+
+template <typename TChar>
+inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(size_t start, size_t len) const {
+    if (start + len > mLength) {
+        return BasicStringPiece<TChar>();
+    }
+    return BasicStringPiece<TChar>(mData + start, len);
+}
+
+template <typename TChar>
+inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(
+        BasicStringPiece<TChar>::const_iterator begin,
+        BasicStringPiece<TChar>::const_iterator end) const {
+    return BasicStringPiece<TChar>(begin, end - begin);
+}
+
+template <typename TChar>
+inline const TChar* BasicStringPiece<TChar>::data() const {
+    return mData;
+}
+
+template <typename TChar>
+inline size_t BasicStringPiece<TChar>::length() const {
+    return mLength;
+}
+
+template <typename TChar>
+inline size_t BasicStringPiece<TChar>::size() const {
+    return mLength;
+}
+
+template <typename TChar>
+inline bool BasicStringPiece<TChar>::empty() const {
+    return mLength == 0;
+}
+
+template <typename TChar>
+inline std::basic_string<TChar> BasicStringPiece<TChar>::toString() const {
+    return std::basic_string<TChar>(mData, mLength);
+}
+
+template <>
+inline int BasicStringPiece<char>::compare(const BasicStringPiece<char>& rhs) const {
+    const char nullStr = '\0';
+    const char* b1 = mData != nullptr ? mData : &nullStr;
+    const char* e1 = b1 + mLength;
+    const char* b2 = rhs.mData != nullptr ? rhs.mData : &nullStr;
+    const char* e2 = b2 + rhs.mLength;
+
+    while (b1 < e1 && b2 < e2) {
+        const int d = static_cast<int>(*b1++) - static_cast<int>(*b2++);
+        if (d) {
+            return d;
+        }
+    }
+    return static_cast<int>(mLength - rhs.mLength);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out, const BasicStringPiece<char16_t>& str) {
+    android::String8 utf8(str.data(), str.size());
+    return out.write(utf8.string(), utf8.size());
+}
+
+
+template <>
+inline int BasicStringPiece<char16_t>::compare(const BasicStringPiece<char16_t>& rhs) const {
+    const char16_t nullStr = u'\0';
+    const char16_t* b1 = mData != nullptr ? mData : &nullStr;
+    const char16_t* b2 = rhs.mData != nullptr ? rhs.mData : &nullStr;
+    return strzcmp16(b1, mLength, b2, rhs.mLength);
+}
+
+template <typename TChar>
+inline bool BasicStringPiece<TChar>::operator<(const BasicStringPiece<TChar>& rhs) const {
+    return compare(rhs) < 0;
+}
+
+template <typename TChar>
+inline bool BasicStringPiece<TChar>::operator>(const BasicStringPiece<TChar>& rhs) const {
+    return compare(rhs) > 0;
+}
+
+template <typename TChar>
+inline bool BasicStringPiece<TChar>::operator==(const BasicStringPiece<TChar>& rhs) const {
+    return compare(rhs) == 0;
+}
+
+template <typename TChar>
+inline bool BasicStringPiece<TChar>::operator!=(const BasicStringPiece<TChar>& rhs) const {
+    return compare(rhs) != 0;
+}
+
+template <typename TChar>
+inline typename BasicStringPiece<TChar>::const_iterator BasicStringPiece<TChar>::begin() const {
+    return mData;
+}
+
+template <typename TChar>
+inline typename BasicStringPiece<TChar>::const_iterator BasicStringPiece<TChar>::end() const {
+    return mData + mLength;
+}
+
+inline ::std::ostream& operator<<(::std::ostream& out, const BasicStringPiece<char>& str) {
+    return out.write(str.data(), str.size());
+}
+
+} // namespace aapt
+
+#endif // AAPT_STRING_PIECE_H
diff --git a/tools/aapt2/StringPiece_test.cpp b/tools/aapt2/StringPiece_test.cpp
new file mode 100644
index 0000000..43f7a37
--- /dev/null
+++ b/tools/aapt2/StringPiece_test.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#include <algorithm>
+#include <gtest/gtest.h>
+#include <string>
+#include <vector>
+
+#include "StringPiece.h"
+
+namespace aapt {
+
+TEST(StringPieceTest, CompareNonNullTerminatedPiece) {
+    StringPiece a("hello world", 5);
+    StringPiece b("hello moon", 5);
+    EXPECT_EQ(a, b);
+
+    StringPiece16 a16(u"hello world", 5);
+    StringPiece16 b16(u"hello moon", 5);
+    EXPECT_EQ(a16, b16);
+}
+
+TEST(StringPieceTest, PiecesHaveCorrectSortOrder) {
+    std::u16string testing(u"testing");
+    std::u16string banana(u"banana");
+    std::u16string car(u"car");
+
+    EXPECT_TRUE(StringPiece16(testing) > banana);
+    EXPECT_TRUE(StringPiece16(testing) > car);
+    EXPECT_TRUE(StringPiece16(banana) < testing);
+    EXPECT_TRUE(StringPiece16(banana) < car);
+    EXPECT_TRUE(StringPiece16(car) < testing);
+    EXPECT_TRUE(StringPiece16(car) > banana);
+}
+
+TEST(StringPieceTest, PiecesHaveCorrectSortOrderUtf8) {
+    std::string testing("testing");
+    std::string banana("banana");
+    std::string car("car");
+
+    EXPECT_TRUE(StringPiece(testing) > banana);
+    EXPECT_TRUE(StringPiece(testing) > car);
+    EXPECT_TRUE(StringPiece(banana) < testing);
+    EXPECT_TRUE(StringPiece(banana) < car);
+    EXPECT_TRUE(StringPiece(car) < testing);
+    EXPECT_TRUE(StringPiece(car) > banana);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/StringPool.cpp b/tools/aapt2/StringPool.cpp
new file mode 100644
index 0000000..b159a00
--- /dev/null
+++ b/tools/aapt2/StringPool.cpp
@@ -0,0 +1,348 @@
+/*
+ * 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.
+ */
+
+#include "BigBuffer.h"
+#include "StringPiece.h"
+#include "StringPool.h"
+#include "Util.h"
+
+#include <algorithm>
+#include <androidfw/ResourceTypes.h>
+#include <memory>
+#include <string>
+
+namespace aapt {
+
+StringPool::Ref::Ref() : mEntry(nullptr) {
+}
+
+StringPool::Ref::Ref(const StringPool::Ref& rhs) : mEntry(rhs.mEntry) {
+    if (mEntry != nullptr) {
+        mEntry->ref++;
+    }
+}
+
+StringPool::Ref::Ref(StringPool::Entry* entry) : mEntry(entry) {
+    if (mEntry != nullptr) {
+        mEntry->ref++;
+    }
+}
+
+StringPool::Ref::~Ref() {
+    if (mEntry != nullptr) {
+        mEntry->ref--;
+    }
+}
+
+StringPool::Ref& StringPool::Ref::operator=(const StringPool::Ref& rhs) {
+    if (rhs.mEntry != nullptr) {
+        rhs.mEntry->ref++;
+    }
+
+    if (mEntry != nullptr) {
+        mEntry->ref--;
+    }
+    mEntry = rhs.mEntry;
+    return *this;
+}
+
+const std::u16string* StringPool::Ref::operator->() const {
+    return &mEntry->value;
+}
+
+const std::u16string& StringPool::Ref::operator*() const {
+    return mEntry->value;
+}
+
+size_t StringPool::Ref::getIndex() const {
+    return mEntry->index;
+}
+
+const StringPool::Context& StringPool::Ref::getContext() const {
+    return mEntry->context;
+}
+
+StringPool::StyleRef::StyleRef() : mEntry(nullptr) {
+}
+
+StringPool::StyleRef::StyleRef(const StringPool::StyleRef& rhs) : mEntry(rhs.mEntry) {
+    if (mEntry != nullptr) {
+        mEntry->ref++;
+    }
+}
+
+StringPool::StyleRef::StyleRef(StringPool::StyleEntry* entry) : mEntry(entry) {
+    if (mEntry != nullptr) {
+        mEntry->ref++;
+    }
+}
+
+StringPool::StyleRef::~StyleRef() {
+    if (mEntry != nullptr) {
+        mEntry->ref--;
+    }
+}
+
+StringPool::StyleRef& StringPool::StyleRef::operator=(const StringPool::StyleRef& rhs) {
+    if (rhs.mEntry != nullptr) {
+        rhs.mEntry->ref++;
+    }
+
+    if (mEntry != nullptr) {
+        mEntry->ref--;
+    }
+    mEntry = rhs.mEntry;
+    return *this;
+}
+
+const StringPool::StyleEntry* StringPool::StyleRef::operator->() const {
+    return mEntry;
+}
+
+const StringPool::StyleEntry& StringPool::StyleRef::operator*() const {
+    return *mEntry;
+}
+
+size_t StringPool::StyleRef::getIndex() const {
+    return mEntry->str.getIndex();
+}
+
+const StringPool::Context& StringPool::StyleRef::getContext() const {
+    return mEntry->str.getContext();
+}
+
+StringPool::Ref StringPool::makeRef(const StringPiece16& str) {
+    return makeRefImpl(str, Context{}, true);
+}
+
+StringPool::Ref StringPool::makeRef(const StringPiece16& str, const Context& context) {
+    return makeRefImpl(str, context, true);
+}
+
+StringPool::Ref StringPool::makeRefImpl(const StringPiece16& str, const Context& context,
+        bool unique) {
+    if (unique) {
+        auto iter = mIndexedStrings.find(str);
+        if (iter != std::end(mIndexedStrings)) {
+            return Ref(iter->second);
+        }
+    }
+
+    Entry* entry = new Entry();
+    entry->value = str.toString();
+    entry->context = context;
+    entry->index = mStrings.size();
+    entry->ref = 0;
+    mStrings.emplace_back(entry);
+    mIndexedStrings.insert(std::make_pair(StringPiece16(entry->value), entry));
+    return Ref(entry);
+}
+
+StringPool::StyleRef StringPool::makeRef(const StyleString& str) {
+    return makeRef(str, Context{});
+}
+
+StringPool::StyleRef StringPool::makeRef(const StyleString& str, const Context& context) {
+    Entry* entry = new Entry();
+    entry->value = str.str;
+    entry->context = context;
+    entry->index = mStrings.size();
+    entry->ref = 0;
+    mStrings.emplace_back(entry);
+    mIndexedStrings.insert(std::make_pair(StringPiece16(entry->value), entry));
+
+    StyleEntry* styleEntry = new StyleEntry();
+    styleEntry->str = Ref(entry);
+    for (const aapt::Span& span : str.spans) {
+        styleEntry->spans.emplace_back(Span{makeRef(span.name),
+                span.firstChar, span.lastChar});
+    }
+    styleEntry->ref = 0;
+    mStyles.emplace_back(styleEntry);
+    return StyleRef(styleEntry);
+}
+
+void StringPool::merge(StringPool&& pool) {
+    mIndexedStrings.insert(pool.mIndexedStrings.begin(), pool.mIndexedStrings.end());
+    pool.mIndexedStrings.clear();
+    std::move(pool.mStrings.begin(), pool.mStrings.end(), std::back_inserter(mStrings));
+    pool.mStrings.clear();
+    std::move(pool.mStyles.begin(), pool.mStyles.end(), std::back_inserter(mStyles));
+    pool.mStyles.clear();
+
+    // Assign the indices.
+    const size_t len = mStrings.size();
+    for (size_t index = 0; index < len; index++) {
+        mStrings[index]->index = index;
+    }
+}
+
+void StringPool::hintWillAdd(size_t stringCount, size_t styleCount) {
+    mStrings.reserve(mStrings.size() + stringCount);
+    mStyles.reserve(mStyles.size() + styleCount);
+}
+
+void StringPool::prune() {
+    const auto iterEnd = std::end(mIndexedStrings);
+    auto indexIter = std::begin(mIndexedStrings);
+    while (indexIter != iterEnd) {
+        if (indexIter->second->ref <= 0) {
+            mIndexedStrings.erase(indexIter++);
+        } else {
+            ++indexIter;
+        }
+    }
+
+    auto endIter2 = std::remove_if(std::begin(mStrings), std::end(mStrings),
+            [](const std::unique_ptr<Entry>& entry) -> bool {
+                return entry->ref <= 0;
+            }
+    );
+
+    auto endIter3 = std::remove_if(std::begin(mStyles), std::end(mStyles),
+            [](const std::unique_ptr<StyleEntry>& entry) -> bool {
+                return entry->ref <= 0;
+            }
+    );
+
+    // Remove the entries at the end or else we'll be accessing
+    // a deleted string from the StyleEntry.
+    mStrings.erase(endIter2, std::end(mStrings));
+    mStyles.erase(endIter3, std::end(mStyles));
+}
+
+void StringPool::sort(const std::function<bool(const Entry&, const Entry&)>& cmp) {
+    std::sort(std::begin(mStrings), std::end(mStrings),
+            [&cmp](const std::unique_ptr<Entry>& a, const std::unique_ptr<Entry>& b) -> bool {
+                return cmp(*a, *b);
+            }
+    );
+
+    // Assign the indices.
+    const size_t len = mStrings.size();
+    for (size_t index = 0; index < len; index++) {
+        mStrings[index]->index = index;
+    }
+
+    // Reorder the styles.
+    std::sort(std::begin(mStyles), std::end(mStyles),
+            [](const std::unique_ptr<StyleEntry>& lhs,
+               const std::unique_ptr<StyleEntry>& rhs) -> bool {
+                return lhs->str.getIndex() < rhs->str.getIndex();
+            }
+    );
+}
+
+static uint8_t* encodeLength(uint8_t* data, size_t length) {
+    if (length > 0x7fu) {
+        *data++ = 0x80u | (0x000000ffu & (length >> 8));
+    }
+    *data++ = 0x000000ffu & length;
+    return data;
+}
+
+static size_t encodedLengthByteCount(size_t length) {
+    return length > 0x7fu ? 2 : 1;
+}
+
+bool StringPool::flattenUtf8(BigBuffer* out, const StringPool& pool) {
+    const size_t startIndex = out->size();
+    android::ResStringPool_header* header = out->nextBlock<android::ResStringPool_header>();
+    header->header.type = android::RES_STRING_POOL_TYPE;
+    header->header.headerSize = sizeof(*header);
+    header->stringCount = pool.size();
+    header->flags |= android::ResStringPool_header::UTF8_FLAG;
+
+    uint32_t* indices = out->nextBlock<uint32_t>(pool.size());
+
+    uint32_t* styleIndices = nullptr;
+    if (!pool.mStyles.empty()) {
+        header->styleCount = pool.mStyles.back()->str.getIndex() + 1;
+        styleIndices = out->nextBlock<uint32_t>(header->styleCount);
+    }
+
+    const size_t beforeStringsIndex = out->size();
+    header->stringsStart = beforeStringsIndex - startIndex;
+
+    for (const auto& entry : pool) {
+        *indices = out->size() - beforeStringsIndex;
+        indices++;
+
+        std::string encoded = util::utf16ToUtf8(entry->value);
+
+        const size_t stringByteLength = sizeof(char) * encoded.length();
+        const size_t totalSize = encodedLengthByteCount(entry->value.size())
+                + encodedLengthByteCount(encoded.length())
+                + stringByteLength
+                + sizeof(char);
+
+        uint8_t* data = out->nextBlock<uint8_t>(totalSize);
+
+        // First encode the actual UTF16 string length.
+        data = encodeLength(data, entry->value.size());
+
+        // Now encode the size of the converted UTF8 string.
+        data = encodeLength(data, encoded.length());
+
+        memcpy(data, encoded.data(), stringByteLength);
+        data += stringByteLength;
+        *data = 0;
+    }
+
+    out->align4();
+
+    if (!pool.mStyles.empty()) {
+        const size_t beforeStylesIndex = out->size();
+        header->stylesStart = beforeStylesIndex - startIndex;
+
+        size_t currentIndex = 0;
+        for (const auto& entry : pool.mStyles) {
+            while (entry->str.getIndex() > currentIndex) {
+                styleIndices[currentIndex++] = out->size() - beforeStylesIndex;
+
+                uint32_t* spanOffset = out->nextBlock<uint32_t>();
+                *spanOffset = android::ResStringPool_span::END;
+            }
+            styleIndices[currentIndex++] = out->size() - beforeStylesIndex;
+
+            android::ResStringPool_span* span =
+                    out->nextBlock<android::ResStringPool_span>(entry->spans.size());
+            for (const auto& s : entry->spans) {
+                span->name.index = s.name.getIndex();
+                span->firstChar = s.firstChar;
+                span->lastChar = s.lastChar;
+                span++;
+            }
+
+            uint32_t* spanEnd = out->nextBlock<uint32_t>();
+            *spanEnd = android::ResStringPool_span::END;
+        }
+
+        // The error checking code in the platform looks for an entire
+        // ResStringPool_span structure worth of 0xFFFFFFFF at the end
+        // of the style block, so fill in the remaining 2 32bit words
+        // with 0xFFFFFFFF.
+        const size_t paddingLength = sizeof(android::ResStringPool_span)
+                - sizeof(android::ResStringPool_span::name);
+        uint8_t* padding = out->nextBlock<uint8_t>(paddingLength);
+        memset(padding, 0xff, paddingLength);
+        out->align4();
+    }
+    header->header.size = out->size() - startIndex;
+    return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/StringPool.h b/tools/aapt2/StringPool.h
new file mode 100644
index 0000000..2aa5b65
--- /dev/null
+++ b/tools/aapt2/StringPool.h
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_STRING_POOL_H
+#define AAPT_STRING_POOL_H
+
+#include "BigBuffer.h"
+#include "ConfigDescription.h"
+#include "StringPiece.h"
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+struct Span {
+    std::u16string name;
+    uint32_t firstChar;
+    uint32_t lastChar;
+};
+
+struct StyleString {
+    std::u16string str;
+    std::vector<Span> spans;
+};
+
+class StringPool {
+public:
+    struct Context {
+        uint32_t priority;
+        ConfigDescription config;
+    };
+
+    class Entry;
+
+    class Ref {
+    public:
+        Ref();
+        Ref(const Ref&);
+        ~Ref();
+
+        Ref& operator=(const Ref& rhs);
+        const std::u16string* operator->() const;
+        const std::u16string& operator*() const;
+
+        size_t getIndex() const;
+        const Context& getContext() const;
+
+    private:
+        friend class StringPool;
+
+        Ref(Entry* entry);
+
+        Entry* mEntry;
+    };
+
+    class StyleEntry;
+
+    class StyleRef {
+    public:
+        StyleRef();
+        StyleRef(const StyleRef&);
+        ~StyleRef();
+
+        StyleRef& operator=(const StyleRef& rhs);
+        const StyleEntry* operator->() const;
+        const StyleEntry& operator*() const;
+
+        size_t getIndex() const;
+        const Context& getContext() const;
+
+    private:
+        friend class StringPool;
+
+        StyleRef(StyleEntry* entry);
+
+        StyleEntry* mEntry;
+    };
+
+    class Entry {
+    public:
+        std::u16string value;
+        Context context;
+        size_t index;
+
+    private:
+        friend class StringPool;
+        friend class Ref;
+
+        int ref;
+    };
+
+    struct Span {
+        Ref name;
+        uint32_t firstChar;
+        uint32_t lastChar;
+    };
+
+    class StyleEntry {
+    public:
+        Ref str;
+        std::vector<Span> spans;
+
+    private:
+        friend class StringPool;
+        friend class StyleRef;
+
+        int ref;
+    };
+
+    using const_iterator = std::vector<std::unique_ptr<Entry>>::const_iterator;
+
+    static bool flattenUtf8(BigBuffer* out, const StringPool& pool);
+    static bool flatten(BigBuffer* out, const StringPool& pool);
+
+    StringPool() = default;
+    StringPool(const StringPool&) = delete;
+
+    /**
+     * Adds a string to the pool, unless it already exists. Returns
+     * a reference to the string in the pool.
+     */
+    Ref makeRef(const StringPiece16& str);
+
+    /**
+     * Adds a string to the pool, unless it already exists, with a context
+     * object that can be used when sorting the string pool. Returns
+     * a reference to the string in the pool.
+     */
+    Ref makeRef(const StringPiece16& str, const Context& context);
+
+    /**
+     * Adds a style to the string pool and returns a reference to it.
+     */
+    StyleRef makeRef(const StyleString& str);
+
+    /**
+     * Adds a style to the string pool with a context object that
+     * can be used when sorting the string pool. Returns a reference
+     * to the style in the string pool.
+     */
+    StyleRef makeRef(const StyleString& str, const Context& context);
+
+    /**
+     * Moves pool into this one without coalescing strings. When this
+     * function returns, pool will be empty.
+     */
+    void merge(StringPool&& pool);
+
+    /**
+     * Retuns the number of strings in the table.
+     */
+    inline size_t size() const;
+
+    /**
+     * Reserves space for strings and styles as an optimization.
+     */
+    void hintWillAdd(size_t stringCount, size_t styleCount);
+
+    /**
+     * Sorts the strings according to some comparison function.
+     */
+    void sort(const std::function<bool(const Entry&, const Entry&)>& cmp);
+
+    /**
+     * Removes any strings that have no references.
+     */
+    void prune();
+
+private:
+    friend const_iterator begin(const StringPool& pool);
+    friend const_iterator end(const StringPool& pool);
+
+    Ref makeRefImpl(const StringPiece16& str, const Context& context, bool unique);
+
+    std::vector<std::unique_ptr<Entry>> mStrings;
+    std::vector<std::unique_ptr<StyleEntry>> mStyles;
+    std::multimap<StringPiece16, Entry*> mIndexedStrings;
+};
+
+//
+// Inline implementation
+//
+
+inline size_t StringPool::size() const {
+    return mStrings.size();
+}
+
+inline StringPool::const_iterator begin(const StringPool& pool) {
+    return pool.mStrings.begin();
+}
+
+inline StringPool::const_iterator end(const StringPool& pool) {
+    return pool.mStrings.end();
+}
+
+} // namespace aapt
+
+#endif // AAPT_STRING_POOL_H
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
new file mode 100644
index 0000000..5ee1a2d
--- /dev/null
+++ b/tools/aapt2/StringPool_test.cpp
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ */
+
+#include "StringPool.h"
+#include "Util.h"
+
+#include <gtest/gtest.h>
+#include <string>
+
+namespace aapt {
+
+TEST(StringPoolTest, InsertOneString) {
+    StringPool pool;
+
+    StringPool::Ref ref = pool.makeRef(u"wut");
+    EXPECT_EQ(*ref, u"wut");
+}
+
+TEST(StringPoolTest, InsertTwoUniqueStrings) {
+    StringPool pool;
+
+    StringPool::Ref ref = pool.makeRef(u"wut");
+    StringPool::Ref ref2 = pool.makeRef(u"hey");
+
+    EXPECT_EQ(*ref, u"wut");
+    EXPECT_EQ(*ref2, u"hey");
+}
+
+TEST(StringPoolTest, DoNotInsertNewDuplicateString) {
+    StringPool pool;
+
+    StringPool::Ref ref = pool.makeRef(u"wut");
+    StringPool::Ref ref2 = pool.makeRef(u"wut");
+
+    EXPECT_EQ(*ref, u"wut");
+    EXPECT_EQ(*ref2, u"wut");
+    EXPECT_EQ(1u, pool.size());
+}
+
+TEST(StringPoolTest, MaintainInsertionOrderIndex) {
+    StringPool pool;
+
+    StringPool::Ref ref = pool.makeRef(u"z");
+    StringPool::Ref ref2 = pool.makeRef(u"a");
+    StringPool::Ref ref3 = pool.makeRef(u"m");
+
+    EXPECT_EQ(0u, ref.getIndex());
+    EXPECT_EQ(1u, ref2.getIndex());
+    EXPECT_EQ(2u, ref3.getIndex());
+}
+
+TEST(StringPoolTest, PruneStringsWithNoReferences) {
+    StringPool pool;
+
+    {
+        StringPool::Ref ref = pool.makeRef(u"wut");
+        EXPECT_EQ(*ref, u"wut");
+        EXPECT_EQ(1u, pool.size());
+    }
+
+    EXPECT_EQ(1u, pool.size());
+    pool.prune();
+    EXPECT_EQ(0u, pool.size());
+}
+
+TEST(StringPoolTest, SortAndMaintainIndexesInReferences) {
+    StringPool pool;
+
+    StringPool::Ref ref = pool.makeRef(u"z");
+    StringPool::StyleRef ref2 = pool.makeRef(StyleString{ {u"a"} });
+    StringPool::Ref ref3 = pool.makeRef(u"m");
+
+    EXPECT_EQ(*ref, u"z");
+    EXPECT_EQ(0u, ref.getIndex());
+
+    EXPECT_EQ(*(ref2->str), u"a");
+    EXPECT_EQ(1u, ref2.getIndex());
+
+    EXPECT_EQ(*ref3, u"m");
+    EXPECT_EQ(2u, ref3.getIndex());
+
+    pool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
+        return a.value < b.value;
+    });
+
+
+    EXPECT_EQ(*ref, u"z");
+    EXPECT_EQ(2u, ref.getIndex());
+
+    EXPECT_EQ(*(ref2->str), u"a");
+    EXPECT_EQ(0u, ref2.getIndex());
+
+    EXPECT_EQ(*ref3, u"m");
+    EXPECT_EQ(1u, ref3.getIndex());
+}
+
+TEST(StringPoolTest, SortAndStillDedupe) {
+    StringPool pool;
+
+    StringPool::Ref ref = pool.makeRef(u"z");
+    StringPool::Ref ref2 = pool.makeRef(u"a");
+    StringPool::Ref ref3 = pool.makeRef(u"m");
+
+    pool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
+        return a.value < b.value;
+    });
+
+    StringPool::Ref ref4 = pool.makeRef(u"z");
+    StringPool::Ref ref5 = pool.makeRef(u"a");
+    StringPool::Ref ref6 = pool.makeRef(u"m");
+
+    EXPECT_EQ(ref4.getIndex(), ref.getIndex());
+    EXPECT_EQ(ref5.getIndex(), ref2.getIndex());
+    EXPECT_EQ(ref6.getIndex(), ref3.getIndex());
+}
+
+TEST(StringPoolTest, AddStyles) {
+    StringPool pool;
+
+    StyleString str {
+        { u"android" },
+        {
+            Span{ { u"b" }, 2, 6 }
+        }
+    };
+
+    StringPool::StyleRef ref = pool.makeRef(str);
+
+    EXPECT_EQ(0u, ref.getIndex());
+    EXPECT_EQ(std::u16string(u"android"), *(ref->str));
+    ASSERT_EQ(1u, ref->spans.size());
+
+    const StringPool::Span& span = ref->spans.front();
+    EXPECT_EQ(*(span.name), u"b");
+    EXPECT_EQ(2u, span.firstChar);
+    EXPECT_EQ(6u, span.lastChar);
+}
+
+TEST(StringPoolTest, DoNotDedupeStyleWithSameStringAsNonStyle) {
+    StringPool pool;
+
+    StringPool::Ref ref = pool.makeRef(u"android");
+
+    StyleString str { { u"android" } };
+    StringPool::StyleRef styleRef = pool.makeRef(str);
+
+    EXPECT_NE(ref.getIndex(), styleRef.getIndex());
+}
+
+constexpr const char16_t* sLongString = u"バッテリーを長持ちさせるため、バッテリーセーバーは端末のパフォーマンスを抑え、バイブレーション、位置情報サービス、大半のバックグラウンドデータを制限します。メール、SMSや、同期を使 用するその他のアプリは、起動しても更新されないことがあります。バッテリーセーバーは端末の充電中は自動的にOFFになります。";
+
+TEST(StringPoolTest, FlattenUtf8) {
+    StringPool pool;
+
+    StringPool::Ref ref1 = pool.makeRef(u"hello");
+    StringPool::Ref ref2 = pool.makeRef(u"goodbye");
+    StringPool::Ref ref3 = pool.makeRef(sLongString);
+    StringPool::StyleRef ref4 = pool.makeRef(StyleString{
+            { u"style" },
+            { Span{ { u"b" }, 0, 1 }, Span{ { u"i" }, 2, 3 } }
+    });
+
+    EXPECT_EQ(0u, ref1.getIndex());
+    EXPECT_EQ(1u, ref2.getIndex());
+    EXPECT_EQ(2u, ref3.getIndex());
+    EXPECT_EQ(3u, ref4.getIndex());
+
+    BigBuffer buffer(1024);
+    StringPool::flattenUtf8(&buffer, pool);
+
+    uint8_t* data = new uint8_t[buffer.size()];
+    uint8_t* p = data;
+    for (const auto& b : buffer) {
+        memcpy(p, b.buffer.get(), b.size);
+        p += b.size;
+    }
+
+    {
+        android::ResStringPool test;
+        ASSERT_EQ(android::NO_ERROR, test.setTo(data, buffer.size()));
+
+        EXPECT_EQ(util::getString(test, 0), u"hello");
+        EXPECT_EQ(util::getString(test, 1), u"goodbye");
+        EXPECT_EQ(util::getString(test, 2), sLongString);
+        EXPECT_EQ(util::getString(test, 3), u"style");
+
+        const android::ResStringPool_span* span = test.styleAt(3);
+        ASSERT_NE(nullptr, span);
+        EXPECT_EQ(util::getString(test, span->name.index), u"b");
+        EXPECT_EQ(0u, span->firstChar);
+        EXPECT_EQ(1u, span->lastChar);
+        span++;
+
+        ASSERT_NE(android::ResStringPool_span::END, span->name.index);
+        EXPECT_EQ(util::getString(test, span->name.index), u"i");
+        EXPECT_EQ(2u, span->firstChar);
+        EXPECT_EQ(3u, span->lastChar);
+        span++;
+
+        EXPECT_EQ(android::ResStringPool_span::END, span->name.index);
+    }
+    delete[] data;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/TableFlattener.cpp b/tools/aapt2/TableFlattener.cpp
new file mode 100644
index 0000000..c306185
--- /dev/null
+++ b/tools/aapt2/TableFlattener.cpp
@@ -0,0 +1,511 @@
+/*
+ * 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.
+ */
+
+#include "BigBuffer.h"
+#include "ConfigDescription.h"
+#include "Logger.h"
+#include "ResourceTable.h"
+#include "ResourceTypeExtensions.h"
+#include "ResourceValues.h"
+#include "StringPool.h"
+#include "TableFlattener.h"
+#include "Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <sstream>
+
+namespace aapt {
+
+struct FlatEntry {
+    const ResourceEntry& entry;
+    const Value& value;
+    uint32_t entryKey;
+    uint32_t sourcePathKey;
+    uint32_t sourceLine;
+};
+
+/**
+ * Visitor that knows how to encode Map values.
+ */
+class MapFlattener : public ConstValueVisitor {
+public:
+    MapFlattener(BigBuffer* out, const FlatEntry& flatEntry,
+                 std::vector<std::pair<ResourceNameRef, uint32_t>>& symbols) :
+            mOut(out), mSymbols(symbols) {
+        mMap = mOut->nextBlock<android::ResTable_map_entry>();
+        mMap->key.index = flatEntry.entryKey;
+        mMap->flags = android::ResTable_entry::FLAG_COMPLEX;
+        if (flatEntry.entry.publicStatus.isPublic) {
+            mMap->flags |= android::ResTable_entry::FLAG_PUBLIC;
+        }
+        if (flatEntry.value.isWeak()) {
+            mMap->flags |= android::ResTable_entry::FLAG_WEAK;
+        }
+
+        ResTable_entry_source* sourceBlock = mOut->nextBlock<ResTable_entry_source>();
+        sourceBlock->pathIndex = flatEntry.sourcePathKey;
+        sourceBlock->line = flatEntry.sourceLine;
+
+        mMap->size = sizeof(*mMap) + sizeof(*sourceBlock);
+    }
+
+    void flattenParent(const Reference& ref) {
+        if (!ref.id.isValid()) {
+            mSymbols.push_back({
+                    ResourceNameRef(ref.name),
+                    (mOut->size() - mMap->size) + sizeof(*mMap) - sizeof(android::ResTable_entry)
+            });
+        }
+        mMap->parent.ident = ref.id.id;
+    }
+
+    void flattenEntry(const Reference& key, const Item& value) {
+        mMap->count++;
+
+        android::ResTable_map* outMapEntry = mOut->nextBlock<android::ResTable_map>();
+
+        // Write the key.
+        if (!Res_INTERNALID(key.id.id) && !key.id.isValid()) {
+            mSymbols.push_back(std::make_pair(ResourceNameRef(key.name),
+                    mOut->size() - sizeof(*outMapEntry)));
+        }
+        outMapEntry->name.ident = key.id.id;
+
+        // Write the value.
+        value.flatten(outMapEntry->value);
+
+        if (outMapEntry->value.data == 0x0) {
+            visitFunc<Reference>(value, [&](const Reference& reference) {
+                mSymbols.push_back(std::make_pair(ResourceNameRef(reference.name),
+                        mOut->size() - sizeof(outMapEntry->value.data)));
+            });
+        }
+        outMapEntry->value.size = sizeof(outMapEntry->value);
+    }
+
+    static bool compareStyleEntries(const Style::Entry* lhs, const Style::Entry* rhs) {
+        return lhs->key.id < rhs->key.id;
+    }
+
+    void visit(const Style& style, ValueVisitorArgs&) override {
+        if (style.parent.name.isValid()) {
+            flattenParent(style.parent);
+        }
+
+        // First sort the entries by ID.
+        std::vector<const Style::Entry*> sortedEntries;
+        for (const auto& styleEntry : style.entries) {
+            auto iter = std::lower_bound(sortedEntries.begin(), sortedEntries.end(),
+                    &styleEntry, compareStyleEntries);
+            sortedEntries.insert(iter, &styleEntry);
+        }
+
+        for (const Style::Entry* styleEntry : sortedEntries) {
+            flattenEntry(styleEntry->key, *styleEntry->value);
+        }
+    }
+
+    void visit(const Attribute& attr, ValueVisitorArgs&) override {
+        android::Res_value tempVal;
+        tempVal.dataType = android::Res_value::TYPE_INT_DEC;
+        tempVal.data = attr.typeMask;
+        flattenEntry(Reference(ResourceId{android::ResTable_map::ATTR_TYPE}),
+                BinaryPrimitive(tempVal));
+
+        for (const auto& symbol : attr.symbols) {
+            tempVal.data = symbol.value;
+            flattenEntry(symbol.symbol, BinaryPrimitive(tempVal));
+        }
+    }
+
+    void visit(const Styleable& styleable, ValueVisitorArgs&) override {
+        for (const auto& attr : styleable.entries) {
+            flattenEntry(attr, BinaryPrimitive(android::Res_value{}));
+        }
+    }
+
+    void visit(const Array& array, ValueVisitorArgs&) override {
+        for (const auto& item : array.items) {
+            flattenEntry({}, *item);
+        }
+    }
+
+    void visit(const Plural& plural, ValueVisitorArgs&) override {
+        const size_t count = plural.values.size();
+        for (size_t i = 0; i < count; i++) {
+            if (!plural.values[i]) {
+                continue;
+            }
+
+            ResourceId q;
+            switch (i) {
+                case Plural::Zero:
+                    q.id = android::ResTable_map::ATTR_ZERO;
+                    break;
+
+                case Plural::One:
+                    q.id = android::ResTable_map::ATTR_ONE;
+                    break;
+
+                case Plural::Two:
+                    q.id = android::ResTable_map::ATTR_TWO;
+                    break;
+
+                case Plural::Few:
+                    q.id = android::ResTable_map::ATTR_FEW;
+                    break;
+
+                case Plural::Many:
+                    q.id = android::ResTable_map::ATTR_MANY;
+                    break;
+
+                case Plural::Other:
+                    q.id = android::ResTable_map::ATTR_OTHER;
+                    break;
+
+                default:
+                    assert(false);
+                    break;
+            }
+
+            flattenEntry(Reference(q), *plural.values[i]);
+        }
+    }
+
+private:
+    BigBuffer* mOut;
+    std::vector<std::pair<ResourceNameRef, uint32_t>>& mSymbols;
+    android::ResTable_map_entry* mMap;
+};
+
+TableFlattener::TableFlattener(Options options)
+: mOptions(options) {
+}
+
+bool TableFlattener::flattenValue(BigBuffer* out, const FlatEntry& flatEntry,
+        std::vector<std::pair<ResourceNameRef, uint32_t>>& symbolEntries) {
+    if (flatEntry.value.isItem()) {
+        android::ResTable_entry* entry = out->nextBlock<android::ResTable_entry>();
+
+        if (flatEntry.entry.publicStatus.isPublic) {
+            entry->flags |= android::ResTable_entry::FLAG_PUBLIC;
+        }
+
+        if (flatEntry.value.isWeak()) {
+            entry->flags |= android::ResTable_entry::FLAG_WEAK;
+        }
+
+        entry->key.index = flatEntry.entryKey;
+        entry->size = sizeof(*entry);
+
+        if (mOptions.useExtendedChunks) {
+            // Write the extra source block. This will be ignored by
+            // the Android runtime.
+            ResTable_entry_source* sourceBlock = out->nextBlock<ResTable_entry_source>();
+            sourceBlock->pathIndex = flatEntry.sourcePathKey;
+            sourceBlock->line = flatEntry.sourceLine;
+
+            entry->size += sizeof(*sourceBlock);
+        }
+
+        android::Res_value* outValue = out->nextBlock<android::Res_value>();
+
+        const Item& item = static_cast<const Item&>(flatEntry.value);
+        if (!item.flatten(*outValue)) {
+            return false;
+        }
+
+        if (outValue->data == 0x0) {
+            visitFunc<Reference>(item, [&](const Reference& reference) {
+                symbolEntries.push_back({
+                        ResourceNameRef(reference.name),
+                        out->size() - sizeof(outValue->data)
+                });
+            });
+        }
+        outValue->size = sizeof(*outValue);
+        return true;
+    }
+
+    MapFlattener flattener(out, flatEntry, symbolEntries);
+    flatEntry.value.accept(flattener, {});
+    return true;
+}
+
+bool TableFlattener::flatten(BigBuffer* out, const ResourceTable& table) {
+    const size_t beginning = out->size();
+
+    if (table.getPackage().size() == 0) {
+        Logger::error()
+                << "ResourceTable has no package name."
+                << std::endl;
+        return false;
+    }
+
+    if (table.getPackageId() == ResourceTable::kUnsetPackageId) {
+        Logger::error()
+                << "ResourceTable has no package ID set."
+                << std::endl;
+        return false;
+    }
+
+    std::vector<std::pair<ResourceNameRef, uint32_t>> symbolEntries;
+
+    StringPool typePool;
+    StringPool keyPool;
+    StringPool sourcePool;
+
+    // Sort the types by their IDs. They will be inserted into the StringPool
+    // in this order.
+    std::vector<ResourceTableType*> sortedTypes;
+    for (const auto& type : table) {
+        if (type->type == ResourceType::kStyleable && !mOptions.useExtendedChunks) {
+            continue;
+        }
+
+        auto iter = std::lower_bound(std::begin(sortedTypes), std::end(sortedTypes), type.get(),
+                [](const ResourceTableType* lhs, const ResourceTableType* rhs) -> bool {
+                    return lhs->typeId < rhs->typeId;
+                });
+        sortedTypes.insert(iter, type.get());
+    }
+
+    BigBuffer typeBlock(1024);
+    size_t expectedTypeId = 1;
+    for (const ResourceTableType* type : sortedTypes) {
+        if (type->typeId == ResourceTableType::kUnsetTypeId
+                || type->typeId == 0) {
+            Logger::error()
+                    << "resource type '"
+                    << type->type
+                    << "' from package '"
+                    << table.getPackage()
+                    << "' has no ID."
+                    << std::endl;
+            return false;
+        }
+
+        // If there is a gap in the type IDs, fill in the StringPool
+        // with empty values until we reach the ID we expect.
+        while (type->typeId > expectedTypeId) {
+            std::u16string typeName(u"?");
+            typeName += expectedTypeId;
+            typePool.makeRef(typeName);
+            expectedTypeId++;
+        }
+        expectedTypeId++;
+        typePool.makeRef(toString(type->type));
+
+        android::ResTable_typeSpec* spec = typeBlock.nextBlock<android::ResTable_typeSpec>();
+        spec->header.type = android::RES_TABLE_TYPE_SPEC_TYPE;
+        spec->header.headerSize = sizeof(*spec);
+        spec->header.size = spec->header.headerSize + (type->entries.size() * sizeof(uint32_t));
+        spec->id = type->typeId;
+        spec->entryCount = type->entries.size();
+
+        // Reserve space for the masks of each resource in this type. These
+        // show for which configuration axis the resource changes.
+        uint32_t* configMasks = typeBlock.nextBlock<uint32_t>(type->entries.size());
+
+        // Sort the entries by entry ID and write their configuration masks.
+        std::vector<ResourceEntry*> entries;
+        const size_t entryCount = type->entries.size();
+        for (size_t entryIndex = 0; entryIndex < entryCount; entryIndex++) {
+            const auto& entry = type->entries[entryIndex];
+
+            if (entry->entryId == ResourceEntry::kUnsetEntryId) {
+                Logger::error()
+                        << "resource '"
+                        << ResourceName{ table.getPackage(), type->type, entry->name }
+                        << "' has no ID."
+                        << std::endl;
+                return false;
+            }
+
+            auto iter = std::lower_bound(std::begin(entries), std::end(entries), entry.get(),
+                    [](const ResourceEntry* lhs, const ResourceEntry* rhs) -> bool {
+                        return lhs->entryId < rhs->entryId;
+                    });
+            entries.insert(iter, entry.get());
+
+            // Populate the config masks for this entry.
+            if (entry->publicStatus.isPublic) {
+                configMasks[entry->entryId] |= android::ResTable_typeSpec::SPEC_PUBLIC;
+            }
+
+            const size_t configCount = entry->values.size();
+            for (size_t i = 0; i < configCount; i++) {
+                const ConfigDescription& config = entry->values[i].config;
+                for (size_t j = i + 1; j < configCount; j++) {
+                    configMasks[entry->entryId] |= config.diff(entry->values[j].config);
+                }
+            }
+        }
+
+        // The binary resource table lists resource entries for each configuration.
+        // We store them inverted, where a resource entry lists the values for each
+        // configuration available. Here we reverse this to match the binary table.
+        std::map<ConfigDescription, std::vector<FlatEntry>> data;
+        for (const ResourceEntry* entry : entries) {
+            size_t keyIndex = keyPool.makeRef(entry->name).getIndex();
+
+            if (keyIndex > std::numeric_limits<uint32_t>::max()) {
+                Logger::error()
+                        << "resource key string pool exceeded max size."
+                        << std::endl;
+                return false;
+            }
+
+            for (const auto& configValue : entry->values) {
+                data[configValue.config].push_back(FlatEntry{
+                        *entry,
+                        *configValue.value,
+                        static_cast<uint32_t>(keyIndex),
+                        static_cast<uint32_t>(sourcePool.makeRef(util::utf8ToUtf16(
+                                        configValue.source.path)).getIndex()),
+                        static_cast<uint32_t>(configValue.source.line)
+                });
+            }
+        }
+
+        // Begin flattening a configuration for the current type.
+        for (const auto& entry : data) {
+            const size_t typeHeaderStart = typeBlock.size();
+            android::ResTable_type* typeHeader = typeBlock.nextBlock<android::ResTable_type>();
+            typeHeader->header.type = android::RES_TABLE_TYPE_TYPE;
+            typeHeader->header.headerSize = sizeof(*typeHeader);
+            typeHeader->id = type->typeId;
+            typeHeader->entryCount = type->entries.size();
+            typeHeader->entriesStart = typeHeader->header.headerSize
+                    + (sizeof(uint32_t) * type->entries.size());
+            typeHeader->config = entry.first;
+
+            uint32_t* indices = typeBlock.nextBlock<uint32_t>(type->entries.size());
+            memset(indices, 0xff, type->entries.size() * sizeof(uint32_t));
+
+            const size_t entryStart = typeBlock.size();
+            for (const FlatEntry& flatEntry : entry.second) {
+                assert(flatEntry.entry.entryId < type->entries.size());
+                indices[flatEntry.entry.entryId] = typeBlock.size() - entryStart;
+                if (!flattenValue(&typeBlock, flatEntry, symbolEntries)) {
+                    Logger::error()
+                            << "failed to flatten resource '"
+                            << ResourceNameRef {
+                                    table.getPackage(), type->type, flatEntry.entry.name }
+                            << "' for configuration '"
+                            << entry.first
+                            << "'."
+                            << std::endl;
+                    return false;
+                }
+            }
+
+            typeBlock.align4();
+            typeHeader->header.size = typeBlock.size() - typeHeaderStart;
+        }
+    }
+
+    const size_t beforeTable = out->size();
+    android::ResTable_header* header = out->nextBlock<android::ResTable_header>();
+    header->header.type = android::RES_TABLE_TYPE;
+    header->header.headerSize = sizeof(*header);
+    header->packageCount = 1;
+
+    SymbolTable_entry* symbolEntryData = nullptr;
+    if (!symbolEntries.empty() && mOptions.useExtendedChunks) {
+        const size_t beforeSymbolTable = out->size();
+        StringPool symbolPool;
+        SymbolTable_header* symbolHeader = out->nextBlock<SymbolTable_header>();
+        symbolHeader->header.type = RES_TABLE_SYMBOL_TABLE_TYPE;
+        symbolHeader->header.headerSize = sizeof(*symbolHeader);
+        symbolHeader->count = symbolEntries.size();
+
+        symbolEntryData = out->nextBlock<SymbolTable_entry>(symbolHeader->count);
+
+        size_t i = 0;
+        for (const auto& entry : symbolEntries) {
+            symbolEntryData[i].offset = entry.second;
+            StringPool::Ref ref = symbolPool.makeRef(
+                    entry.first.package.toString() + u":" +
+                    toString(entry.first.type).toString() + u"/" +
+                    entry.first.entry.toString());
+            symbolEntryData[i].stringIndex = ref.getIndex();
+            i++;
+        }
+
+        StringPool::flattenUtf8(out, symbolPool);
+        out->align4();
+        symbolHeader->header.size = out->size() - beforeSymbolTable;
+    }
+
+    if (sourcePool.size() > 0 && mOptions.useExtendedChunks) {
+        const size_t beforeSourcePool = out->size();
+        android::ResChunk_header* sourceHeader = out->nextBlock<android::ResChunk_header>();
+        sourceHeader->type = RES_TABLE_SOURCE_POOL_TYPE;
+        sourceHeader->headerSize = sizeof(*sourceHeader);
+        StringPool::flattenUtf8(out, sourcePool);
+        out->align4();
+        sourceHeader->size = out->size() - beforeSourcePool;
+    }
+
+    StringPool::flattenUtf8(out, table.getValueStringPool());
+
+    const size_t beforePackageIndex = out->size();
+    android::ResTable_package* package = out->nextBlock<android::ResTable_package>();
+    package->header.type = android::RES_TABLE_PACKAGE_TYPE;
+    package->header.headerSize = sizeof(*package);
+
+    if (table.getPackageId() > std::numeric_limits<uint8_t>::max()) {
+        Logger::error()
+                << "package ID 0x'"
+                << std::hex << table.getPackageId() << std::dec
+                << "' is invalid."
+                << std::endl;
+        return false;
+    }
+    package->id = table.getPackageId();
+
+    if (table.getPackage().size() >= sizeof(package->name) / sizeof(package->name[0])) {
+        Logger::error()
+                << "package name '"
+                << table.getPackage()
+                << "' is too long."
+                << std::endl;
+        return false;
+    }
+    memcpy(package->name, reinterpret_cast<const uint16_t*>(table.getPackage().data()),
+            table.getPackage().length() * sizeof(char16_t));
+    package->name[table.getPackage().length()] = 0;
+
+    package->typeStrings = package->header.headerSize;
+    StringPool::flattenUtf8(out, typePool);
+    package->keyStrings = out->size() - beforePackageIndex;
+    StringPool::flattenUtf8(out, keyPool);
+
+    if (symbolEntryData != nullptr) {
+        for (size_t i = 0; i < symbolEntries.size(); i++) {
+            symbolEntryData[i].offset += out->size() - beginning;
+        }
+    }
+
+    out->appendBuffer(std::move(typeBlock));
+
+    package->header.size = out->size() - beforePackageIndex;
+    header->header.size = out->size() - beforeTable;
+    return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/TableFlattener.h b/tools/aapt2/TableFlattener.h
new file mode 100644
index 0000000..0ae798c
--- /dev/null
+++ b/tools/aapt2/TableFlattener.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_TABLE_FLATTENER_H
+#define AAPT_TABLE_FLATTENER_H
+
+#include "BigBuffer.h"
+#include "ResourceTable.h"
+
+namespace aapt {
+
+struct FlatEntry;
+
+/**
+ * Flattens a ResourceTable into a binary format suitable
+ * for loading into a ResTable on the host or device.
+ */
+struct TableFlattener {
+    /**
+     * A set of options for this TableFlattener.
+     */
+    struct Options {
+        /**
+         * Specifies whether to output extended chunks, like
+         * source information and mising symbol entries. Default
+         * is true.
+         *
+         * Set this to false when emitting the final table to be used
+         * on device.
+         */
+        bool useExtendedChunks = true;
+    };
+
+    TableFlattener(Options options);
+
+    bool flatten(BigBuffer* out, const ResourceTable& table);
+
+private:
+    bool flattenValue(BigBuffer* out, const FlatEntry& flatEntry,
+                      std::vector<std::pair<ResourceNameRef, uint32_t>>& symbolEntries);
+
+    Options mOptions;
+};
+
+} // namespace aapt
+
+#endif // AAPT_TABLE_FLATTENER_H
diff --git a/tools/aapt2/Util.cpp b/tools/aapt2/Util.cpp
new file mode 100644
index 0000000..8a4c88f
--- /dev/null
+++ b/tools/aapt2/Util.cpp
@@ -0,0 +1,290 @@
+/*
+ * 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.
+ */
+
+#include "BigBuffer.h"
+#include "Maybe.h"
+#include "StringPiece.h"
+#include "Util.h"
+
+#include <algorithm>
+#include <ostream>
+#include <string>
+#include <utils/Unicode.h>
+#include <vector>
+
+namespace aapt {
+namespace util {
+
+static std::vector<std::string> splitAndTransform(const StringPiece& str, char sep,
+        const std::function<char(char)>& f) {
+    std::vector<std::string> parts;
+    const StringPiece::const_iterator end = std::end(str);
+    StringPiece::const_iterator start = std::begin(str);
+    StringPiece::const_iterator current;
+    do {
+        current = std::find(start, end, sep);
+        parts.emplace_back(str.substr(start, current).toString());
+        if (f) {
+            std::string& part = parts.back();
+            std::transform(part.begin(), part.end(), part.begin(), f);
+        }
+        start = current + 1;
+    } while (current != end);
+    return parts;
+}
+
+std::vector<std::string> split(const StringPiece& str, char sep) {
+    return splitAndTransform(str, sep, nullptr);
+}
+
+std::vector<std::string> splitAndLowercase(const StringPiece& str, char sep) {
+    return splitAndTransform(str, sep, ::tolower);
+}
+
+bool stringEndsWith(const StringPiece& str, const StringPiece& suffix) {
+    if (str.size() < suffix.size()) {
+        return false;
+    }
+    return str.substr(str.size() - suffix.size(), suffix.size()) == suffix;
+}
+
+StringPiece16 trimWhitespace(const StringPiece16& str) {
+    if (str.size() == 0 || str.data() == nullptr) {
+        return str;
+    }
+
+    const char16_t* start = str.data();
+    const char16_t* end = str.data() + str.length();
+
+    while (start != end && util::isspace16(*start)) {
+        start++;
+    }
+
+    while (end != start && util::isspace16(*(end - 1))) {
+        end--;
+    }
+
+    return StringPiece16(start, end - start);
+}
+
+StringPiece16::const_iterator findNonAlphaNumericAndNotInSet(const StringPiece16& str,
+        const StringPiece16& allowedChars) {
+    const auto endIter = str.end();
+    for (auto iter = str.begin(); iter != endIter; ++iter) {
+        char16_t c = *iter;
+        if ((c >= u'a' && c <= u'z') ||
+                (c >= u'A' && c <= u'Z') ||
+                (c >= u'0' && c <= u'9')) {
+            continue;
+        }
+
+        bool match = false;
+        for (char16_t i : allowedChars) {
+            if (c == i) {
+                match = true;
+                break;
+            }
+        }
+
+        if (!match) {
+            return iter;
+        }
+    }
+    return endIter;
+}
+
+static Maybe<char16_t> parseUnicodeCodepoint(const char16_t** start, const char16_t* end) {
+    char16_t code = 0;
+    for (size_t i = 0; i < 4 && *start != end; i++, (*start)++) {
+        char16_t c = **start;
+        int a;
+        if (c >= '0' && c <= '9') {
+            a = c - '0';
+        } else if (c >= 'a' && c <= 'f') {
+            a = c - 'a' + 10;
+        } else if (c >= 'A' && c <= 'F') {
+            a = c - 'A' + 10;
+        } else {
+            return make_nothing<char16_t>();
+        }
+        code = (code << 4) | a;
+    }
+    return make_value(code);
+}
+
+StringBuilder& StringBuilder::append(const StringPiece16& str) {
+    if (!mError.empty()) {
+        return *this;
+    }
+
+    const char16_t* const end = str.end();
+    const char16_t* start = str.begin();
+    const char16_t* current = start;
+    while (current != end) {
+        if (*current == u'"') {
+            if (!mQuote && mTrailingSpace) {
+                // We found an opening quote, and we have
+                // trailing space, so we should append that
+                // space now.
+                if (mTrailingSpace) {
+                    // We had trailing whitespace, so
+                    // replace with a single space.
+                    if (!mStr.empty()) {
+                        mStr += u' ';
+                    }
+                    mTrailingSpace = false;
+                }
+            }
+            mQuote = !mQuote;
+            mStr.append(start, current - start);
+            start = current + 1;
+        } else if (*current == u'\'' && !mQuote) {
+            // This should be escaped.
+            mError = "unescaped apostrophe";
+            return *this;
+        } else if (*current == u'\\') {
+            // This is an escape sequence, convert to the real value.
+            if (!mQuote && mTrailingSpace) {
+                // We had trailing whitespace, so
+                // replace with a single space.
+                if (!mStr.empty()) {
+                    mStr += u' ';
+                }
+                mTrailingSpace = false;
+            }
+            mStr.append(start, current - start);
+            start = current + 1;
+
+            current++;
+            if (current != end) {
+                switch (*current) {
+                    case u't':
+                        mStr += u'\t';
+                        break;
+                    case u'n':
+                        mStr += u'\n';
+                        break;
+                    case u'#':
+                        mStr += u'#';
+                        break;
+                    case u'@':
+                        mStr += u'@';
+                        break;
+                    case u'?':
+                        mStr += u'?';
+                        break;
+                    case u'"':
+                        mStr += u'"';
+                        break;
+                    case u'\'':
+                        mStr += u'\'';
+                        break;
+                    case u'\\':
+                        mStr += u'\\';
+                        break;
+                    case u'u': {
+                        current++;
+                        Maybe<char16_t> c = parseUnicodeCodepoint(&current, end);
+                        if (!c) {
+                            mError = "invalid unicode escape sequence";
+                            return *this;
+                        }
+                        mStr += c.value();
+                        current -= 1;
+                        break;
+                    }
+
+                    default:
+                        // Ignore.
+                        break;
+                }
+                start = current + 1;
+            }
+        } else if (!mQuote) {
+            // This is not quoted text, so look for whitespace.
+            if (isspace16(*current)) {
+                // We found whitespace, see if we have seen some
+                // before.
+                if (!mTrailingSpace) {
+                    // We didn't see a previous adjacent space,
+                    // so mark that we did.
+                    mTrailingSpace = true;
+                    mStr.append(start, current - start);
+                }
+
+                // Keep skipping whitespace.
+                start = current + 1;
+            } else if (mTrailingSpace) {
+                // We saw trailing space before, so replace all
+                // that trailing space with one space.
+                if (!mStr.empty()) {
+                    mStr += u' ';
+                }
+                mTrailingSpace = false;
+            }
+        }
+        current++;
+    }
+    mStr.append(start, end - start);
+    return *this;
+}
+
+std::u16string utf8ToUtf16(const StringPiece& utf8) {
+    ssize_t utf16Length = utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(utf8.data()),
+            utf8.length());
+    if (utf16Length <= 0) {
+        return {};
+    }
+
+    std::u16string utf16;
+    utf16.resize(utf16Length);
+    utf8_to_utf16(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length(), &*utf16.begin());
+    return utf16;
+}
+
+std::string utf16ToUtf8(const StringPiece16& utf16) {
+    ssize_t utf8Length = utf16_to_utf8_length(utf16.data(), utf16.length());
+    if (utf8Length <= 0) {
+        return {};
+    }
+
+    std::string utf8;
+    utf8.resize(utf8Length);
+    utf16_to_utf8(utf16.data(), utf16.length(), &*utf8.begin());
+    return utf8;
+}
+
+bool writeAll(std::ostream& out, const BigBuffer& buffer) {
+    for (const auto& b : buffer) {
+        if (!out.write(reinterpret_cast<const char*>(b.buffer.get()), b.size)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+std::unique_ptr<uint8_t[]> copy(const BigBuffer& buffer) {
+    std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(new uint8_t[buffer.size()]);
+    uint8_t* p = data.get();
+    for (const auto& block : buffer) {
+        memcpy(p, block.buffer.get(), block.size);
+        p += block.size;
+    }
+    return data;
+}
+
+} // namespace util
+} // namespace aapt
diff --git a/tools/aapt2/Util.h b/tools/aapt2/Util.h
new file mode 100644
index 0000000..4c5249be
--- /dev/null
+++ b/tools/aapt2/Util.h
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_UTIL_H
+#define AAPT_UTIL_H
+
+#include "BigBuffer.h"
+#include "StringPiece.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <functional>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <vector>
+
+namespace aapt {
+namespace util {
+
+std::vector<std::string> split(const StringPiece& str, char sep);
+std::vector<std::string> splitAndLowercase(const StringPiece& str, char sep);
+
+/**
+ * Returns true if the string ends with suffix.
+ */
+bool stringEndsWith(const StringPiece& str, const StringPiece& suffix);
+
+/**
+ * Creates a new StringPiece16 that points to a substring
+ * of the original string without leading or trailing whitespace.
+ */
+StringPiece16 trimWhitespace(const StringPiece16& str);
+
+/**
+ * UTF-16 isspace(). It basically checks for lower range characters that are
+ * whitespace.
+ */
+inline bool isspace16(char16_t c) {
+    return c < 0x0080 && isspace(c);
+}
+
+/**
+ * Returns an iterator to the first character that is not alpha-numeric and that
+ * is not in the allowedChars set.
+ */
+StringPiece16::const_iterator findNonAlphaNumericAndNotInSet(const StringPiece16& str,
+        const StringPiece16& allowedChars);
+
+/**
+ * Makes a std::unique_ptr<> with the template parameter inferred by the compiler.
+ * This will be present in C++14 and can be removed then.
+ */
+template <typename T, class... Args>
+std::unique_ptr<T> make_unique(Args&&... args) {
+    return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
+}
+
+/**
+ * Writes a set of items to the std::ostream, joining the times with the provided
+ * separator.
+ */
+template <typename Iterator>
+::std::function<::std::ostream&(::std::ostream&)> joiner(Iterator begin, Iterator end,
+        const char* sep) {
+    return [begin, end, sep](::std::ostream& out) -> ::std::ostream& {
+        for (auto iter = begin; iter != end; ++iter) {
+            if (iter != begin) {
+                out << sep;
+            }
+            out << *iter;
+        }
+        return out;
+    };
+}
+
+inline ::std::function<::std::ostream&(::std::ostream&)> formatSize(size_t size) {
+    return [size](::std::ostream& out) -> ::std::ostream& {
+        constexpr size_t K = 1024;
+        constexpr size_t M = K * K;
+        constexpr size_t G = M * M;
+        if (size < K) {
+            out << size << "B";
+        } else if (size < M) {
+            out << (double(size) / K) << " KiB";
+        } else if (size < G) {
+            out << (double(size) / M) << " MiB";
+        } else {
+            out << (double(size) / G) << " GiB";
+        }
+        return out;
+    };
+}
+
+/**
+ * Helper method to extract a string from a StringPool.
+ */
+inline StringPiece16 getString(const android::ResStringPool& pool, size_t idx) {
+    size_t len;
+    const char16_t* str = pool.stringAt(idx, &len);
+    if (str != nullptr) {
+        return StringPiece16(str, len);
+    }
+    return StringPiece16();
+}
+
+class StringBuilder {
+public:
+    StringBuilder& append(const StringPiece16& str);
+    const std::u16string& str() const;
+    const std::string& error() const;
+    operator bool() const;
+
+private:
+    std::u16string mStr;
+    bool mQuote = false;
+    bool mTrailingSpace = false;
+    std::string mError;
+};
+
+inline const std::u16string& StringBuilder::str() const {
+    return mStr;
+}
+
+inline const std::string& StringBuilder::error() const {
+    return mError;
+}
+
+inline StringBuilder::operator bool() const {
+    return mError.empty();
+}
+
+/**
+ * Converts a UTF8 string to a UTF16 string.
+ */
+std::u16string utf8ToUtf16(const StringPiece& utf8);
+std::string utf16ToUtf8(const StringPiece16& utf8);
+
+/**
+ * Writes the entire BigBuffer to the output stream.
+ */
+bool writeAll(std::ostream& out, const BigBuffer& buffer);
+
+/*
+ * Copies the entire BigBuffer into a single buffer.
+ */
+std::unique_ptr<uint8_t[]> copy(const BigBuffer& buffer);
+
+/**
+ * A Tokenizer implemented as an iterable collection. It does not allocate
+ * any memory on the heap nor use standard containers.
+ */
+template <typename Char>
+class Tokenizer {
+public:
+    class iterator {
+    public:
+        iterator(const iterator&) = default;
+        iterator& operator=(const iterator&) = default;
+
+        iterator& operator++();
+        BasicStringPiece<Char> operator*();
+        bool operator==(const iterator& rhs) const;
+        bool operator!=(const iterator& rhs) const;
+
+    private:
+        friend class Tokenizer<Char>;
+
+        iterator(BasicStringPiece<Char> s, Char sep, BasicStringPiece<Char> tok);
+
+        BasicStringPiece<Char> str;
+        Char separator;
+        BasicStringPiece<Char> token;
+    };
+
+    Tokenizer(BasicStringPiece<Char> str, Char sep);
+    iterator begin();
+    iterator end();
+
+private:
+    const iterator mBegin;
+    const iterator mEnd;
+};
+
+template <typename Char>
+inline Tokenizer<Char> tokenize(BasicStringPiece<Char> str, Char sep) {
+    return Tokenizer<Char>(str, sep);
+}
+
+template <typename Char>
+typename Tokenizer<Char>::iterator& Tokenizer<Char>::iterator::operator++() {
+    const Char* start = token.end();
+    const Char* end = str.end();
+    if (start == end) {
+        token.assign(token.end(), 0);
+        return *this;
+    }
+
+    start += 1;
+    const Char* current = start;
+    while (current != end) {
+        if (*current == separator) {
+            token.assign(start, current - start);
+            return *this;
+        }
+        ++current;
+    }
+    token.assign(start, end - start);
+    return *this;
+}
+
+template <typename Char>
+inline BasicStringPiece<Char> Tokenizer<Char>::iterator::operator*() {
+    return token;
+}
+
+template <typename Char>
+inline bool Tokenizer<Char>::iterator::operator==(const iterator& rhs) const {
+    // We check equality here a bit differently.
+    // We need to know that the addresses are the same.
+    return token.begin() == rhs.token.begin() && token.end() == rhs.token.end();
+}
+
+template <typename Char>
+inline bool Tokenizer<Char>::iterator::operator!=(const iterator& rhs) const {
+    return !(*this == rhs);
+}
+
+template <typename Char>
+inline Tokenizer<Char>::iterator::iterator(BasicStringPiece<Char> s, Char sep,
+                                           BasicStringPiece<Char> tok) :
+        str(s), separator(sep), token(tok) {
+}
+
+template <typename Char>
+inline typename Tokenizer<Char>::iterator Tokenizer<Char>::begin() {
+    return mBegin;
+}
+
+template <typename Char>
+inline typename Tokenizer<Char>::iterator Tokenizer<Char>::end() {
+    return mEnd;
+}
+
+template <typename Char>
+inline Tokenizer<Char>::Tokenizer(BasicStringPiece<Char> str, Char sep) :
+        mBegin(++iterator(str, sep, BasicStringPiece<Char>(str.begin() - 1, 0))),
+        mEnd(str, sep, BasicStringPiece<Char>(str.end(), 0)) {
+}
+
+} // namespace util
+
+/**
+ * Stream operator for functions. Calls the function with the stream as an argument.
+ * In the aapt namespace for lookup.
+ */
+inline ::std::ostream& operator<<(::std::ostream& out,
+                                  ::std::function<::std::ostream&(::std::ostream&)> f) {
+    return f(out);
+}
+
+} // namespace aapt
+
+#endif // AAPT_UTIL_H
diff --git a/tools/aapt2/Util_test.cpp b/tools/aapt2/Util_test.cpp
new file mode 100644
index 0000000..7dbe7e0
--- /dev/null
+++ b/tools/aapt2/Util_test.cpp
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include <string>
+
+#include "StringPiece.h"
+#include "Util.h"
+
+namespace aapt {
+
+TEST(UtilTest, TrimOnlyWhitespace) {
+    const std::u16string full = u"\n        ";
+
+    StringPiece16 trimmed = util::trimWhitespace(full);
+    EXPECT_TRUE(trimmed.empty());
+    EXPECT_EQ(0u, trimmed.size());
+}
+
+TEST(UtilTest, StringEndsWith) {
+    EXPECT_TRUE(util::stringEndsWith("hello.xml", ".xml"));
+}
+
+TEST(UtilTest, StringBuilderWhitespaceRemoval) {
+    EXPECT_EQ(StringPiece16(u"hey guys this is so cool"),
+            util::StringBuilder().append(u"    hey guys ")
+                                 .append(u" this is so cool ")
+                                 .str());
+
+    EXPECT_EQ(StringPiece16(u" wow,  so many \t spaces. what?"),
+            util::StringBuilder().append(u" \" wow,  so many \t ")
+                                 .append(u"spaces. \"what? ")
+                                 .str());
+
+    EXPECT_EQ(StringPiece16(u"where is the pie?"),
+            util::StringBuilder().append(u"  where \t ")
+                                 .append(u" \nis the "" pie?")
+                                 .str());
+}
+
+TEST(UtilTest, StringBuilderEscaping) {
+    EXPECT_EQ(StringPiece16(u"hey guys\n this \t is so\\ cool"),
+            util::StringBuilder().append(u"    hey guys\\n ")
+                                 .append(u" this \\t is so\\\\ cool ")
+                                 .str());
+
+    EXPECT_EQ(StringPiece16(u"@?#\\\'"),
+            util::StringBuilder().append(u"\\@\\?\\#\\\\\\'")
+                                 .str());
+}
+
+TEST(UtilTest, StringBuilderMisplacedQuote) {
+    util::StringBuilder builder{};
+    EXPECT_FALSE(builder.append(u"they're coming!"));
+}
+
+TEST(UtilTest, StringBuilderUnicodeCodes) {
+    EXPECT_EQ(StringPiece16(u"\u00AF\u0AF0 woah"),
+            util::StringBuilder().append(u"\\u00AF\\u0AF0 woah")
+                                 .str());
+
+    EXPECT_FALSE(util::StringBuilder().append(u"\\u00 yo"));
+}
+
+TEST(UtilTest, TokenizeInput) {
+    auto tokenizer = util::tokenize(StringPiece16(u"this| is|the|end"), u'|');
+    auto iter = tokenizer.begin();
+    ASSERT_EQ(*iter, StringPiece16(u"this"));
+    ++iter;
+    ASSERT_EQ(*iter, StringPiece16(u" is"));
+    ++iter;
+    ASSERT_EQ(*iter, StringPiece16(u"the"));
+    ++iter;
+    ASSERT_EQ(*iter, StringPiece16(u"end"));
+    ++iter;
+    ASSERT_EQ(tokenizer.end(), iter);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/XliffXmlPullParser.cpp b/tools/aapt2/XliffXmlPullParser.cpp
new file mode 100644
index 0000000..f0950a3
--- /dev/null
+++ b/tools/aapt2/XliffXmlPullParser.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#include "XliffXmlPullParser.h"
+
+#include <string>
+
+namespace aapt {
+
+XliffXmlPullParser::XliffXmlPullParser(const std::shared_ptr<XmlPullParser>& parser) :
+        mParser(parser) {
+}
+
+XmlPullParser::Event XliffXmlPullParser::next() {
+    while (XmlPullParser::isGoodEvent(mParser->next())) {
+        Event event = mParser->getEvent();
+        if (event != Event::kStartElement && event != Event::kEndElement) {
+            break;
+        }
+
+        if (mParser->getElementNamespace() !=
+                u"urn:oasis:names:tc:xliff:document:1.2") {
+            break;
+        }
+
+        const std::u16string& name = mParser->getElementName();
+        if (name != u"bpt"
+                && name != u"ept"
+                && name != u"it"
+                && name != u"ph"
+                && name != u"g"
+                && name != u"bx"
+                && name != u"ex"
+                && name != u"x") {
+            break;
+        }
+
+        // We hit a tag that was ignored, so get the next event.
+    }
+    return mParser->getEvent();
+}
+
+XmlPullParser::Event XliffXmlPullParser::getEvent() const {
+    return mParser->getEvent();
+}
+
+const std::string& XliffXmlPullParser::getLastError() const {
+    return mParser->getLastError();
+}
+
+const std::u16string& XliffXmlPullParser::getComment() const {
+    return mParser->getComment();
+}
+
+size_t XliffXmlPullParser::getLineNumber() const {
+    return mParser->getLineNumber();
+}
+
+size_t XliffXmlPullParser::getDepth() const {
+    return mParser->getDepth();
+}
+
+const std::u16string& XliffXmlPullParser::getText() const {
+    return mParser->getText();
+}
+
+const std::u16string& XliffXmlPullParser::getNamespacePrefix() const {
+    return mParser->getNamespacePrefix();
+}
+
+const std::u16string& XliffXmlPullParser::getNamespaceUri() const {
+    return mParser->getNamespaceUri();
+}
+
+const std::u16string& XliffXmlPullParser::getElementNamespace() const {
+    return mParser->getElementNamespace();
+}
+
+const std::u16string& XliffXmlPullParser::getElementName() const {
+    return mParser->getElementName();
+}
+
+size_t XliffXmlPullParser::getAttributeCount() const {
+    return mParser->getAttributeCount();
+}
+
+XmlPullParser::const_iterator XliffXmlPullParser::beginAttributes() const {
+    return mParser->beginAttributes();
+}
+
+XmlPullParser::const_iterator XliffXmlPullParser::endAttributes() const {
+    return mParser->endAttributes();
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/XliffXmlPullParser.h b/tools/aapt2/XliffXmlPullParser.h
new file mode 100644
index 0000000..d362521
--- /dev/null
+++ b/tools/aapt2/XliffXmlPullParser.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_XLIFF_XML_PULL_PARSER_H
+#define AAPT_XLIFF_XML_PULL_PARSER_H
+
+#include "XmlPullParser.h"
+
+#include <string>
+
+namespace aapt {
+
+/**
+ * Strips xliff elements and provides the caller with a view of the
+ * underlying XML without xliff.
+ */
+class XliffXmlPullParser : public XmlPullParser {
+public:
+    XliffXmlPullParser(const std::shared_ptr<XmlPullParser>& parser);
+    XliffXmlPullParser(const XliffXmlPullParser& rhs) = delete;
+
+    Event getEvent() const;
+    const std::string& getLastError() const;
+    Event next();
+
+    const std::u16string& getComment() const;
+    size_t getLineNumber() const;
+    size_t getDepth() const;
+
+    const std::u16string& getText() const;
+
+    const std::u16string& getNamespacePrefix() const;
+    const std::u16string& getNamespaceUri() const;
+
+    const std::u16string& getElementNamespace() const;
+    const std::u16string& getElementName() const;
+
+    const_iterator beginAttributes() const;
+    const_iterator endAttributes() const;
+    size_t getAttributeCount() const;
+
+private:
+    std::shared_ptr<XmlPullParser> mParser;
+};
+
+} // namespace aapt
+
+#endif // AAPT_XLIFF_XML_PULL_PARSER_H
diff --git a/tools/aapt2/XliffXmlPullParser_test.cpp b/tools/aapt2/XliffXmlPullParser_test.cpp
new file mode 100644
index 0000000..f9030724
--- /dev/null
+++ b/tools/aapt2/XliffXmlPullParser_test.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#include "SourceXmlPullParser.h"
+#include "XliffXmlPullParser.h"
+
+#include <gtest/gtest.h>
+#include <sstream>
+#include <string>
+
+namespace aapt {
+
+TEST(XliffXmlPullParserTest, IgnoreXliffTags) {
+    std::stringstream input;
+    input << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl
+          << "<resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">" << std::endl
+          << "<string name=\"foo\">"
+          << "Hey <xliff:g><xliff:it>there</xliff:it></xliff:g> world</string>" << std::endl
+          << "</resources>" << std::endl;
+    std::shared_ptr<XmlPullParser> sourceParser = std::make_shared<SourceXmlPullParser>(input);
+    XliffXmlPullParser parser(sourceParser);
+    EXPECT_EQ(XmlPullParser::Event::kStartDocument, parser.getEvent());
+
+    EXPECT_EQ(XmlPullParser::Event::kStartNamespace, parser.next());
+    EXPECT_EQ(parser.getNamespaceUri(), u"urn:oasis:names:tc:xliff:document:1.2");
+    EXPECT_EQ(parser.getNamespacePrefix(), u"xliff");
+
+    EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.next());
+    EXPECT_EQ(parser.getElementNamespace(), u"");
+    EXPECT_EQ(parser.getElementName(), u"resources");
+    EXPECT_EQ(XmlPullParser::Event::kText, parser.next()); // Account for newline/whitespace.
+
+    EXPECT_EQ(XmlPullParser::Event::kStartElement, parser.next());
+    EXPECT_EQ(parser.getElementNamespace(), u"");
+    EXPECT_EQ(parser.getElementName(), u"string");
+
+    EXPECT_EQ(XmlPullParser::Event::kText, parser.next());
+    EXPECT_EQ(parser.getText(), u"Hey ");
+
+    EXPECT_EQ(XmlPullParser::Event::kText, parser.next());
+    EXPECT_EQ(parser.getText(), u"there");
+
+    EXPECT_EQ(XmlPullParser::Event::kText, parser.next());
+    EXPECT_EQ(parser.getText(), u" world");
+
+    EXPECT_EQ(XmlPullParser::Event::kEndElement, parser.next());
+    EXPECT_EQ(parser.getElementNamespace(), u"");
+    EXPECT_EQ(parser.getElementName(), u"string");
+    EXPECT_EQ(XmlPullParser::Event::kText, parser.next()); // Account for newline/whitespace.
+
+    EXPECT_EQ(XmlPullParser::Event::kEndElement, parser.next());
+    EXPECT_EQ(parser.getElementNamespace(), u"");
+    EXPECT_EQ(parser.getElementName(), u"resources");
+
+    EXPECT_EQ(XmlPullParser::Event::kEndNamespace, parser.next());
+    EXPECT_EQ(parser.getNamespacePrefix(), u"xliff");
+    EXPECT_EQ(parser.getNamespaceUri(), u"urn:oasis:names:tc:xliff:document:1.2");
+
+    EXPECT_EQ(XmlPullParser::Event::kEndDocument, parser.next());
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/XmlFlattener.cpp b/tools/aapt2/XmlFlattener.cpp
new file mode 100644
index 0000000..b6ca6d5
--- /dev/null
+++ b/tools/aapt2/XmlFlattener.cpp
@@ -0,0 +1,437 @@
+/*
+ * 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.
+ */
+
+#include "BigBuffer.h"
+#include "Logger.h"
+#include "Maybe.h"
+#include "Resolver.h"
+#include "Resource.h"
+#include "ResourceParser.h"
+#include "ResourceValues.h"
+#include "SdkConstants.h"
+#include "Source.h"
+#include "StringPool.h"
+#include "Util.h"
+#include "XmlFlattener.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <limits>
+#include <map>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+struct AttributeValueFlattener : ValueVisitor {
+    struct Args : ValueVisitorArgs {
+        Args(std::shared_ptr<Resolver> r, SourceLogger& s, android::Res_value& oV,
+                std::shared_ptr<XmlPullParser> p, bool& e, StringPool::Ref& rV,
+                std::vector<std::pair<StringPool::Ref, android::ResStringPool_ref*>>& sR) :
+                resolver(r), logger(s), outValue(oV), parser(p), error(e), rawValue(rV),
+                stringRefs(sR) {
+        }
+
+        std::shared_ptr<Resolver> resolver;
+        SourceLogger& logger;
+        android::Res_value& outValue;
+        std::shared_ptr<XmlPullParser> parser;
+        bool& error;
+        StringPool::Ref& rawValue;
+        std::vector<std::pair<StringPool::Ref, android::ResStringPool_ref*>>& stringRefs;
+    };
+
+    void visit(Reference& reference, ValueVisitorArgs& a) override {
+        Args& args = static_cast<Args&>(a);
+
+        Maybe<ResourceId> result = args.resolver->findId(reference.name);
+        if (!result || !result.value().isValid()) {
+            args.logger.error(args.parser->getLineNumber())
+                    << "unresolved reference '"
+                    << reference.name
+                    << "'."
+                    << std::endl;
+            args.error = true;
+        } else {
+            reference.id = result.value();
+            reference.flatten(args.outValue);
+        }
+    }
+
+    void visit(String& string, ValueVisitorArgs& a) override {
+        Args& args = static_cast<Args&>(a);
+
+        args.outValue.dataType = android::Res_value::TYPE_STRING;
+        args.stringRefs.emplace_back(args.rawValue,
+                reinterpret_cast<android::ResStringPool_ref*>(&args.outValue.data));
+    }
+
+    void visitItem(Item& item, ValueVisitorArgs& a) override {
+        Args& args = static_cast<Args&>(a);
+        item.flatten(args.outValue);
+    }
+};
+
+struct XmlAttribute {
+    uint32_t resourceId;
+    const XmlPullParser::Attribute* xmlAttr;
+    const Attribute* attr;
+    StringPool::Ref nameRef;
+};
+
+static bool lessAttributeId(const XmlAttribute& a, uint32_t id) {
+    return a.resourceId < id;
+}
+
+XmlFlattener::XmlFlattener(const std::shared_ptr<Resolver>& resolver) : mResolver(resolver) {
+}
+
+/**
+ * Reads events from the parser and writes to a BigBuffer. The binary XML file
+ * expects the StringPool to appear first, but we haven't collected the strings yet. We
+ * write to a temporary BigBuffer while parsing the input, adding strings we encounter
+ * to the StringPool. At the end, we write the StringPool to the given BigBuffer and
+ * then move the data from the temporary BigBuffer into the given one. This incurs no
+ * copies as the given BigBuffer simply takes ownership of the data.
+ */
+Maybe<size_t> XmlFlattener::flatten(const Source& source,
+                                    const std::shared_ptr<XmlPullParser>& parser,
+                                    BigBuffer* outBuffer, Options options) {
+    SourceLogger logger(source);
+    StringPool pool;
+    bool error = false;
+
+    size_t smallestStrippedAttributeSdk = std::numeric_limits<size_t>::max();
+
+    // Attribute names are stored without packages, but we use
+    // their StringPool index to lookup their resource IDs.
+    // This will cause collisions, so we can't dedupe
+    // attribute names from different packages. We use separate
+    // pools that we later combine.
+    std::map<std::u16string, StringPool> packagePools;
+
+    // Attribute resource IDs are stored in the same order
+    // as the attribute names appear in the StringPool.
+    // Since the StringPool contains more than just attribute
+    // names, to maintain a tight packing of resource IDs,
+    // we must ensure that attribute names appear first
+    // in our StringPool. For this, we assign a low priority
+    // (0xffffffff) to non-attribute strings. Attribute
+    // names will be stored along with a priority equal
+    // to their resource ID so that they are ordered.
+    StringPool::Context lowPriority { 0xffffffffu };
+
+    // Once we sort the StringPool, we can assign the updated indices
+    // to the correct data locations.
+    std::vector<std::pair<StringPool::Ref, android::ResStringPool_ref*>> stringRefs;
+
+    // Since we don't know the size of the final StringPool, we write to this
+    // temporary BigBuffer, which we will append to outBuffer later.
+    BigBuffer out(1024);
+    while (XmlPullParser::isGoodEvent(parser->next())) {
+        XmlPullParser::Event event = parser->getEvent();
+        switch (event) {
+            case XmlPullParser::Event::kStartNamespace:
+            case XmlPullParser::Event::kEndNamespace: {
+                const size_t startIndex = out.size();
+                android::ResXMLTree_node* node = out.nextBlock<android::ResXMLTree_node>();
+                if (event == XmlPullParser::Event::kStartNamespace) {
+                    node->header.type = android::RES_XML_START_NAMESPACE_TYPE;
+                } else {
+                    node->header.type = android::RES_XML_END_NAMESPACE_TYPE;
+                }
+
+                node->header.headerSize = sizeof(*node);
+                node->lineNumber = parser->getLineNumber();
+                node->comment.index = -1;
+
+                android::ResXMLTree_namespaceExt* ns =
+                        out.nextBlock<android::ResXMLTree_namespaceExt>();
+                stringRefs.emplace_back(
+                        pool.makeRef(parser->getNamespacePrefix(), lowPriority), &ns->prefix);
+                stringRefs.emplace_back(
+                        pool.makeRef(parser->getNamespaceUri(), lowPriority), &ns->uri);
+
+                out.align4();
+                node->header.size = out.size() - startIndex;
+                break;
+            }
+
+            case XmlPullParser::Event::kStartElement: {
+                const size_t startIndex = out.size();
+                android::ResXMLTree_node* node = out.nextBlock<android::ResXMLTree_node>();
+                node->header.type = android::RES_XML_START_ELEMENT_TYPE;
+                node->header.headerSize = sizeof(*node);
+                node->lineNumber = parser->getLineNumber();
+                node->comment.index = -1;
+
+                android::ResXMLTree_attrExt* elem = out.nextBlock<android::ResXMLTree_attrExt>();
+                stringRefs.emplace_back(
+                        pool.makeRef(parser->getElementNamespace(), lowPriority), &elem->ns);
+                stringRefs.emplace_back(
+                        pool.makeRef(parser->getElementName(), lowPriority), &elem->name);
+                elem->attributeStart = sizeof(*elem);
+                elem->attributeSize = sizeof(android::ResXMLTree_attribute);
+
+                // The resource system expects attributes to be sorted by resource ID.
+                std::vector<XmlAttribute> sortedAttributes;
+                uint32_t nextAttributeId = 0;
+                const auto endAttrIter = parser->endAttributes();
+                for (auto attrIter = parser->beginAttributes();
+                     attrIter != endAttrIter;
+                     ++attrIter) {
+                    uint32_t id;
+                    StringPool::Ref nameRef;
+                    const Attribute* attr = nullptr;
+                    if (attrIter->namespaceUri.empty()) {
+                        // Attributes that have no resource ID (because they don't belong to a
+                        // package) should appear after those that do have resource IDs. Assign
+                        // them some/ integer value that will appear after.
+                        id = 0x80000000u | nextAttributeId++;
+                        nameRef = pool.makeRef(attrIter->name, StringPool::Context{ id });
+                    } else {
+                        StringPiece16 package;
+                        if (attrIter->namespaceUri == u"http://schemas.android.com/apk/res-auto") {
+                            package = mResolver->getDefaultPackage();
+                        } else {
+                            // TODO(adamlesinski): Extract package from namespace.
+                            // The package name appears like so:
+                            // http://schemas.android.com/apk/res/<package name>
+                            package = u"android";
+                        }
+
+                        // Find the Attribute object via our Resolver.
+                        ResourceName attrName = {
+                                package.toString(), ResourceType::kAttr, attrIter->name };
+                        Maybe<Resolver::Entry> result = mResolver->findAttribute(attrName);
+                        if (!result || !result.value().id.isValid()) {
+                            logger.error(parser->getLineNumber())
+                                    << "unresolved attribute '"
+                                    << attrName
+                                    << "'."
+                                    << std::endl;
+                            error = true;
+                            continue;
+                        }
+
+                        if (!result.value().attr) {
+                            logger.error(parser->getLineNumber())
+                                    << "not a valid attribute '"
+                                    << attrName
+                                    << "'."
+                                    << std::endl;
+                            error = true;
+                            continue;
+                        }
+
+                        if (options.maxSdkAttribute && package == u"android") {
+                            size_t sdkVersion = findAttributeSdkLevel(attrIter->name);
+                            if (sdkVersion > options.maxSdkAttribute.value()) {
+                                // We will silently omit this attribute
+                                smallestStrippedAttributeSdk =
+                                        std::min(smallestStrippedAttributeSdk, sdkVersion);
+                                continue;
+                            }
+                        }
+
+                        id = result.value().id.id;
+                        attr = result.value().attr;
+
+                        // Put the attribute name into a package specific pool, since we don't
+                        // want to collapse names from different packages.
+                        nameRef = packagePools[package.toString()].makeRef(
+                                attrIter->name, StringPool::Context{ id });
+                    }
+
+                    // Insert the attribute into the sorted vector.
+                    auto iter = std::lower_bound(sortedAttributes.begin(), sortedAttributes.end(),
+                                                 id, lessAttributeId);
+                    sortedAttributes.insert(iter, XmlAttribute{ id, &*attrIter, attr, nameRef });
+                }
+
+                if (error) {
+                    break;
+                }
+
+                // Now that we have filtered out some attributes, get the final count.
+                elem->attributeCount = sortedAttributes.size();
+
+                // Flatten the sorted attributes.
+                for (auto entry : sortedAttributes) {
+                    android::ResXMLTree_attribute* attr =
+                            out.nextBlock<android::ResXMLTree_attribute>();
+                    stringRefs.emplace_back(
+                            pool.makeRef(entry.xmlAttr->namespaceUri, lowPriority), &attr->ns);
+                    StringPool::Ref rawValueRef = pool.makeRef(entry.xmlAttr->value, lowPriority);
+                    stringRefs.emplace_back(rawValueRef, &attr->rawValue);
+                    stringRefs.emplace_back(entry.nameRef, &attr->name);
+
+                    if (entry.attr) {
+                        std::unique_ptr<Item> value = ResourceParser::parseItemForAttribute(
+                                entry.xmlAttr->value, *entry.attr, mResolver->getDefaultPackage());
+                        if (value) {
+                            AttributeValueFlattener flattener;
+                            value->accept(flattener, AttributeValueFlattener::Args{
+                                    mResolver,
+                                    logger,
+                                    attr->typedValue,
+                                    parser,
+                                    error,
+                                    rawValueRef,
+                                    stringRefs
+                            });
+                        } else if (!(entry.attr->typeMask & android::ResTable_map::TYPE_STRING)) {
+                            logger.error(parser->getLineNumber())
+                                    << "'"
+                                    << *rawValueRef
+                                    << "' is not compatible with attribute "
+                                    << *entry.attr
+                                    << "."
+                                    << std::endl;
+                            error = true;
+                        } else {
+                            attr->typedValue.dataType = android::Res_value::TYPE_STRING;
+                            stringRefs.emplace_back(rawValueRef,
+                                    reinterpret_cast<android::ResStringPool_ref*>(
+                                            &attr->typedValue.data));
+                        }
+                    } else {
+                        attr->typedValue.dataType = android::Res_value::TYPE_STRING;
+                        stringRefs.emplace_back(rawValueRef,
+                                reinterpret_cast<android::ResStringPool_ref*>(
+                                        &attr->typedValue.data));
+                    }
+                    attr->typedValue.size = sizeof(attr->typedValue);
+                }
+
+                out.align4();
+                node->header.size = out.size() - startIndex;
+                break;
+            }
+
+            case XmlPullParser::Event::kEndElement: {
+                const size_t startIndex = out.size();
+                android::ResXMLTree_node* node = out.nextBlock<android::ResXMLTree_node>();
+                node->header.type = android::RES_XML_END_ELEMENT_TYPE;
+                node->header.headerSize = sizeof(*node);
+                node->lineNumber = parser->getLineNumber();
+                node->comment.index = -1;
+
+                android::ResXMLTree_endElementExt* elem =
+                        out.nextBlock<android::ResXMLTree_endElementExt>();
+                stringRefs.emplace_back(
+                        pool.makeRef(parser->getElementNamespace(), lowPriority), &elem->ns);
+                stringRefs.emplace_back(
+                        pool.makeRef(parser->getElementName(), lowPriority), &elem->name);
+
+                out.align4();
+                node->header.size = out.size() - startIndex;
+                break;
+            }
+
+            case XmlPullParser::Event::kText: {
+                StringPiece16 text = util::trimWhitespace(parser->getText());
+                if (text.empty()) {
+                    break;
+                }
+
+                const size_t startIndex = out.size();
+                android::ResXMLTree_node* node = out.nextBlock<android::ResXMLTree_node>();
+                node->header.type = android::RES_XML_CDATA_TYPE;
+                node->header.headerSize = sizeof(*node);
+                node->lineNumber = parser->getLineNumber();
+                node->comment.index = -1;
+
+                android::ResXMLTree_cdataExt* elem = out.nextBlock<android::ResXMLTree_cdataExt>();
+                stringRefs.emplace_back(pool.makeRef(text, lowPriority), &elem->data);
+
+                out.align4();
+                node->header.size = out.size() - startIndex;
+                break;
+            }
+
+            default:
+                break;
+        }
+
+    }
+    out.align4();
+
+    if (error) {
+        return {};
+    }
+
+    if (parser->getEvent() == XmlPullParser::Event::kBadDocument) {
+        logger.error(parser->getLineNumber())
+                << parser->getLastError()
+                << std::endl;
+        return {};
+    }
+
+    // Merge the package pools into the main pool.
+    for (auto& packagePoolEntry : packagePools) {
+        pool.merge(std::move(packagePoolEntry.second));
+    }
+
+    // Sort so that attribute resource IDs show up first.
+    pool.sort([](const StringPool::Entry& a, const StringPool::Entry& b) -> bool {
+        return a.context.priority < b.context.priority;
+    });
+
+    // Now we flatten the string pool references into the correct places.
+    for (const auto& refEntry : stringRefs) {
+        refEntry.second->index = refEntry.first.getIndex();
+    }
+
+    // Write the XML header.
+    const size_t beforeXmlTreeIndex = outBuffer->size();
+    android::ResXMLTree_header* header = outBuffer->nextBlock<android::ResXMLTree_header>();
+    header->header.type = android::RES_XML_TYPE;
+    header->header.headerSize = sizeof(*header);
+
+    // Write the array of resource IDs, indexed by StringPool order.
+    const size_t beforeResIdMapIndex = outBuffer->size();
+    android::ResChunk_header* resIdMapChunk = outBuffer->nextBlock<android::ResChunk_header>();
+    resIdMapChunk->type = android::RES_XML_RESOURCE_MAP_TYPE;
+    resIdMapChunk->headerSize = sizeof(*resIdMapChunk);
+    for (const auto& str : pool) {
+        ResourceId id { str->context.priority };
+        if (!id.isValid()) {
+            // When we see the first non-resource ID,
+            // we're done.
+            break;
+        }
+
+        uint32_t* flatId = outBuffer->nextBlock<uint32_t>();
+        *flatId = id.id;
+    }
+    resIdMapChunk->size = outBuffer->size() - beforeResIdMapIndex;
+
+    // Flatten the StringPool.
+    StringPool::flattenUtf8(outBuffer, pool);
+
+    // Move the temporary BigBuffer into outBuffer->
+    outBuffer->appendBuffer(std::move(out));
+
+    header->header.size = outBuffer->size() - beforeXmlTreeIndex;
+
+    if (smallestStrippedAttributeSdk == std::numeric_limits<size_t>::max()) {
+        // Nothing was stripped
+        return 0u;
+    }
+    return smallestStrippedAttributeSdk;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/XmlFlattener.h b/tools/aapt2/XmlFlattener.h
new file mode 100644
index 0000000..abf64ab
--- /dev/null
+++ b/tools/aapt2/XmlFlattener.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_XML_FLATTENER_H
+#define AAPT_XML_FLATTENER_H
+
+#include "BigBuffer.h"
+#include "Maybe.h"
+#include "Resolver.h"
+#include "Source.h"
+#include "XmlPullParser.h"
+
+namespace aapt {
+
+/**
+ * Flattens an XML file into a binary representation parseable by
+ * the Android resource system. References to resources are checked
+ * and string values are transformed to typed data where possible.
+ */
+class XmlFlattener {
+public:
+    struct Options {
+        /**
+         * If set, tells the XmlFlattener to strip out
+         * attributes that have been introduced after
+         * max SDK.
+         */
+        Maybe<size_t> maxSdkAttribute;
+    };
+
+    /**
+     * Creates a flattener with a Resolver to resolve references
+     * and attributes.
+     */
+    XmlFlattener(const std::shared_ptr<Resolver>& resolver);
+
+    XmlFlattener(const XmlFlattener&) = delete; // Not copyable.
+
+    /**
+     * Flatten an XML file, reading from the XML parser and writing to the
+     * BigBuffer. The source object is mainly for logging errors. If the
+     * function succeeds, returns the smallest SDK version of an attribute that
+     * was stripped out. If no attributes were stripped out, the return value
+     * is 0.
+     */
+    Maybe<size_t> flatten(const Source& source, const std::shared_ptr<XmlPullParser>& parser,
+                          BigBuffer* outBuffer, Options options);
+
+private:
+    std::shared_ptr<Resolver> mResolver;
+};
+
+} // namespace aapt
+
+#endif // AAPT_XML_FLATTENER_H
diff --git a/tools/aapt2/XmlFlattener_test.cpp b/tools/aapt2/XmlFlattener_test.cpp
new file mode 100644
index 0000000..79030be
--- /dev/null
+++ b/tools/aapt2/XmlFlattener_test.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#include "Resolver.h"
+#include "ResourceTable.h"
+#include "ResourceValues.h"
+#include "SourceXmlPullParser.h"
+#include "Util.h"
+#include "XmlFlattener.h"
+
+#include <androidfw/AssetManager.h>
+#include <androidfw/ResourceTypes.h>
+#include <gtest/gtest.h>
+#include <sstream>
+#include <string>
+
+namespace aapt {
+
+constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+
+class XmlFlattenerTest : public ::testing::Test {
+public:
+    virtual void SetUp() override {
+        std::shared_ptr<ResourceTable> table = std::make_shared<ResourceTable>();
+        table->setPackage(u"android");
+        table->setPackageId(0x01);
+
+        table->addResource(ResourceName{ {}, ResourceType::kAttr, u"id" },
+                           ResourceId{ 0x01010000 }, {}, {},
+                           util::make_unique<Attribute>(false, android::ResTable_map::TYPE_ANY));
+
+        table->addResource(ResourceName{ {}, ResourceType::kId, u"test" },
+                           ResourceId{ 0x01020000 }, {}, {}, util::make_unique<Id>());
+
+        mFlattener = std::make_shared<XmlFlattener>(
+                std::make_shared<Resolver>(table, std::make_shared<android::AssetManager>()));
+    }
+
+    ::testing::AssertionResult testFlatten(std::istream& in, android::ResXMLTree* outTree) {
+        std::stringstream input(kXmlPreamble);
+        input << in.rdbuf() << std::endl;
+        std::shared_ptr<XmlPullParser> xmlParser = std::make_shared<SourceXmlPullParser>(input);
+        BigBuffer outBuffer(1024);
+        if (!mFlattener->flatten(Source{ "test" }, xmlParser, &outBuffer, {})) {
+            return ::testing::AssertionFailure();
+        }
+
+        std::unique_ptr<uint8_t[]> data = util::copy(outBuffer);
+        if (outTree->setTo(data.get(), outBuffer.size(), true) != android::NO_ERROR) {
+            return ::testing::AssertionFailure();
+        }
+        return ::testing::AssertionSuccess();
+    }
+
+    std::shared_ptr<XmlFlattener> mFlattener;
+};
+
+TEST_F(XmlFlattenerTest, ParseSimpleView) {
+    std::stringstream input;
+    input << "<View xmlns:android=\"http://schemas.android.com/apk/res/android\"" << std::endl
+          << "      android:id=\"@id/test\">" << std::endl
+          << "</View>" << std::endl;
+
+    android::ResXMLTree tree;
+    ASSERT_TRUE(testFlatten(input, &tree));
+
+    while (tree.next() != android::ResXMLTree::END_DOCUMENT) {
+        ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
+    }
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/XmlPullParser.h b/tools/aapt2/XmlPullParser.h
new file mode 100644
index 0000000..c667df2
--- /dev/null
+++ b/tools/aapt2/XmlPullParser.h
@@ -0,0 +1,234 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_XML_PULL_PARSER_H
+#define AAPT_XML_PULL_PARSER_H
+
+#include <algorithm>
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "StringPiece.h"
+
+namespace aapt {
+
+class XmlPullParser {
+public:
+    enum class Event {
+        kBadDocument,
+        kStartDocument,
+        kEndDocument,
+
+        kStartNamespace,
+        kEndNamespace,
+        kStartElement,
+        kEndElement,
+        kText,
+        kComment,
+    };
+
+    static void skipCurrentElement(XmlPullParser* parser);
+    static bool isGoodEvent(Event event);
+
+    virtual ~XmlPullParser() {}
+
+    /**
+     * Returns the current event that is being processed.
+     */
+    virtual Event getEvent() const = 0;
+
+    virtual const std::string& getLastError() const = 0;
+
+    /**
+     * Note, unlike XmlPullParser, the first call to next() will return
+     * StartElement of the first element.
+     */
+    virtual Event next() = 0;
+
+    //
+    // These are available for all nodes.
+    //
+
+    virtual const std::u16string& getComment() const = 0;
+    virtual size_t getLineNumber() const = 0;
+    virtual size_t getDepth() const = 0;
+
+    /**
+     * Returns the character data for a Text event.
+     */
+    virtual const std::u16string& getText() const = 0;
+
+    /**
+     * Namespace prefix is available for StartNamespace and EndNamespace.
+     */
+    virtual const std::u16string& getNamespacePrefix() const = 0;
+
+    /**
+     * Namespace URI is available for StartNamespace.
+     */
+    virtual const std::u16string& getNamespaceUri() const = 0;
+
+    //
+    // These are available for StartElement and EndElement.
+    //
+
+    virtual const std::u16string& getElementNamespace() const = 0;
+    virtual const std::u16string& getElementName() const = 0;
+
+    //
+    // Remaining methods are for retrieving information about attributes
+    // associated with a StartElement.
+    //
+    // Attributes must be in sorted order (according to the less than operator
+    // of struct Attribute).
+    //
+
+    struct Attribute {
+        std::u16string namespaceUri;
+        std::u16string name;
+        std::u16string value;
+
+        int compare(const Attribute& rhs) const;
+        bool operator<(const Attribute& rhs) const;
+        bool operator==(const Attribute& rhs) const;
+        bool operator!=(const Attribute& rhs) const;
+    };
+
+    using const_iterator = std::vector<Attribute>::const_iterator;
+
+    virtual const_iterator beginAttributes() const = 0;
+    virtual const_iterator endAttributes() const = 0;
+    virtual size_t getAttributeCount() const = 0;
+    const_iterator findAttribute(StringPiece16 namespaceUri, StringPiece16 name) const;
+};
+
+/*
+ * Automatically reads up to the end tag of the element it was initialized with
+ * when being destroyed.
+ */
+class AutoFinishElement {
+public:
+    AutoFinishElement(const std::shared_ptr<XmlPullParser>& parser);
+    ~AutoFinishElement();
+
+private:
+    std::shared_ptr<XmlPullParser> mParser;
+    int mDepth;
+};
+
+//
+// Implementation
+//
+
+inline ::std::ostream& operator<<(::std::ostream& out, XmlPullParser::Event event) {
+    switch (event) {
+        case XmlPullParser::Event::kBadDocument: return out << "BadDocument";
+        case XmlPullParser::Event::kStartDocument: return out << "StartDocument";
+        case XmlPullParser::Event::kEndDocument: return out << "EndDocument";
+        case XmlPullParser::Event::kStartNamespace: return out << "StartNamespace";
+        case XmlPullParser::Event::kEndNamespace: return out << "EndNamespace";
+        case XmlPullParser::Event::kStartElement: return out << "StartElement";
+        case XmlPullParser::Event::kEndElement: return out << "EndElement";
+        case XmlPullParser::Event::kText: return out << "Text";
+        case XmlPullParser::Event::kComment: return out << "Comment";
+    }
+    return out;
+}
+
+inline void XmlPullParser::skipCurrentElement(XmlPullParser* parser) {
+    int depth = 1;
+    while (depth > 0) {
+        switch (parser->next()) {
+            case Event::kEndDocument:
+            case Event::kBadDocument:
+                return;
+            case Event::kStartElement:
+                depth++;
+                break;
+            case Event::kEndElement:
+                depth--;
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+inline bool XmlPullParser::isGoodEvent(XmlPullParser::Event event) {
+    return event != Event::kBadDocument && event != Event::kEndDocument;
+}
+
+inline int XmlPullParser::Attribute::compare(const Attribute& rhs) const {
+    int cmp = namespaceUri.compare(rhs.namespaceUri);
+    if (cmp != 0) return cmp;
+    return name.compare(rhs.name);
+}
+
+inline bool XmlPullParser::Attribute::operator<(const Attribute& rhs) const {
+    return compare(rhs) < 0;
+}
+
+inline bool XmlPullParser::Attribute::operator==(const Attribute& rhs) const {
+    return compare(rhs) == 0;
+}
+
+inline bool XmlPullParser::Attribute::operator!=(const Attribute& rhs) const {
+    return compare(rhs) != 0;
+}
+
+inline XmlPullParser::const_iterator XmlPullParser::findAttribute(StringPiece16 namespaceUri,
+                                                                  StringPiece16 name) const {
+    const auto endIter = endAttributes();
+    const auto iter = std::lower_bound(beginAttributes(), endIter,
+            std::pair<StringPiece16, StringPiece16>(namespaceUri, name),
+            [](const Attribute& attr, const std::pair<StringPiece16, StringPiece16>& rhs) -> bool {
+                int cmp = attr.namespaceUri.compare(0, attr.namespaceUri.size(),
+                        rhs.first.data(), rhs.first.size());
+                if (cmp < 0) return true;
+                if (cmp > 0) return false;
+                cmp = attr.name.compare(0, attr.name.size(), rhs.second.data(), rhs.second.size());
+                if (cmp < 0) return true;
+                return false;
+            }
+    );
+
+    if (iter != endIter && namespaceUri == iter->namespaceUri && name == iter->name) {
+        return iter;
+    }
+    return endIter;
+}
+
+inline AutoFinishElement::AutoFinishElement(const std::shared_ptr<XmlPullParser>& parser) :
+        mParser(parser), mDepth(parser->getDepth()) {
+}
+
+inline AutoFinishElement::~AutoFinishElement() {
+    int depth;
+    XmlPullParser::Event event;
+    while ((depth = mParser->getDepth()) >= mDepth &&
+            XmlPullParser::isGoodEvent(event = mParser->getEvent())) {
+        if (depth == mDepth && (event == XmlPullParser::Event::kEndElement ||
+                event == XmlPullParser::Event::kEndNamespace)) {
+            return;
+        }
+        mParser->next();
+    }
+}
+
+} // namespace aapt
+
+#endif // AAPT_XML_PULL_PARSER_H
diff --git a/tools/aapt2/data/AndroidManifest.xml b/tools/aapt2/data/AndroidManifest.xml
new file mode 100644
index 0000000..c017a0d
--- /dev/null
+++ b/tools/aapt2/data/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.app">
+    <application>
+    </application>
+</manifest>
diff --git a/tools/aapt2/data/res/drawable/image.xml b/tools/aapt2/data/res/drawable/image.xml
new file mode 100644
index 0000000..9b38739
--- /dev/null
+++ b/tools/aapt2/data/res/drawable/image.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector />
diff --git a/tools/aapt2/data/res/layout/main.xml b/tools/aapt2/data/res/layout/main.xml
new file mode 100644
index 0000000..e0b55c0
--- /dev/null
+++ b/tools/aapt2/data/res/layout/main.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/view"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+    <View xmlns:app="http://schemas.android.com/apk/res-auto"
+        android:id="@+id/me"
+        android:layout_width="1dp"
+        android:layout_height="match_parent"
+        app:layout_width="false"
+        app:flags="complex|weak"
+        android:colorAccent="#ffffff"/>
+</LinearLayout>
diff --git a/tools/aapt2/data/res/values-v4/styles.xml b/tools/aapt2/data/res/values-v4/styles.xml
new file mode 100644
index 0000000..979a82a
--- /dev/null
+++ b/tools/aapt2/data/res/values-v4/styles.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <style name="App" parent="android:Theme.Material">
+        <item name="android:colorAccent">@color/accent</item>
+        <item name="android:text">Hey</item>
+    </style>
+</resources>
diff --git a/tools/aapt2/data/res/values/colors.xml b/tools/aapt2/data/res/values/colors.xml
new file mode 100644
index 0000000..89db5fb
--- /dev/null
+++ b/tools/aapt2/data/res/values/colors.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="primary">#f44336</color>
+    <color name="primary_dark">#b71c1c</color>
+    <color name="accent">#fdd835</color>
+</resources>
diff --git a/tools/aapt2/data/res/values/styles.xml b/tools/aapt2/data/res/values/styles.xml
new file mode 100644
index 0000000..71ce388
--- /dev/null
+++ b/tools/aapt2/data/res/values/styles.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <style name="App" parent="android:Theme.Material">
+        <item name="android:background">@color/primary</item>
+        <item name="android:colorPrimary">@color/primary</item>
+        <item name="android:colorPrimaryDark">@color/primary_dark</item>
+        <item name="android:colorAccent">@color/accent</item>
+    </style>
+    <attr name="custom" format="reference" />
+    <style name="Pop">
+        <item name="custom">@drawable/image</item>
+    </style>
+    <string name="yo">@string/wow</string>
+
+    <declare-styleable name="View">
+        <attr name="custom" />
+        <attr name="decor">
+            <enum name="no-border" value="0"/>
+            <enum name="border" value="1"/>
+            <enum name="shadow" value="2"/>
+        </attr>
+    </declare-styleable>
+
+</resources>
diff --git a/tools/aapt2/data/res/values/test.xml b/tools/aapt2/data/res/values/test.xml
new file mode 100644
index 0000000..d3ead34
--- /dev/null
+++ b/tools/aapt2/data/res/values/test.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="hooha"><font bgcolor="#ffffff">Hey guys!</font> <xliff:g>My</xliff:g> name is <b>Adam</b>. How <b><i>are</i></b> you?</string>
+    <public name="hooha" type="string" id="0x7f020001"/>
+    <string name="wow">@android:string/ok</string>
+    <public name="image" type="drawable" id="0x7f060000" />
+    <attr name="layout_width" format="boolean" />
+    <attr name="flags">
+        <flag name="complex" value="1" />
+        <flag name="pub" value="2" />
+        <flag name="weak" value="4" />
+    </attr>
+</resources>
diff --git a/tools/aapt2/data/resources.arsc b/tools/aapt2/data/resources.arsc
new file mode 100644
index 0000000..6a416df
--- /dev/null
+++ b/tools/aapt2/data/resources.arsc
Binary files differ
diff --git a/tools/aapt2/data/resources_base.arsc b/tools/aapt2/data/resources_base.arsc
new file mode 100644
index 0000000..f9d0610
--- /dev/null
+++ b/tools/aapt2/data/resources_base.arsc
Binary files differ
diff --git a/tools/aapt2/data/resources_hdpi.arsc b/tools/aapt2/data/resources_hdpi.arsc
new file mode 100644
index 0000000..97232a3
--- /dev/null
+++ b/tools/aapt2/data/resources_hdpi.arsc
Binary files differ
diff --git a/tools/aapt2/process.dot b/tools/aapt2/process.dot
new file mode 100644
index 0000000..a92405d
--- /dev/null
+++ b/tools/aapt2/process.dot
@@ -0,0 +1,92 @@
+digraph aapt {
+    out_package [label="out/default/package.apk"];
+    out_fr_package [label="out/fr/package.apk"];
+    out_table_aligned [label="out/default/resources-aligned.arsc"];
+    out_table_fr_aligned [label="out/fr/resources-aligned.arsc"];
+    out_res_layout_main_xml [label="out/res/layout/main.xml"];
+    out_res_layout_v21_main_xml [color=red,label="out/res/layout-v21/main.xml"];
+    out_res_layout_fr_main_xml [label="out/res/layout-fr/main.xml"];
+    out_res_layout_fr_v21_main_xml [color=red,label="out/res/layout-fr-v21/main.xml"];
+    out_table [label="out/default/resources.arsc"];
+    out_fr_table [label="out/fr/resources.arsc"];
+    out_values_table [label="out/values/resources.arsc"];
+    out_layout_table [label="out/layout/resources.arsc"];
+    out_values_fr_table [label="out/values-fr/resources.arsc"];
+    out_layout_fr_table [label="out/layout-fr/resources.arsc"];
+    res_values_strings_xml [label="res/values/strings.xml"];
+    res_values_attrs_xml [label="res/values/attrs.xml"];
+    res_layout_main_xml [label="res/layout/main.xml"];
+    res_layout_fr_main_xml [label="res/layout-fr/main.xml"];
+    res_values_fr_strings_xml [label="res/values-fr/strings.xml"];
+
+    out_package -> package_default;
+    out_fr_package -> package_fr;
+
+    package_default [shape=box,label="Assemble",color=blue];
+    package_default -> out_table_aligned;
+    package_default -> out_res_layout_main_xml;
+    package_default -> out_res_layout_v21_main_xml [color=red];
+
+    package_fr [shape=box,label="Assemble",color=blue];
+    package_fr -> out_table_fr_aligned;
+    package_fr -> out_res_layout_fr_main_xml;
+    package_fr -> out_res_layout_fr_v21_main_xml [color=red];
+
+    out_table_aligned -> align_tables;
+    out_table_fr_aligned -> align_tables;
+
+    align_tables [shape=box,label="Align",color=blue];
+    align_tables -> out_table;
+    align_tables -> out_fr_table;
+
+    out_table -> link_tables;
+
+    link_tables [shape=box,label="Link",color=blue];
+    link_tables -> out_values_table;
+    link_tables -> out_layout_table;
+
+    out_values_table -> compile_values;
+
+    compile_values [shape=box,label="Collect",color=blue];
+    compile_values -> res_values_strings_xml;
+    compile_values -> res_values_attrs_xml;
+
+    out_layout_table -> collect_xml;
+
+    collect_xml [shape=box,label="Collect",color=blue];
+    collect_xml -> res_layout_main_xml;
+
+    out_fr_table -> link_fr_tables;
+
+    link_fr_tables [shape=box,label="Link",color=blue];
+    link_fr_tables -> out_values_fr_table;
+    link_fr_tables -> out_layout_fr_table;
+
+    out_values_fr_table -> compile_values_fr;
+
+    compile_values_fr [shape=box,label="Compile",color=blue];
+    compile_values_fr -> res_values_fr_strings_xml;
+
+    out_layout_fr_table -> collect_xml_fr;
+
+    collect_xml_fr [shape=box,label="Collect",color=blue];
+    collect_xml_fr -> res_layout_fr_main_xml;
+
+    compile_res_layout_main_xml [shape=box,label="Compile",color=blue];
+
+    out_res_layout_main_xml -> compile_res_layout_main_xml;
+
+    out_res_layout_v21_main_xml -> compile_res_layout_main_xml [color=red];
+
+    compile_res_layout_main_xml -> res_layout_main_xml;
+    compile_res_layout_main_xml -> out_table_aligned;
+
+    compile_res_layout_fr_main_xml [shape=box,label="Compile",color=blue];
+
+    out_res_layout_fr_main_xml -> compile_res_layout_fr_main_xml;
+
+    out_res_layout_fr_v21_main_xml -> compile_res_layout_fr_main_xml [color=red];
+
+    compile_res_layout_fr_main_xml -> res_layout_fr_main_xml;
+    compile_res_layout_fr_main_xml -> out_table_fr_aligned;
+}
diff --git a/tools/aapt2/public_attr_map.py b/tools/aapt2/public_attr_map.py
new file mode 100644
index 0000000..92136a8
--- /dev/null
+++ b/tools/aapt2/public_attr_map.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python
+
+import sys
+import xml.etree.ElementTree as ET
+
+def findSdkLevelForAttribute(id):
+    intId = int(id, 16)
+    packageId = 0x000000ff & (intId >> 24)
+    typeId = 0x000000ff & (intId >> 16)
+    entryId = 0x0000ffff & intId
+
+    if packageId != 0x01 or typeId != 0x01:
+        return 0
+
+    levels = [(1, 0x021c), (2, 0x021d), (3, 0x0269), (4, 0x028d),
+              (5, 0x02ad), (6, 0x02b3), (7, 0x02b5), (8, 0x02bd),
+              (9, 0x02cb), (11, 0x0361), (12, 0x0366), (13, 0x03a6),
+              (16, 0x03ae), (17, 0x03cc), (18, 0x03da), (19, 0x03f1),
+              (20, 0x03f6), (21, 0x04ce)]
+    for level, attrEntryId in levels:
+        if entryId <= attrEntryId:
+            return level
+    return 22
+
+
+tree = None
+with open(sys.argv[1], 'rt') as f:
+    tree = ET.parse(f)
+
+attrs = []
+for node in tree.iter('public'):
+    if node.get('type') == 'attr':
+        sdkLevel = findSdkLevelForAttribute(node.get('id', '0'))
+        if sdkLevel > 1 and sdkLevel < 22:
+            attrs.append("{{ u\"{}\", {} }}".format(node.get('name'), sdkLevel))
+
+print "#include <string>"
+print "#include <unordered_map>"
+print
+print "namespace aapt {"
+print
+print "static std::unordered_map<std::u16string, size_t> sAttrMap = {"
+print ",\n    ".join(attrs)
+print "};"
+print
+print "size_t findAttributeSdkLevel(const std::u16string& name) {"
+print "    auto iter = sAttrMap.find(name);"
+print "    if (iter != sAttrMap.end()) {"
+print "        return iter->second;"
+print "    }"
+print "    return 0;"
+print "}"
+print
+print "} // namespace aapt"
+print
diff --git a/tools/aapt2/todo.txt b/tools/aapt2/todo.txt
new file mode 100644
index 0000000..acc8bfb
--- /dev/null
+++ b/tools/aapt2/todo.txt
@@ -0,0 +1,29 @@
+XML Files
+X Collect declared IDs
+X Build StringPool
+X Flatten
+
+Resource Table Operations
+X Build Resource Table (with StringPool) from XML.
+X Modify Resource Table.
+X - Copy and transform resources.
+X   - Pre-17/21 attr correction.
+X Perform analysis of types.
+X Flatten.
+X Assign resource IDs.
+X Assign public resource IDs.
+X Merge resource tables
+- Assign private attributes to different typespace.
+- Align resource tables
+
+Splits
+- Collect all resources (ids from layouts).
+- Generate resource table from base resources.
+- Generate resource table from individual resources of the required type.
+- Align resource tables (same type/name = same ID).
+
+Fat Apk
+X Collect all resources (ids from layouts).
+X Generate resource tables for all configurations.
+- Align individual resource tables.
+- Merge resource tables.
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 5c6d870..72ee343 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -26,14 +26,17 @@
 $ apilint.py /tmp/currentblame.txt previous.txt --no-color
 """
 
-import re, sys, collections, traceback
+import re, sys, collections, traceback, argparse
 
 
 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
 
+ALLOW_GOOGLE = False
+USE_COLOR = True
+
 def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
     # manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes
-    if "--no-color" in sys.argv: return ""
+    if not USE_COLOR: return ""
     codes = []
     if reset: codes.append("0")
     else:
@@ -976,7 +979,7 @@
     verify_collections(clazz)
     verify_flags(clazz)
     verify_exception(clazz)
-    verify_google(clazz)
+    if not ALLOW_GOOGLE: verify_google(clazz)
     verify_bitset(clazz)
     verify_manager(clazz)
     verify_boxed(clazz)
@@ -1061,11 +1064,30 @@
 
 
 if __name__ == "__main__":
-    with open(sys.argv[1]) as f:
-        cur_fail = examine_stream(f)
+    parser = argparse.ArgumentParser(description="Enforces common Android public API design \
+            patterns. It ignores lint messages from a previous API level, if provided.")
+    parser.add_argument("current.txt", type=argparse.FileType('r'), help="current.txt")
+    parser.add_argument("previous.txt", nargs='?', type=argparse.FileType('r'), default=None,
+            help="previous.txt")
+    parser.add_argument("--no-color", action='store_const', const=True,
+            help="Disable terminal colors")
+    parser.add_argument("--allow-google", action='store_const', const=True,
+            help="Allow references to Google")
+    args = vars(parser.parse_args())
 
-    if len(sys.argv) > 2:
-        with open(sys.argv[2]) as f:
+    if args['no_color']:
+        USE_COLOR = False
+
+    if args['allow_google']:
+        ALLOW_GOOGLE = True
+
+    current_file = args['current.txt']
+    previous_file = args['previous.txt']
+
+    with current_file as f:
+        cur_fail = examine_stream(f)
+    if not previous_file is None:
+        with previous_file as f:
             prev_fail = examine_stream(f)
 
         # ignore errors from previous API level
diff --git a/tools/layoutlib/.idea/libraries/guava.xml b/tools/layoutlib/.idea/libraries/guava.xml
index d47fc06..eb60719 100644
--- a/tools/layoutlib/.idea/libraries/guava.xml
+++ b/tools/layoutlib/.idea/libraries/guava.xml
@@ -1,11 +1,11 @@
 <component name="libraryTable">
   <library name="guava">
     <CLASSES>
-      <root url="jar://$PROJECT_DIR$/../../../../out/host/common/obj/JAVA_LIBRARIES/guavalib_intermediates/javalib.jar!/" />
+      <root url="jar://$PROJECT_DIR$/../../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/15.0/guava-15.0.jar!/" />
     </CLASSES>
     <JAVADOC />
     <SOURCES>
-      <root url="file://$PROJECT_DIR$/../../../../external/guava/guava/src" />
+      <root url="jar://$PROJECT_DIR$/../../../../prebuilts/tools/common/m2/repository/com/google/guava/guava/15.0/guava-15.0-sources.jar!/" />
     </SOURCES>
   </library>
 </component>
\ No newline at end of file
diff --git a/tools/layoutlib/.idea/runConfigurations/All_in_bridge.xml b/tools/layoutlib/.idea/runConfigurations/All_in_bridge.xml
index f965ba7..0b22717 100644
--- a/tools/layoutlib/.idea/runConfigurations/All_in_bridge.xml
+++ b/tools/layoutlib/.idea/runConfigurations/All_in_bridge.xml
@@ -1,5 +1,5 @@
 <component name="ProjectRunConfigurationManager">
-  <configuration default="false" name="All in bridge" type="JUnit" factoryName="JUnit" singleton="true" nameIsGenerated="true">
+  <configuration default="false" name="All in bridge" type="JUnit" factoryName="JUnit" singleton="true">
     <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
     <module name="bridge" />
     <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
@@ -8,7 +8,7 @@
     <option name="MAIN_CLASS_NAME" value="" />
     <option name="METHOD_NAME" value="" />
     <option name="TEST_OBJECT" value="package" />
-    <option name="VM_PARAMETERS" value="-ea -Dtest_res.dir=&quot;$PROJECT_DIR$/bridge/tests/res&quot;" />
+    <option name="VM_PARAMETERS" value="-ea" />
     <option name="PARAMETERS" value="" />
     <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
     <option name="ENV_VARIABLES" />
@@ -18,13 +18,7 @@
     </option>
     <envs />
     <patterns />
-    <RunnerSettings RunnerId="Debug">
-      <option name="DEBUG_PORT" value="" />
-      <option name="TRANSPORT" value="0" />
-      <option name="LOCAL" value="true" />
-    </RunnerSettings>
     <RunnerSettings RunnerId="Run" />
-    <ConfigurationWrapper RunnerId="Debug" />
     <ConfigurationWrapper RunnerId="Run" />
     <method />
   </configuration>
diff --git a/tools/layoutlib/.idea/runConfigurations/Bridge_quick.xml b/tools/layoutlib/.idea/runConfigurations/Bridge_quick.xml
new file mode 100644
index 0000000..4f0eb8d
--- /dev/null
+++ b/tools/layoutlib/.idea/runConfigurations/Bridge_quick.xml
@@ -0,0 +1,29 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="Bridge quick" type="JUnit" factoryName="JUnit">
+    <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
+    <module name="bridge" />
+    <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+    <option name="ALTERNATIVE_JRE_PATH" value="" />
+    <option name="PACKAGE_NAME" />
+    <option name="MAIN_CLASS_NAME" value="" />
+    <option name="METHOD_NAME" value="" />
+    <option name="TEST_OBJECT" value="pattern" />
+    <option name="VM_PARAMETERS" value="-ea" />
+    <option name="PARAMETERS" value="" />
+    <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
+    <option name="ENV_VARIABLES" />
+    <option name="PASS_PARENT_ENVS" value="true" />
+    <option name="TEST_SEARCH_SCOPE">
+      <value defaultName="singleModule" />
+    </option>
+    <envs />
+    <patterns>
+      <pattern testClass="com.android.layoutlib.bridge.TestDelegates" />
+      <pattern testClass="android.graphics.Matrix_DelegateTest" />
+      <pattern testClass="com.android.layoutlib.bridge.android.BridgeXmlBlockParserTest" />
+    </patterns>
+    <RunnerSettings RunnerId="Run" />
+    <ConfigurationWrapper RunnerId="Run" />
+    <method />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/tools/layoutlib/.idea/runConfigurations/Create.xml b/tools/layoutlib/.idea/runConfigurations/Create.xml
index fb0b866..ff173e5 100644
--- a/tools/layoutlib/.idea/runConfigurations/Create.xml
+++ b/tools/layoutlib/.idea/runConfigurations/Create.xml
@@ -5,8 +5,8 @@
     <option name="VM_PARAMETERS" value="" />
     <option name="PROGRAM_PARAMETERS" value="out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar out/target/common/obj/JAVA_LIBRARIES/core-libart_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/javalib.jar" />
     <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/../../../../" />
-    <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
-    <option name="ALTERNATIVE_JRE_PATH" value="1.6" />
+    <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+    <option name="ALTERNATIVE_JRE_PATH" value="" />
     <option name="ENABLE_SWING_INSPECTOR" value="false" />
     <option name="ENV_VARIABLES" />
     <option name="PASS_PARENT_ENVS" value="true" />
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index be75dde..4d2d100 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -675,7 +675,7 @@
                             graphics.fillRoundRect(
                                     (int)left, (int)top,
                                     (int)(right - left), (int)(bottom - top),
-                                    (int)rx, (int)ry);
+                                    2 * (int)rx, 2 * (int)ry);
                         }
 
                         if (style == Paint.Style.STROKE.nativeInt ||
@@ -683,7 +683,7 @@
                             graphics.drawRoundRect(
                                     (int)left, (int)top,
                                     (int)(right - left), (int)(bottom - top),
-                                    (int)rx, (int)ry);
+                                    2 * (int)rx, 2 * (int)ry);
                         }
                     }
         });
diff --git a/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java b/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java
index 24e4b54..c72efc2 100644
--- a/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java
+++ b/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java
@@ -166,7 +166,7 @@
             if (lineBreaks.breaks.length != mBreaksList.size()) {
                 lineBreaks.breaks = new int[mBreaksList.size()];
                 lineBreaks.widths = new float[mWidthsList.size()];
-                lineBreaks.flags = new boolean[mFlagsList.size()];
+                lineBreaks.flags = new int[mFlagsList.size()];
             }
 
             int i = 0;
@@ -181,7 +181,7 @@
             }
             i = 0;
             for (boolean b : mFlagsList) {
-                lineBreaks.flags[i] = b;
+                lineBreaks.flags[i] = b ? TAB_MASK : 0;
                 i++;
             }
 
diff --git a/tools/layoutlib/bridge/src/android/text/LineBreaker.java b/tools/layoutlib/bridge/src/android/text/LineBreaker.java
index 8be3635..54445a42 100644
--- a/tools/layoutlib/bridge/src/android/text/LineBreaker.java
+++ b/tools/layoutlib/bridge/src/android/text/LineBreaker.java
@@ -26,6 +26,8 @@
 // frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
 public abstract class LineBreaker {
 
+    protected static final int TAB_MASK   = 0x20000000;  // keep in sync with StaticLayout
+
     protected final @NonNull List<Primitive> mPrimitives;
     protected final @NonNull LineWidth mLineWidth;
     protected final @NonNull TabStops mTabStops;
diff --git a/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java b/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java
index d5d7798..cd92581 100644
--- a/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java
+++ b/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java
@@ -51,7 +51,7 @@
             assert p.type == PrimitiveType.PENALTY;
             breakInfo.breaks = new int[]{0};
             breakInfo.widths = new float[]{p.width};
-            breakInfo.flags = new boolean[]{false};
+            breakInfo.flags = new int[]{0};
             return;
         }
         Node[] opt = new Node[numBreaks];
@@ -129,7 +129,7 @@
 
             breakInfo.breaks[count] = mPrimitives.get(idx).location;
             breakInfo.widths[count] = opt[idx].mWidth;
-            breakInfo.flags [count] = opt[idx].mHasTabs;
+            breakInfo.flags [count] = opt[idx].mHasTabs ? TAB_MASK : 0;
             idx = opt[idx].mPrev;
         }
     }
@@ -140,7 +140,7 @@
         }
         int[] breaks = new int[size];
         float[] widths = new float[size];
-        boolean[] flags = new boolean[size];
+        int[] flags = new int[size];
 
         int toCopy = Math.min(size, lineBreaks.breaks.length);
         System.arraycopy(lineBreaks.breaks, 0, breaks, 0, toCopy);
diff --git a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
index e24b3d5..86d8da3 100644
--- a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
@@ -4,10 +4,15 @@
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
+import android.graphics.BidiRenderer;
+import android.graphics.Paint;
+import android.graphics.Paint_Delegate;
+import android.graphics.RectF;
 import android.text.StaticLayout.LineBreaks;
 import android.text.Primitive.PrimitiveType;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import com.ibm.icu.text.BreakIterator;
@@ -33,7 +38,7 @@
         new DelegateManager<Builder>(Builder.class);
 
     @LayoutlibDelegate
-    /*package*/ static int nComputeLineBreaks(long nativeBuilder, char[] inputText, float[] widths,
+    /*package*/ static int nComputeLineBreaks(long nativeBuilder,
             int length, float firstWidth, int firstWidthLineCount, float restWidth,
             int[] variableTabStops, int defaultTabStop, boolean optimize, LineBreaks recycle,
             int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength) {
@@ -41,7 +46,7 @@
         Builder builder = sBuilderManager.getDelegate(nativeBuilder);
         // compute all possible breakpoints.
         BreakIterator it = BreakIterator.getLineInstance(new ULocale(builder.mLocale));
-        it.setText(new Segment(inputText, 0, length));
+        it.setText(new Segment(builder.mText, 0, length));
         // average word length in english is 5. So, initialize the possible breaks with a guess.
         List<Integer> breaks = new ArrayList<Integer>((int) Math.ceil(length / 5d));
         int loc;
@@ -52,7 +57,7 @@
 
         LineWidth lineWidth = new LineWidth(firstWidth, firstWidthLineCount, restWidth);
         TabStops tabStopCalculator = new TabStops(variableTabStops, defaultTabStop);
-        List<Primitive> primitives = computePrimitives(inputText, widths, length, breaks);
+        List<Primitive> primitives = computePrimitives(builder.mText, builder.mWidths, length, breaks);
         LineBreaker lineBreaker;
         if (optimize) {
             lineBreaker = new OptimizingLineBreaker(primitives, lineWidth, tabStopCalculator);
@@ -119,16 +124,62 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static void nBuilderSetLocale(long nativeBuilder, String locale) {
+    /*package*/ static void nSetLocale(long nativeBuilder, String locale) {
         Builder builder = sBuilderManager.getDelegate(nativeBuilder);
         builder.mLocale = locale;
     }
 
+    @LayoutlibDelegate
+    /*package*/ static void nSetText(long nativeBuilder, char[] text, int length) {
+        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
+        builder.mText = text;
+        builder.mWidths = new float[length];
+    }
+
+
+    @LayoutlibDelegate
+    /*package*/ static float nAddStyleRun(long nativeBuilder, long nativePaint, long nativeTypeface,
+            int start, int end, boolean isRtl) {
+        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
+
+        int bidiFlags = isRtl ? Paint.BIDI_FORCE_RTL : Paint.BIDI_FORCE_LTR;
+        return measureText(nativePaint, builder.mText, start, end - start, builder.mWidths, bidiFlags);
+    }
+
+
+    @LayoutlibDelegate
+    /*package*/ static void nAddMeasuredRun(long nativeBuilder, int start, int end, float[] widths) {
+        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
+        System.arraycopy(widths, start, builder.mWidths, start, end - start);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nAddReplacementRun(long nativeBuilder, int start, int end, float width) {
+        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
+        builder.mWidths[start] = width;
+        Arrays.fill(builder.mWidths, start + 1, end, 0.0f);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nGetWidths(long nativeBuilder, float[] floatsArray) {
+        Builder builder = sBuilderManager.getDelegate(nativeBuilder);
+        System.arraycopy(builder.mWidths, 0, floatsArray, 0, builder.mWidths.length);
+    }
+
+    private static float measureText(long nativePaint, char []text, int index, int count,
+            float[] widths, int bidiFlags) {
+        Paint_Delegate paint = Paint_Delegate.getDelegate(nativePaint);
+        RectF bounds = new BidiRenderer(null, paint, text)
+            .renderText(index, index + count, bidiFlags, widths, 0, false);
+        return bounds.right - bounds.left;
+    }
+
     /**
-     * Java representation of the native Builder class. It currently only stores the locale
-     * set by nBuilderSetLocale.
+     * Java representation of the native Builder class.
      */
     static class Builder {
         String mLocale;
+        char[] mText;
+        float[] mWidths;
     }
 }
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
index 4acbd1c..80036e5 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -131,11 +131,11 @@
     }
 
     @Override
-    public View createViewFromTag(View parent, String name, AttributeSet attrs,
-            boolean inheritContext) {
+    public View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
+            boolean ignoreThemeAttrs) {
         View view;
         try {
-            view = super.createViewFromTag(parent, name, attrs, inheritContext);
+            view = super.createViewFromTag(parent, name, context, attrs, ignoreThemeAttrs);
         } catch (InflateException e) {
             // try to load the class from using the custom view loader
             try {
diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
index 7a73fae..7f1e977 100644
--- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
@@ -21,9 +21,11 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import android.content.Context;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.util.AttributeSet;
+import android.util.TypedValue;
 import android.util.Xml;
 
 import java.io.IOException;
@@ -36,9 +38,13 @@
  *
  */
 public class LayoutInflater_Delegate {
-
     private static final String TAG_MERGE = "merge";
 
+    private static final String ATTR_LAYOUT = "layout";
+
+    private static final int[] ATTRS_THEME = new int[] {
+            com.android.internal.R.attr.theme };
+
     public static boolean sIsInInclude = false;
 
     /**
@@ -49,7 +55,7 @@
      */
     @LayoutlibDelegate
     /* package */ static void rInflate(LayoutInflater thisInflater, XmlPullParser parser,
-            View parent, final AttributeSet attrs, boolean finishInflate, boolean inheritContext)
+            View parent, Context context, AttributeSet attrs, boolean finishInflate)
             throws XmlPullParserException, IOException {
 
         if (finishInflate == false) {
@@ -61,7 +67,7 @@
 
         // ---- START DEFAULT IMPLEMENTATION.
 
-        thisInflater.rInflate_Original(parser, parent, attrs, finishInflate, inheritContext);
+        thisInflater.rInflate_Original(parser, parent, context, attrs, finishInflate);
 
         // ---- END DEFAULT IMPLEMENTATION.
 
@@ -74,15 +80,50 @@
     }
 
     @LayoutlibDelegate
-    public static void parseInclude(LayoutInflater thisInflater, XmlPullParser parser, View parent,
-            AttributeSet attrs, boolean inheritContext) throws XmlPullParserException, IOException {
-
+    public static void parseInclude(LayoutInflater thisInflater, XmlPullParser parser,
+            Context context, View parent, AttributeSet attrs)
+            throws XmlPullParserException, IOException {
         int type;
 
         if (parent instanceof ViewGroup) {
-            final int layout = attrs.getAttributeResourceValue(null, "layout", 0);
+            // Apply a theme wrapper, if requested. This is sort of a weird
+            // edge case, since developers think the <include> overwrites
+            // values in the AttributeSet of the included View. So, if the
+            // included View has a theme attribute, we'll need to ignore it.
+            final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
+            final int themeResId = ta.getResourceId(0, 0);
+            final boolean hasThemeOverride = themeResId != 0;
+            if (hasThemeOverride) {
+                context = new ContextThemeWrapper(context, themeResId);
+            }
+            ta.recycle();
+
+            // If the layout is pointing to a theme attribute, we have to
+            // massage the value to get a resource identifier out of it.
+            int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0);
             if (layout == 0) {
-                final String value = attrs.getAttributeValue(null, "layout");
+                final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
+                if (value == null || value.length() <= 0) {
+                    throw new InflateException("You must specify a layout in the"
+                            + " include tag: <include layout=\"@layout/layoutID\" />");
+                }
+
+                // Attempt to resolve the "?attr/name" string to an identifier.
+                layout = context.getResources().getIdentifier(value.substring(1), null, null);
+            }
+
+            // The layout might be referencing a theme attribute.
+            // ---- START CHANGES
+            if (layout != 0) {
+                final TypedValue tempValue = new TypedValue();
+                if (context.getTheme().resolveAttribute(layout, tempValue, true)) {
+                    layout = tempValue.resourceId;
+                }
+            }
+            // ---- END CHANGES
+
+            if (layout == 0) {
+                final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
                 if (value == null) {
                     throw new InflateException("You must specifiy a layout in the"
                             + " include tag: <include layout=\"@layout/layoutID\" />");
@@ -111,13 +152,24 @@
 
                     if (TAG_MERGE.equals(childName)) {
                         // Inflate all children.
-                        thisInflater.rInflate(childParser, parent, childAttrs, false,
-                                inheritContext);
+                        thisInflater.rInflate(childParser, parent, context, childAttrs, false);
                     } else {
                         final View view = thisInflater.createViewFromTag(parent, childName,
-                                childAttrs, inheritContext);
+                                context, childAttrs, hasThemeOverride);
                         final ViewGroup group = (ViewGroup) parent;
 
+                        final TypedArray a = context.obtainStyledAttributes(
+                                attrs, com.android.internal.R.styleable.Include);
+                        final int id = a.getResourceId(
+                                com.android.internal.R.styleable.Include_id, View.NO_ID);
+                        final int visibility = a.getInt(
+                                com.android.internal.R.styleable.Include_visibility, -1);
+                        final boolean hasWidth = a.hasValue(
+                                com.android.internal.R.styleable.Include_layout_width);
+                        final boolean hasHeight = a.hasValue(
+                                com.android.internal.R.styleable.Include_layout_height);
+                        a.recycle();
+
                         // We try to load the layout params set in the <include /> tag. If
                         // they don't exist, we will rely on the layout params set in the
                         // included XML file.
@@ -127,40 +179,27 @@
                         // successfully loaded layout params from the <include /> tag,
                         // false means we need to rely on the included layout params.
                         ViewGroup.LayoutParams params = null;
-                        try {
-                            // ---- START CHANGES
-                            sIsInInclude = true;
-                            // ---- END CHANGES
+                        if (hasWidth && hasHeight) {
+                            try {
+                                // ---- START CHANGES
+                                sIsInInclude = true;
+                                // ---- END CHANGES
 
-                            params = group.generateLayoutParams(attrs);
+                                params = group.generateLayoutParams(attrs);
 
-                        } catch (RuntimeException e) {
-                            // ---- START CHANGES
-                            sIsInInclude = false;
-                            // ---- END CHANGES
-
-                            params = group.generateLayoutParams(childAttrs);
-                        } finally {
-                            // ---- START CHANGES
-                            sIsInInclude = false;
-                            // ---- END CHANGES
-
-                            if (params != null) {
-                                view.setLayoutParams(params);
+                            } finally {
+                                // ---- START CHANGES
+                                sIsInInclude = false;
+                                // ---- END CHANGES
                             }
                         }
+                        if (params == null) {
+                            params = group.generateLayoutParams(childAttrs);
+                        }
+                        view.setLayoutParams(params);
 
                         // Inflate all children.
-                        thisInflater.rInflate(childParser, view, childAttrs, true, true);
-
-                        // Attempt to override the included layout's android:id with the
-                        // one set on the <include /> tag itself.
-                        TypedArray a = thisInflater.mContext.obtainStyledAttributes(attrs,
-                            com.android.internal.R.styleable.View, 0, 0);
-                        int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID);
-                        // While we're at it, let's try to override android:visibility.
-                        int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1);
-                        a.recycle();
+                        thisInflater.rInflateChildren(childParser, view, childAttrs, true);
 
                         if (id != View.NO_ID) {
                             view.setId(id);
@@ -188,12 +227,6 @@
             throw new InflateException("<include /> can only be used inside of a ViewGroup");
         }
 
-        final int currentDepth = parser.getDepth();
-        while (((type = parser.next()) != XmlPullParser.END_TAG ||
-                parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
-            // Empty
-        }
+        LayoutInflater.consumeChildElements(parser);
     }
-
-
 }
diff --git a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
new file mode 100644
index 0000000..ec3a8d6
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
@@ -0,0 +1,156 @@
+/*
+ * 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 android.view;
+
+import com.android.layoutlib.bridge.impl.ResourceHelper;
+
+import android.graphics.Canvas;
+import android.graphics.LinearGradient;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.Path;
+import android.graphics.Path.FillType;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region.Op;
+import android.graphics.Shader.TileMode;
+
+/**
+ * Paints shadow for rounded rectangles. Inspiration from CardView. Couldn't use that directly,
+ * since it modifies the size of the content, that we can't do.
+ */
+public class RectShadowPainter {
+
+
+    private static final int START_COLOR = ResourceHelper.getColor("#37000000");
+    private static final int END_COLOR = ResourceHelper.getColor("#03000000");
+    private static final float PERPENDICULAR_ANGLE = 90f;
+
+    public static void paintShadow(Outline viewOutline, float elevation, Canvas canvas) {
+        float shadowSize = elevationToShadow(elevation);
+        int saved = modifyCanvas(canvas, shadowSize);
+        if (saved == -1) {
+            return;
+        }
+        try {
+            Paint cornerPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+            cornerPaint.setStyle(Style.FILL);
+            Paint edgePaint = new Paint(cornerPaint);
+            edgePaint.setAntiAlias(false);
+            Rect outline = viewOutline.mRect;
+            float radius = viewOutline.mRadius;
+            float outerArcRadius = radius + shadowSize;
+            int[] colors = {START_COLOR, START_COLOR, END_COLOR};
+            cornerPaint.setShader(new RadialGradient(0, 0, outerArcRadius, colors,
+                    new float[]{0f, radius / outerArcRadius, 1f}, TileMode.CLAMP));
+            edgePaint.setShader(new LinearGradient(0, 0, -shadowSize, 0, START_COLOR, END_COLOR,
+                    TileMode.CLAMP));
+            Path path = new Path();
+            path.setFillType(FillType.EVEN_ODD);
+            // A rectangle bounding the complete shadow.
+            RectF shadowRect = new RectF(outline);
+            shadowRect.inset(-shadowSize, -shadowSize);
+            // A rectangle with edges corresponding to the straight edges of the outline.
+            RectF inset = new RectF(outline);
+            inset.inset(radius, radius);
+            // A rectangle used to represent the edge shadow.
+            RectF edgeShadowRect = new RectF();
+
+
+            // left and right sides.
+            edgeShadowRect.set(-shadowSize, 0f, 0f, inset.height());
+            // Left shadow
+            sideShadow(canvas, edgePaint, edgeShadowRect, outline.left, inset.top, 0);
+            // Right shadow
+            sideShadow(canvas, edgePaint, edgeShadowRect, outline.right, inset.bottom, 2);
+            // Top shadow
+            edgeShadowRect.set(-shadowSize, 0, 0, inset.width());
+            sideShadow(canvas, edgePaint, edgeShadowRect, inset.right, outline.top, 1);
+            // bottom shadow. This needs an inset so that blank doesn't appear when the content is
+            // moved up.
+            edgeShadowRect.set(-shadowSize, 0, shadowSize / 2f, inset.width());
+            edgePaint.setShader(new LinearGradient(edgeShadowRect.right, 0, edgeShadowRect.left, 0,
+                    colors, new float[]{0f, 1 / 3f, 1f}, TileMode.CLAMP));
+            sideShadow(canvas, edgePaint, edgeShadowRect, inset.left, outline.bottom, 3);
+
+            // Draw corners.
+            drawCorner(canvas, cornerPaint, path, inset.right, inset.bottom, outerArcRadius, 0);
+            drawCorner(canvas, cornerPaint, path, inset.left, inset.bottom, outerArcRadius, 1);
+            drawCorner(canvas, cornerPaint, path, inset.left, inset.top, outerArcRadius, 2);
+            drawCorner(canvas, cornerPaint, path, inset.right, inset.top, outerArcRadius, 3);
+        } finally {
+            canvas.restoreToCount(saved);
+        }
+    }
+
+    private static float elevationToShadow(float elevation) {
+        // The factor is chosen by eyeballing the shadow size on device and preview.
+        return elevation * 0.5f;
+    }
+
+    /**
+     * Translate canvas by half of shadow size up, so that it appears that light is coming
+     * slightly from above. Also, remove clipping, so that shadow is not clipped.
+     */
+    private static int modifyCanvas(Canvas canvas, float shadowSize) {
+        Rect clipBounds = canvas.getClipBounds();
+        if (clipBounds.isEmpty()) {
+            return -1;
+        }
+        int saved = canvas.save();
+        // Usually canvas has been translated to the top left corner of the view when this is
+        // called. So, setting a clip rect at 0,0 will clip the top left part of the shadow.
+        // Thus, we just expand in each direction by width and height of the canvas.
+        canvas.clipRect(-canvas.getWidth(), -canvas.getHeight(), canvas.getWidth(),
+                canvas.getHeight(), Op.REPLACE);
+        canvas.translate(0, shadowSize / 2f);
+        return saved;
+    }
+
+    private static void sideShadow(Canvas canvas, Paint edgePaint,
+            RectF edgeShadowRect, float dx, float dy, int rotations) {
+        int saved = canvas.save();
+        canvas.translate(dx, dy);
+        canvas.rotate(rotations * PERPENDICULAR_ANGLE);
+        canvas.drawRect(edgeShadowRect, edgePaint);
+        canvas.restoreToCount(saved);
+    }
+
+    /**
+     * @param canvas Canvas to draw the rectangle on.
+     * @param paint Paint to use when drawing the corner.
+     * @param path A path to reuse. Prevents allocating memory for each path.
+     * @param x Center of circle, which this corner is a part of.
+     * @param y Center of circle, which this corner is a part of.
+     * @param radius radius of the arc
+     * @param rotations number of quarter rotations before starting to paint the arc.
+     */
+    private static void drawCorner(Canvas canvas, Paint paint, Path path, float x, float y,
+            float radius, int rotations) {
+        int saved = canvas.save();
+        canvas.translate(x, y);
+        path.reset();
+        path.arcTo(-radius, -radius, radius, radius, rotations * PERPENDICULAR_ANGLE,
+                PERPENDICULAR_ANGLE, false);
+        path.lineTo(0, 0);
+        path.close();
+        canvas.drawPath(path, paint);
+        canvas.restoreToCount(saved);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java b/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java
index 82ae1df..e72a0db 100644
--- a/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java
@@ -16,12 +16,9 @@
 
 package android.view;
 
-import com.android.annotations.NonNull;
-import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.resources.Density;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
-import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap_Delegate;
 import android.graphics.Canvas;
@@ -29,8 +26,6 @@
 import android.graphics.Path_Delegate;
 import android.graphics.Rect;
 import android.graphics.Region.Op;
-import android.util.DisplayMetrics;
-import android.util.TypedValue;
 import android.view.animation.Transformation;
 
 import java.awt.Graphics2D;
@@ -50,33 +45,36 @@
     @LayoutlibDelegate
     /*package*/ static boolean drawChild(ViewGroup thisVG, Canvas canvas, View child,
             long drawingTime) {
-        boolean retVal = thisVG.drawChild_Original(canvas, child, drawingTime);
         if (child.getZ() > thisVG.getZ()) {
             ViewOutlineProvider outlineProvider = child.getOutlineProvider();
             Outline outline = new Outline();
             outlineProvider.getOutline(child, outline);
-
+            if (outline.mPath == null && outline.mRect == null) {
+                // Sometimes, the bounds of the background drawable are not set until View.draw()
+                // is called. So, we set the bounds manually and try to get the outline again.
+                child.getBackground().setBounds(0, 0, child.mRight - child.mLeft,
+                        child.mBottom - child.mTop);
+                outlineProvider.getOutline(child, outline);
+            }
             if (outline.mPath != null || (outline.mRect != null && !outline.mRect.isEmpty())) {
                 int restoreTo = transformCanvas(thisVG, canvas, child);
                 drawShadow(thisVG, canvas, child, outline);
                 canvas.restoreToCount(restoreTo);
             }
         }
-        return retVal;
+        return thisVG.drawChild_Original(canvas, child, drawingTime);
     }
 
     private static void drawShadow(ViewGroup parent, Canvas canvas, View child,
             Outline outline) {
+        float elevation = getElevation(child, parent);
+        if(outline.mRect != null) {
+            RectShadowPainter.paintShadow(outline, elevation, canvas);
+            return;
+        }
         BufferedImage shadow = null;
-        int x = 0;
-        if (outline.mRect != null) {
-            Shadow s = getRectShadow(parent, canvas, child, outline);
-            if (s != null) {
-              shadow = s.mShadow;
-              x = -s.mShadowWidth;
-            }
-        } else if (outline.mPath != null) {
-            shadow = getPathShadow(child, outline, canvas);
+        if (outline.mPath != null) {
+            shadow = getPathShadow(outline, canvas, elevation);
         }
         if (shadow == null) {
             return;
@@ -85,52 +83,17 @@
                 Density.getEnum(canvas.getDensity()));
         Rect clipBounds = canvas.getClipBounds();
         Rect newBounds = new Rect(clipBounds);
-        newBounds.left = newBounds.left + x;
+        newBounds.inset((int)-elevation, (int)-elevation);
         canvas.clipRect(newBounds, Op.REPLACE);
-        canvas.drawBitmap(bitmap, x, 0, null);
+        canvas.drawBitmap(bitmap, 0, 0, null);
         canvas.clipRect(clipBounds, Op.REPLACE);
     }
 
-    private static Shadow getRectShadow(ViewGroup parent, Canvas canvas, View child,
-            Outline outline) {
-        BufferedImage shadow;
-        Rect clipBounds = canvas.getClipBounds();
-        if (clipBounds.isEmpty()) {
-            return null;
-        }
-        float height = child.getZ() - parent.getZ();
-        // Draw large shadow if difference in z index is more than 10dp
-        float largeShadowThreshold = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f,
-                getMetrics(child));
-        boolean largeShadow = height > largeShadowThreshold;
-        int shadowSize = largeShadow ? ShadowPainter.SHADOW_SIZE : ShadowPainter.SMALL_SHADOW_SIZE;
-        shadow = new BufferedImage(clipBounds.width() + shadowSize, clipBounds.height(),
-                BufferedImage.TYPE_INT_ARGB);
-        Graphics2D graphics = shadow.createGraphics();
-        Rect rect = outline.mRect;
-        if (largeShadow) {
-            ShadowPainter.drawRectangleShadow(graphics,
-                    rect.left + shadowSize, rect.top, rect.width(), rect.height());
-        } else {
-            ShadowPainter.drawSmallRectangleShadow(graphics,
-                    rect.left + shadowSize, rect.top, rect.width(), rect.height());
-        }
-        graphics.dispose();
-        return new Shadow(shadow, shadowSize);
+    private static float getElevation(View child, ViewGroup parent) {
+        return child.getZ() - parent.getZ();
     }
 
-    @NonNull
-    private static DisplayMetrics getMetrics(View view) {
-        Context context = view.getContext();
-        context = BridgeContext.getBaseContext(context);
-        if (context instanceof BridgeContext) {
-            return ((BridgeContext) context).getMetrics();
-        }
-        throw new RuntimeException("View " + view.getClass().getName() + " not created with the " +
-                "right context");
-    }
-
-    private static BufferedImage getPathShadow(View child, Outline outline, Canvas canvas) {
+    private static BufferedImage getPathShadow(Outline outline, Canvas canvas, float elevation) {
         Rect clipBounds = canvas.getClipBounds();
         if (clipBounds.isEmpty()) {
           return null;
@@ -140,7 +103,7 @@
         Graphics2D graphics = image.createGraphics();
         graphics.draw(Path_Delegate.getDelegate(outline.mPath.mNativePath).getJavaShape());
         graphics.dispose();
-        return ShadowPainter.createDropShadow(image, ((int) child.getZ()));
+        return ShadowPainter.createDropShadow(image, (int) elevation);
     }
 
     // Copied from android.view.View#draw(Canvas, ViewGroup, long) and removed code paths
@@ -194,15 +157,4 @@
         }
         return restoreTo;
     }
-
-    private static class Shadow {
-        public BufferedImage mShadow;
-        public int mShadowWidth;
-
-        public Shadow(BufferedImage shadow, int shadowWidth) {
-            mShadow = shadow;
-            mShadowWidth = shadowWidth;
-        }
-
-    }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index e1c58fd..8e74ce1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1010,6 +1010,12 @@
     }
 
     @Override
+    public int checkSelfPermission(String arg0) {
+        // pass
+        return 0;
+    }
+
+    @Override
     public int checkPermission(String arg0, int arg1, int arg2, IBinder arg3) {
         // pass
         return 0;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
index 39ebdfc..085df85 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
@@ -145,4 +145,14 @@
     public void boostScreenBrightness(long time) throws RemoteException {
         // pass for now.
     }
+
+    @Override
+    public boolean isDeviceIdleMode() throws RemoteException {
+        return false;
+    }
+
+    @Override
+    public boolean isScreenBrightnessBoosted() throws RemoteException {
+        return false;
+    }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java
index dde041b..9f9b968 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java
@@ -16,6 +16,8 @@
 
 package com.android.layoutlib.bridge.bars;
 
+import android.os.Build.VERSION_CODES;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -40,12 +42,12 @@
     private static final int BLACK = 0xFF000000;
 
     public static boolean showOnScreenNavBar(int platformVersion) {
-        return platformVersion == 0 || platformVersion >= ICE_CREAM_SANDWICH;
+        return isGreaterOrEqual(platformVersion, ICE_CREAM_SANDWICH);
     }
 
     public static int getStatusBarColor(int platformVersion) {
         // return white for froyo and earlier; black otherwise.
-        return platformVersion == 0 || platformVersion >= GINGERBREAD ? BLACK : WHITE;
+        return isGreaterOrEqual(platformVersion, GINGERBREAD) ? BLACK : WHITE;
     }
 
     public static List<String> getResourceDirs(int platformVersion) {
@@ -98,7 +100,7 @@
     }
 
     public static int getTimeColor(int platformVersion) {
-        if (platformVersion == 0 || platformVersion >= KITKAT ||
+        if (isGreaterOrEqual(platformVersion, KITKAT) ||
                 platformVersion > FROYO && platformVersion < HONEYCOMB) {
             // Gingerbread and KitKat onwards.
             return WHITE;
@@ -117,4 +119,13 @@
     public static String getWifiIconType(int platformVersion) {
         return platformVersion == 0 ? "xml" : "png";
     }
+
+    /**
+     * Compare simulated platform version and code from {@link VERSION_CODES} to check if
+     * the simulated platform is greater than or equal to the version code.
+     */
+    public static boolean isGreaterOrEqual(int platformVersion, int code) {
+        // simulated platform version = 0 means that we use the latest.
+        return platformVersion == 0 || platformVersion >= code;
+    }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index 13ddf07..bc1a41d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -16,6 +16,7 @@
 
 package com.android.layoutlib.bridge.bars;
 
+import com.android.annotations.NonNull;
 import com.android.ide.common.rendering.api.RenderResources;
 import com.android.ide.common.rendering.api.ResourceValue;
 import com.android.ide.common.rendering.api.StyleResourceValue;
@@ -26,6 +27,7 @@
 import com.android.layoutlib.bridge.impl.ResourceHelper;
 import com.android.resources.Density;
 import com.android.resources.LayoutDirection;
+import com.android.resources.ResourceType;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -47,6 +49,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 
+import static android.os.Build.VERSION_CODES.LOLLIPOP;
+
 /**
  * Base "bar" class for the window decor around the the edited layout.
  * This is basically an horizontal layout that loads a given layout on creation (it is read
@@ -63,7 +67,7 @@
 
     protected abstract TextView getStyleableTextView();
 
-    protected CustomBar(Context context, int orientation, String layoutPath,
+    protected CustomBar(BridgeContext context, int orientation, String layoutPath,
             String name, int simulatedPlatformVersion) throws XmlPullParserException {
         super(context);
         mSimulatedPlatformVersion = simulatedPlatformVersion;
@@ -197,7 +201,7 @@
 
 
                 ResourceValue textColor = res.findItemInStyle(textStyle, "textColor",
-                        true /*isFrameworkAttr*/);
+                        true);
                 textColor = res.resolveResValue(textColor);
                 if (textColor != null) {
                     ColorStateList stateList = ResourceHelper.getColorStateList(
@@ -210,12 +214,39 @@
         }
     }
 
+    /**
+     * Given a theme attribute name, get the color referenced by it. The theme attribute may be
+     * used in a layout like "?attr/foo".
+     * <p/>
+     * Returns 0 if not found.
+     *
+     * @throws NumberFormatException if color resolved to an invalid string.
+     */
+    protected int getThemeAttrColor(@NonNull String attrName, boolean isFramework) {
+        if (!Config.isGreaterOrEqual(mSimulatedPlatformVersion, LOLLIPOP)) {
+            return 0;
+        }
+        assert mContext instanceof BridgeContext;
+        BridgeContext context = ((BridgeContext) mContext);
+        RenderResources renderResources = context.getRenderResources();
+        // From ?attr/foo to @color/bar. This is most likely an ItemResourceValue.
+        ResourceValue resource = renderResources.findItemInTheme(attrName, isFramework);
+        if (resource != null) {
+            // Form @color/bar to the #AARRGGBB
+            resource = renderResources.resolveResValue(resource);
+        }
+        if (resource != null && ResourceType.COLOR.equals(resource.getResourceType())) {
+            return ResourceHelper.getColor(resource.getValue());
+        }
+        return 0;
+    }
+
     private ResourceValue getResourceValue(String reference) {
         BridgeContext bridgeContext = (BridgeContext) mContext;
         RenderResources res = bridgeContext.getRenderResources();
 
         // find the resource
-        ResourceValue value = res.findResValue(reference, false /*isFramework*/);
+        ResourceValue value = res.findResValue(reference, false);
 
         // resolve it if needed
         return res.resolveResValue(value);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
index 283ff57..9450b6c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
@@ -16,22 +16,26 @@
 
 package com.android.layoutlib.bridge.bars;
 
+import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.resources.Density;
 
 import org.xmlpull.v1.XmlPullParserException;
 
-import android.content.Context;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
 public class NavigationBar extends CustomBar {
 
-    public NavigationBar(Context context, Density density, int orientation, boolean isRtl,
+    /** Navigation bar background color attribute name. */
+    private static final String ATTR_COLOR = "navigationBarColor";
+
+    public NavigationBar(BridgeContext context, Density density, int orientation, boolean isRtl,
             boolean rtlEnabled, int simulatedPlatformVersion) throws XmlPullParserException {
         super(context, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml",
                 simulatedPlatformVersion);
 
-        setBackgroundColor(0xFF000000);
+        int color = getThemeAttrColor(ATTR_COLOR, true);
+        setBackgroundColor(color == 0 ? 0xFF000000 : color);
 
         // Cannot access the inside items through id because no R.id values have been
         // created for them.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index c7c62d6..e5f1f68 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -25,7 +25,6 @@
 
 import org.xmlpull.v1.XmlPullParserException;
 
-import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.view.Gravity;
 import android.view.View;
@@ -38,20 +37,21 @@
 
 public class StatusBar extends CustomBar {
 
-    private final Context mContext;
     private final int mSimulatedPlatformVersion;
+    /** Status bar background color attribute name. */
+    private static final String ATTR_COLOR = "colorPrimaryDark";
 
-    public StatusBar(Context context, Density density, int direction, boolean RtlEnabled,
+    public StatusBar(BridgeContext context, Density density, int direction, boolean RtlEnabled,
             int simulatedPlatformVersion) throws XmlPullParserException {
         // FIXME: if direction is RTL but it's not enabled in application manifest, mirror this bar.
         super(context, LinearLayout.HORIZONTAL, "/bars/status_bar.xml", "status_bar.xml",
                 simulatedPlatformVersion);
-        mContext = context;
         mSimulatedPlatformVersion = simulatedPlatformVersion;
 
         // FIXME: use FILL_H?
         setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT);
-        setBackgroundColor(Config.getStatusBarColor(simulatedPlatformVersion));
+        int color = getThemeAttrColor(ATTR_COLOR, true);
+        setBackgroundColor(color == 0 ? Config.getStatusBarColor(simulatedPlatformVersion) : color);
 
         // Cannot access the inside items through id because no R.id values have been
         // created for them.
@@ -82,10 +82,8 @@
                 try {
                     BridgeXmlBlockParser parser = new BridgeXmlBlockParser(
                             ParserFactory.create(stream, null), (BridgeContext) mContext, true);
-                    Drawable drawable = Drawable.createFromXml(mContext.getResources(), parser);
-                    if (drawable != null) {
-                        imageView.setImageDrawable(drawable);
-                    }
+                    imageView.setImageDrawable(
+                            Drawable.createFromXml(mContext.getResources(), parser));
                 } catch (XmlPullParserException e) {
                     Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Unable to draw wifi icon", e,
                             null);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
index 10f1383..c610601 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
@@ -16,9 +16,10 @@
 
 package com.android.layoutlib.bridge.bars;
 
+import com.android.layoutlib.bridge.android.BridgeContext;
+
 import org.xmlpull.v1.XmlPullParserException;
 
-import android.content.Context;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -26,7 +27,7 @@
 
     private TextView mTextView;
 
-    public TitleBar(Context context, String label, int simulatedPlatformVersion)
+    public TitleBar(BridgeContext context, String label, int simulatedPlatformVersion)
             throws XmlPullParserException {
         super(context, LinearLayout.HORIZONTAL, "/bars/title_bar.xml", "title_bar.xml",
                 simulatedPlatformVersion);
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle
index 80be12d..0f37fce 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle
@@ -3,7 +3,7 @@
         jcenter()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:0.12.+'
+        classpath 'com.android.tools.build:gradle:1.1.3'
 
         // NOTE: Do not place your application dependencies here; they belong
         // in the individual module build.gradle files
@@ -19,22 +19,24 @@
 apply plugin: 'com.android.application'
 
 android {
-    compileSdkVersion 20
-    buildToolsVersion '20'
+    compileSdkVersion 21
+    buildToolsVersion '21.1.2'
     defaultConfig {
         applicationId 'com.android.layoutlib.test.myapplication'
         minSdkVersion 19
-        targetSdkVersion 20
+        targetSdkVersion 21
         versionCode 1
         versionName '1.0'
     }
     buildTypes {
         release {
-            runProguard false
+            minifyEnabled false
             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
         }
     }
-    productFlavors {
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_6
+        targetCompatibility JavaVersion.VERSION_1_6
     }
 }
 
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/androidTest/debug/com/android/layoutlib/test/myapplication/test/BuildConfig.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/androidTest/debug/com/android/layoutlib/test/myapplication/test/BuildConfig.class
new file mode 100644
index 0000000..1ca7e01
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/androidTest/debug/com/android/layoutlib/test/myapplication/test/BuildConfig.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/BuildConfig.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/BuildConfig.class
index 2b4f7bf..ceb56bf 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/BuildConfig.class
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/BuildConfig.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomCalendar.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomCalendar.class
new file mode 100644
index 0000000..c363055
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomCalendar.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomDate.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomDate.class
new file mode 100644
index 0000000..edda3de
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/CustomDate.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png
index 943cdf1..e38f437 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/activity.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.properties b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.properties
index 5de946b..3b51ffe 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.properties
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Apr 10 15:27:10 PDT 2013
+#Tue Mar 17 15:13:06 PDT 2015
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/androidTest/java/com/android/layoulib/test/myapplication/ApplicationTest.java b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/androidTest/java/com/android/layoulib/test/myapplication/ApplicationTest.java
deleted file mode 100644
index 7304af1..0000000
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/androidTest/java/com/android/layoulib/test/myapplication/ApplicationTest.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.android.layoulib.test.myapplication;
-
-import android.app.Application;
-import android.test.ApplicationTestCase;
-
-/**
- * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
- */
-public class ApplicationTest extends ApplicationTestCase<Application> {
-    public ApplicationTest() {
-        super(Application.class);
-    }
-}
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/CustomCalendar.java b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/CustomCalendar.java
new file mode 100644
index 0000000..80bbaf1
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/CustomCalendar.java
@@ -0,0 +1,31 @@
+package com.android.layoutlib.test.myapplication;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.CalendarView;
+
+public class CustomCalendar extends CalendarView {
+    public CustomCalendar(Context context) {
+        super(context);
+        init();
+    }
+
+    public CustomCalendar(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public CustomCalendar(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init();
+    }
+
+    public CustomCalendar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init();
+    }
+
+    private void init() {
+        setDate(871703200000L, false, true);
+    }
+}
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/CustomDate.java b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/CustomDate.java
new file mode 100644
index 0000000..cb750f4
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/CustomDate.java
@@ -0,0 +1,31 @@
+package com.android.layoutlib.test.myapplication;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.DatePicker;
+
+public class CustomDate extends DatePicker {
+    public CustomDate(Context context) {
+        super(context);
+        init();
+    }
+
+    public CustomDate(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public CustomDate(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        init();
+    }
+
+    public CustomDate(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        init();
+    }
+
+    private void init() {
+        init(2015, 0, 20, null);
+    }
+}
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml
index b8ec5661..c1f663e 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml
@@ -1,17 +1,25 @@
 <?xml version="1.0" encoding="utf-8"?>
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical" android:layout_width="match_parent"
+<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:columnCount="2"
     android:layout_height="match_parent">
 
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="Some text"/>
-    <DatePicker
+    <Switch
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:checked="true"
+        android:layout_gravity="center"
+        />
+    <com.android.layoutlib.test.myapplication.CustomDate
         android:layout_width="100dp"
-        android:layout_height="100dp"/>
-    <CalendarView
-        android:layout_width="100dp"
-        android:layout_height="100dp"/>
-</LinearLayout>
\ No newline at end of file
+        android:layout_height="wrap_content"/>
+    <com.android.layoutlib.test.myapplication.CustomCalendar
+        android:layout_width="200dp"
+        android:layout_gravity="center_horizontal"
+        android:layout_height="200dp"/>
+</GridLayout>
\ No newline at end of file
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/ImageUtils.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/ImageUtils.java
index e13ad72..d7e5486 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/ImageUtils.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/ImageUtils.java
@@ -56,7 +56,7 @@
 
     private static final int THUMBNAIL_SIZE = 250;
 
-    private static final double MAX_PERCENT_DIFFERENCE = 0.1;
+    private static final double MAX_PERCENT_DIFFERENCE = 0.3;
 
     public static void requireSimilar(@NonNull String relativePath, @NonNull BufferedImage image)
             throws IOException {
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index a86fcdd..ac23564 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -316,7 +316,8 @@
         FolderConfiguration config = configGenerator.getFolderConfig();
         ResourceResolver resourceResolver =
                 ResourceResolver.create(mProjectResources.getConfiguredResources(config),
-                        mFrameworkRepo.getConfiguredResources(config), "Theme.Material", false);
+                        mFrameworkRepo.getConfiguredResources(config),
+                        "Theme.Material.Light.DarkActionBar", false);
 
         return new SessionParams(
                 layoutParser,
@@ -336,7 +337,7 @@
                 @Override
                 public void warning(String tag, String message, Object data) {
                     System.out.println("Warning " + tag + ": " + message);
-                    fail(message);
+                    failWithMsg(message);
                 }
 
                 @Override
@@ -346,13 +347,13 @@
                     if (throwable != null) {
                         throwable.printStackTrace();
                     }
-                    fail(message);
+                    failWithMsg(message);
                 }
 
                 @Override
                 public void error(String tag, String message, Object data) {
                     System.out.println("Error " + tag + ": " + message);
-                    fail(message);
+                    failWithMsg(message);
                 }
 
                 @Override
@@ -361,7 +362,7 @@
                     if (throwable != null) {
                         throwable.printStackTrace();
                     }
-                    fail(message);
+                    failWithMsg(message);
                 }
             };
         }
@@ -376,12 +377,12 @@
                     if (t != null) {
                         t.printStackTrace();
                     }
-                    fail(String.format(msgFormat, args));
+                    failWithMsg(msgFormat, args);
                 }
 
                 @Override
                 public void warning(String msgFormat, Object... args) {
-                    fail(String.format(msgFormat, args));
+                    failWithMsg(msgFormat, args);
                 }
 
                 @Override
@@ -397,4 +398,8 @@
         }
         return mLogger;
     }
+
+    private static void failWithMsg(String msgFormat, Object... args) {
+        fail(msgFormat == null || args == null ? "" : String.format(msgFormat, args));
+    }
 }
diff --git a/wifi/java/android/net/wifi/IRttManager.aidl b/wifi/java/android/net/wifi/IRttManager.aidl
index d929f55..90f66c4 100644
--- a/wifi/java/android/net/wifi/IRttManager.aidl
+++ b/wifi/java/android/net/wifi/IRttManager.aidl
@@ -15,8 +15,8 @@
  */
 
 package android.net.wifi;
-
 import android.os.Messenger;
+import android.net.wifi.RttManager;
 
 /**
  * {@hide}
@@ -24,4 +24,5 @@
 interface IRttManager
 {
     Messenger getMessenger();
+    RttManager.RttCapabilities getRttCapabilities();
 }
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index bc95a36..5342494 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -155,6 +155,10 @@
 
     void setAllowScansWithTraffic(int enabled);
 
+    boolean getAllowScansWhileAssociated();
+
+    void setAllowScansWhileAssociated(boolean enabled);
+
     WifiConnectionStatistics getConnectionStatistics();
 
     void disableEphemeralNetwork(String SSID);
diff --git a/wifi/java/android/net/wifi/RttManager.aidl b/wifi/java/android/net/wifi/RttManager.aidl
new file mode 100644
index 0000000..5c6d447
--- /dev/null
+++ b/wifi/java/android/net/wifi/RttManager.aidl
@@ -0,0 +1,18 @@
+/**
+ * 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 android.net.wifi;
+parcelable RttManager.RttCapabilities;
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index 57343c5..65ecf5d 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -26,10 +26,19 @@
     private static final boolean DBG = true;
     private static final String TAG = "RttManager";
 
-    public static final int RTT_TYPE_UNSPECIFIED    = 0;
-    public static final int RTT_TYPE_ONE_SIDED      = 1;
-    public static final int RTT_TYPE_11_V           = 2;
-    public static final int RTT_TYPE_11_MC          = 4;
+    /** @deprecated Type must be specified*/
+    @Deprecated
+    public static final int RTT_TYPE_UNSPECIFIED        = 0;
+    public static final int RTT_TYPE_ONE_SIDED          = 1;
+
+    /** @deprecated It is not supported*/
+    @Deprecated
+    public static final int RTT_TYPE_11_V               = 2;
+    public static final int RTT_TYPE_TWO_SIDED          = 4;
+
+    /** @deprecated It is not supported*/
+    @Deprecated
+    public static final int RTT_TYPE_11_MC              = 4;
 
     public static final int RTT_PEER_TYPE_UNSPECIFIED    = 0;
     public static final int RTT_PEER_TYPE_AP             = 1;
@@ -42,6 +51,9 @@
     public static final int RTT_CHANNEL_WIDTH_80P80   = 4;
     public static final int RTT_CHANNEL_WIDTH_5       = 5;
     public static final int RTT_CHANNEL_WIDTH_10      = 6;
+
+    /** @deprecated channel info must be specified*/
+    @Deprecated
     public static final int RTT_CHANNEL_WIDTH_UNSPECIFIED = -1;
 
     public static final int RTT_STATUS_SUCCESS                  = 0;
@@ -53,6 +65,12 @@
     public static final int RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL  = 6;
     public static final int RTT_STATUS_FAIL_NO_CAPABILITY       = 7;
     public static final int RTT_STATUS_ABORTED                  = 8;
+    //if the T1-T4 or TOD/TOA Timestamp is illegal
+    public static final int RTT_STATUS_FAIL_INVALID_TS          = 9;
+    //11mc protocol failed, eg, unrecognized FTMR/FTM
+    public static final int RTT_STATUS_FAIL_PROTOCOL            = 10;
+    public static final int RTT_STATUS_FAIL_SCHEDULE            = 11;
+    public static final int RTT_STATUS_FAIL_BUSY_TRY_LATER      = 12;
 
     public static final int REASON_UNSPECIFIED              = -1;
     public static final int REASON_NOT_AVAILABLE            = -2;
@@ -61,41 +79,269 @@
 
     public static final String DESCRIPTION_KEY  = "android.net.wifi.RttManager.Description";
 
+    /**
+     * RTT BW supported bit mask
+     */
+    public static final int RTT_BW_5_SUPPORT   = 0x1;
+    public static final int RTT_BW_10_SUPPORT  = 0x2;
+    public static final int RTT_BW_20_SUPPORT  = 0x4;
+    public static final int RTT_BW_40_SUPPORT  = 0x8;
+    public static final int RTT_BW_80_SUPPORT  = 0x10;
+    public static final int RTT_BW_160_SUPPORT = 0x20;
+
+    /**
+     * RTT Preamble Support bit mask
+     */
+    public static final int PREAMBLE_LEGACY  = 0x1;
+    public static final int PREAMBLE_HT      = 0x2;
+    public static final int PREAMBLE_VHT     = 0x4;
+
+    /** @deprecated It has been replaced by RttCapabilities*/
+    @Deprecated
     public class Capabilities {
         public int supportedType;
         public int supportedPeerType;
     }
 
+    /** @deprecated It has been replaced by getRttCapabilities*/
+    @Deprecated
     public Capabilities getCapabilities() {
         return new Capabilities();
     }
 
+    /**
+     * This class describe the RTT capability of the Hardware
+     */
+    public static class RttCapabilities implements Parcelable {
+        /** @deprecated It is not supported*/
+        @Deprecated
+        public boolean supportedType;
+        /** @deprecated It is not supported*/
+        @Deprecated
+        public boolean supportedPeerType;
+        //1-sided rtt measurement is supported
+        public boolean oneSidedRttSupported;
+        //11mc 2-sided rtt measurement is supported
+        public boolean twoSided11McRttSupported;
+        //location configuration information supported
+        public boolean lciSupported;
+        //location civic records supported
+        public boolean lcrSupported;
+        //preamble supported, see bit mask definition above
+        public int preambleSupported;
+        //RTT bandwidth supported
+        public int bwSupported;
+
+        @Override
+        public String toString() {
+            StringBuffer sb = new StringBuffer();
+            sb.append("oneSidedRtt ").
+            append(oneSidedRttSupported ? "is Supported. " : "is not supported. ").
+            append("twoSided11McRtt ").
+            append(twoSided11McRttSupported ? "is Supported. " : "is not supported. ").
+            append("lci ").
+            append(lciSupported ? "is Supported. " : "is not supported. ").
+            append("lcr ").
+            append(lcrSupported ? "is Supported. " : "is not supported. ");
+
+            if ((preambleSupported & PREAMBLE_LEGACY) != 0) {
+                sb.append("Legacy ");
+            }
+
+            if ((preambleSupported & PREAMBLE_HT) != 0) {
+                sb.append("HT ");
+            }
+
+            if ((preambleSupported & PREAMBLE_VHT) != 0) {
+                sb.append("VHT ");
+            }
+
+            sb.append("is supported. \n");
+
+            if ((bwSupported & RTT_BW_5_SUPPORT) != 0) {
+                sb.append("5 MHz ");
+            }
+
+            if ((bwSupported & RTT_BW_10_SUPPORT) != 0) {
+                sb.append("10 MHz ");
+            }
+
+            if ((bwSupported & RTT_BW_20_SUPPORT) != 0) {
+                sb.append("20 MHz ");
+            }
+
+            if ((bwSupported & RTT_BW_40_SUPPORT) != 0) {
+                sb.append("40 MHz ");
+            }
+
+            if ((bwSupported & RTT_BW_80_SUPPORT) != 0) {
+                sb.append("80 MHz ");
+            }
+
+            if ((bwSupported & RTT_BW_160_SUPPORT) != 0) {
+                sb.append("160 MHz ");
+            }
+
+            sb.append("is supported.");
+
+            return sb.toString();
+        }
+        /** Implement the Parcelable interface {@hide} */
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        /** Implement the Parcelable interface {@hide} */
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(oneSidedRttSupported ? 1 : 0);
+            dest.writeInt(twoSided11McRttSupported ? 1 : 0);
+            dest.writeInt(lciSupported ? 1 : 0);
+            dest.writeInt(lcrSupported ? 1 : 0);
+            dest.writeInt(preambleSupported);
+            dest.writeInt(bwSupported);
+
+        }
+
+        /** Implement the Parcelable interface {@hide} */
+        public static final Creator<RttCapabilities> CREATOR =
+            new Creator<RttCapabilities>() {
+               public RttCapabilities createFromParcel(Parcel in) {
+                    RttCapabilities capabilities = new RttCapabilities();
+                    capabilities.oneSidedRttSupported = in.readInt() == 1 ? true : false;
+                        capabilities.twoSided11McRttSupported = in.readInt() == 1 ? true : false;
+                        capabilities.lciSupported = in.readInt() == 1 ? true : false;
+                        capabilities.lcrSupported = in.readInt() == 1 ? true : false;
+                        capabilities.preambleSupported = in.readInt();
+                        capabilities.bwSupported = in.readInt();
+                        return capabilities;
+                    }
+                /** Implement the Parcelable interface {@hide} */
+                @Override
+                public RttCapabilities[] newArray(int size) {
+                    return new RttCapabilities[size];
+                }
+             };
+    }
+
+    public RttCapabilities getRttCapabilities() {
+        synchronized (sCapabilitiesLock) {
+            if (mRttCapabilities == null) {
+                try {
+                    mRttCapabilities = mService.getRttCapabilities();
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Can not get RTT Capabilities");
+                }
+            }
+            return mRttCapabilities;
+        }
+    }
+
     /** specifies parameters for RTT request */
     public static class RttParams {
-
-        /** type of device being ranged; one of RTT_PEER_TYPE_AP or RTT_PEER_TYPE_STA */
+        /**
+         * type of destination device being ranged; one of RTT_PEER_TYPE_AP or RTT_PEER_TYPE_STA
+         */
         public int deviceType;
 
-        /** type of RTT being sought; one of RTT_TYPE_ONE_SIDED
-         *  RTT_TYPE_11_V or RTT_TYPE_11_MC or RTT_TYPE_UNSPECIFIED */
+        /**
+         * type of RTT measurement method; one of RTT_TYPE_ONE_SIDED or RTT_TYPE_TWO_SIDED.
+         */
         public int requestType;
 
         /** mac address of the device being ranged */
         public String bssid;
 
-        /** channel frequency that the device is on; optional */
+        /**
+         * The primary 20 MHz frequency (in MHz) of the channel over which the client is
+         * communicating with the access point.Similar as ScanResult.frequency
+         */
         public int frequency;
 
-        /** optional channel width. wider channels result in better accuracy,
-         *  but they take longer time, and even get aborted may times; use
-         *  RTT_CHANNEL_WIDTH_UNSPECIFIED if not specifying */
+        /**
+         * channel width used for RTT measurement. User need verify the highest BW the destination
+         * support (from scan result etc) before set this value. Wider channels result usually give
+         * better accuracy. However, the frame loss can increase. Similar as ScanResult.channelWidth
+         */
         public int channelWidth;
 
-        /** number of samples to be taken */
+        /**
+         * Not used if the AP bandwidth is 20 MHz
+         * If the AP use 40, 80 or 160 MHz, this is the center frequency
+         * if the AP use 80 + 80 MHz, this is the center frequency of the first segment
+         * similar as ScanResult.centerFreq0
+         */
+         public int centerFreq0;
+
+         /**
+          * Only used if the AP bandwidth is 80 + 80 MHz
+          * if the AP use 80 + 80 MHz, this is the center frequency of the second segment
+          * similar as ScanResult.centerFreq1
+          */
+          public int centerFreq1;
+        /**
+         * number of samples to be taken
+         * @deprecated  It has been replaced by numSamplesPerBurst
+         */
+        @Deprecated
         public int num_samples;
 
-        /** number of retries if a sample fails */
+        /**
+         * number of retries if a sample fails
+         * @deprecated It has been replaced by numRetriesPerMeasurementFrame
+         */
+        @Deprecated
         public int num_retries;
+
+        /** Number of burst. fixed to 1 for single side RTT*/
+        public int numberBurst;
+
+        /** valid only if numberBurst > 1, interval between burst(ms). Not used by singe side RTT */
+        public int interval;
+
+        /** number of samples to be taken in one burst*/
+        public int numSamplesPerBurst;
+
+        /** number of retries for each measurement frame if a sample fails
+         *  Only used by single side RTT
+         */
+        public int numRetriesPerMeasurementFrame;
+
+        /** number of retries for FTMR frame if fails Only used by 80211MC double side RTT */
+        public int numRetriesPerFTMR;
+
+        /** Request LCI information */
+        public boolean LCIRequest;
+
+        /** Request LCR information */
+        public boolean LCRRequest;
+
+        /** Timeout for each burst, unit of 250 us*/
+        public int burstTimeout;
+
+        /** preamble used for RTT measurement
+         *  should be one of PREAMBLE_LEGACY, PREAMBLE_HT, PREAMBLE_VHT
+         */
+        public int preamble;
+
+        /** bandWidth used for RTT measurement.User need verify the highest BW the destination
+         * support (from scan result etc) before set this value. Wider channels result usually give
+         * better accuracy. However, the frame loss can increase too.
+         * should be one of RTT_CHANNEL_WIDTH_20 to RTT_CHANNEL_WIDTH_80
+         */
+        public int bandwidth;
+
+        public RttParams() {
+            //provide initial value for RttParams
+            deviceType = RTT_PEER_TYPE_AP;
+            numberBurst = 1;
+            numSamplesPerBurst = 8;
+            numRetriesPerMeasurementFrame  = 0;
+            burstTimeout = 40 + numSamplesPerBurst *4;
+            preamble = PREAMBLE_LEGACY;
+            bandwidth = RTT_CHANNEL_WIDTH_20;
+        }
     }
 
     /** pseudo-private class used to parcel arguments */
@@ -121,10 +367,20 @@
                     dest.writeInt(params.deviceType);
                     dest.writeInt(params.requestType);
                     dest.writeString(params.bssid);
-                    dest.writeInt(params.frequency);
                     dest.writeInt(params.channelWidth);
-                    dest.writeInt(params.num_samples);
-                    dest.writeInt(params.num_retries);
+                    dest.writeInt(params.frequency);
+                    dest.writeInt(params.centerFreq0);
+                    dest.writeInt(params.centerFreq1);
+                    dest.writeInt(params.numberBurst);
+                    dest.writeInt(params.interval);
+                    dest.writeInt(params.numSamplesPerBurst);
+                    dest.writeInt(params.numRetriesPerMeasurementFrame);
+                    dest.writeInt(params.numRetriesPerFTMR);
+                    dest.writeInt(params.LCIRequest ? 1 : 0);
+                    dest.writeInt(params.LCRRequest ? 1 : 0);
+                    dest.writeInt(params.burstTimeout);
+                    dest.writeInt(params.preamble);
+                    dest.writeInt(params.bandwidth);
                 }
             } else {
                 dest.writeInt(0);
@@ -148,11 +404,20 @@
                             params[i].deviceType = in.readInt();
                             params[i].requestType = in.readInt();
                             params[i].bssid = in.readString();
-                            params[i].frequency = in.readInt();
                             params[i].channelWidth = in.readInt();
-                            params[i].num_samples = in.readInt();
-                            params[i].num_retries = in.readInt();
-
+                            params[i].frequency = in.readInt();
+                            params[i].centerFreq0 = in.readInt();
+                            params[i].centerFreq1 = in.readInt();
+                            params[i].numberBurst = in.readInt();
+                            params[i].interval = in.readInt();
+                            params[i].numSamplesPerBurst = in.readInt();
+                            params[i].numRetriesPerMeasurementFrame = in.readInt();
+                            params[i].numRetriesPerFTMR = in.readInt();
+                            params[i].LCIRequest = in.readInt() == 1 ? true : false;
+                            params[i].LCRRequest = in.readInt() == 1 ? true : false;
+                            params[i].burstTimeout = in.readInt();
+                            params[i].preamble = in.readInt();
+                            params[i].bandwidth = in.readInt();
                         }
 
                         ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
@@ -165,46 +430,143 @@
                 };
     }
 
+    public class wifiInformationElement {
+        /** Information Element ID*/
+        public int id;
+        public String data;
+    }
     /** specifies RTT results */
     public static class RttResult {
         /** mac address of the device being ranged */
         public String bssid;
 
+        /** # of burst for this measurement*/
+        public int burstNumber;
+
+        /** total number of measurement frames in this measurement*/
+        public int measurementFrameNumber;
+
+        /** total successful number of measurement frames in this measurement*/
+        public int successMeasurementFrameNumber;
+
+        /** Maximum number of frames per burst supported by peer */
+        public int frameNumberPerBurstPeer;
+
         /** status of the request */
         public int status;
 
-        /** type of the request used */
+        /**
+         * type of the request used
+         * @deprecated It has been replaced by measurementType
+         */
+        @Deprecated
         public int requestType;
 
+        /** RTT measurement method type used, shoudl be one of RTT_TYPE_ONE_SIDED or
+         *  RTT_TYPE_TWO_SIDED.
+         */
+        public int measurementType;
+
+        /** please retry RTT measurement after this S since peer indicate busy at ths moment*/
+        public int retryAfterDuration;
+
         /** timestamp of completion, in microsecond since boot */
         public long ts;
 
-        /** average RSSI observed */
+        /** average RSSI observed, unit of 0.5 dB */
         public int rssi;
 
-        /** RSSI spread (i.e. max - min) */
+        /**
+         * RSSI spread (i.e. max - min)
+         * @deprecated It has been replaced by rssi_spread
+         */
+        @Deprecated
         public int rssi_spread;
 
-        /** average transmit rate */
+        /**RSSI spread (i.e. max - min), unit of 0.5 dB */
+        public int rssiSpread;
+
+        /**
+         * average transmit rate
+         * @deprecated It has been replaced by txRate
+         */
+        @Deprecated
         public int tx_rate;
 
-        /** average round trip time in nano second */
+        /** average transmit rate */
+        public int txRate;
+
+        /** average receiving rate */
+        public int rxRate;
+
+       /**
+        * average round trip time in nano second
+        * @deprecated  It has been replaced by rtt
+        */
+        @Deprecated
         public long rtt_ns;
 
-        /** standard deviation observed in round trip time */
+        /** average round trip time in 0.1 nano second */
+        public long rtt;
+
+        /**
+         * standard deviation observed in round trip time
+         * @deprecated It has been replaced by rttStandardDeviation
+         */
+        @Deprecated
         public long rtt_sd_ns;
 
-        /** spread (i.e. max - min) round trip time */
+        /** standard deviation of RTT in 0.1 ns */
+        public long rttStandardDeviation;
+
+        /**
+         * spread (i.e. max - min) round trip time
+         * @deprecated It has been replaced by rttSpread
+         */
+        @Deprecated
         public long rtt_spread_ns;
 
-        /** average distance in centimeter, computed based on rtt_ns */
+        /** spread (i.e. max - min) RTT in 0.1 ns */
+        public long rttSpread;
+
+        /**
+         * average distance in centimeter, computed based on rtt_ns
+         * @deprecated It has been replaced by distance
+         */
+        @Deprecated
         public int distance_cm;
 
-        /** standard deviation observed in distance */
+        /** average distance in cm, computed based on rtt */
+        public int distance;
+
+        /**
+         * standard deviation observed in distance
+         * @deprecated It has been replaced with distanceStandardDeviation
+         */
+        @Deprecated
         public int distance_sd_cm;
 
-        /** spread (i.e. max - min) distance */
+        /** standard deviation observed in distance in cm*/
+        public int distanceStandardDeviation;
+
+        /**
+         * spread (i.e. max - min) distance
+         * @deprecated It has been replaced by distanceSpread
+         */
+        @Deprecated
         public int distance_spread_cm;
+
+        /** spread (i.e. max - min) distance in cm */
+        public int distanceSpread;
+
+        /** the duration of this measurement burst*/
+        public int burstDuration;
+
+        /** LCI information Element*/
+        wifiInformationElement LCI;
+
+        /** LCR information Element*/
+        wifiInformationElement LCR;
     }
 
 
@@ -228,18 +590,28 @@
                 dest.writeInt(mResults.length);
                 for (RttResult result : mResults) {
                     dest.writeString(result.bssid);
+                    dest.writeInt(result.burstNumber);
+                    dest.writeInt(result.measurementFrameNumber);
+                    dest.writeInt(result.successMeasurementFrameNumber);
+                    dest.writeInt(result.frameNumberPerBurstPeer);
                     dest.writeInt(result.status);
-                    dest.writeInt(result.requestType);
+                    dest.writeInt(result.measurementType);
+                    dest.writeInt(result.retryAfterDuration);
                     dest.writeLong(result.ts);
                     dest.writeInt(result.rssi);
-                    dest.writeInt(result.rssi_spread);
-                    dest.writeInt(result.tx_rate);
-                    dest.writeLong(result.rtt_ns);
-                    dest.writeLong(result.rtt_sd_ns);
-                    dest.writeLong(result.rtt_spread_ns);
-                    dest.writeInt(result.distance_cm);
-                    dest.writeInt(result.distance_sd_cm);
-                    dest.writeInt(result.distance_spread_cm);
+                    dest.writeInt(result.rssiSpread);
+                    dest.writeInt(result.txRate);
+                    dest.writeLong(result.rtt);
+                    dest.writeLong(result.rttStandardDeviation);
+                    dest.writeLong(result.rttSpread);
+                    dest.writeInt(result.distance);
+                    dest.writeInt(result.distanceStandardDeviation);
+                    dest.writeInt(result.distanceSpread);
+                    dest.writeInt(result.burstDuration);
+                    //dest.writeInt(result.LCI.id);
+                    //dest.writeString(result.LCI.data);
+                    //dest.writeInt(result.LCR.id);
+                    //dest.writeString(result.LCR.data);
                 }
             } else {
                 dest.writeInt(0);
@@ -261,18 +633,28 @@
                         for (int i = 0; i < num; i++) {
                             results[i] = new RttResult();
                             results[i].bssid = in.readString();
+                            results[i].burstNumber = in.readInt();
+                            results[i].measurementFrameNumber = in.readInt();
+                            results[i].successMeasurementFrameNumber = in.readInt();
+                            results[i].frameNumberPerBurstPeer = in.readInt();
                             results[i].status = in.readInt();
-                            results[i].requestType = in.readInt();
+                            results[i].measurementType = in.readInt();
+                            results[i].retryAfterDuration = in.readInt();
                             results[i].ts = in.readLong();
                             results[i].rssi = in.readInt();
-                            results[i].rssi_spread = in.readInt();
-                            results[i].tx_rate = in.readInt();
-                            results[i].rtt_ns = in.readLong();
-                            results[i].rtt_sd_ns = in.readLong();
-                            results[i].rtt_spread_ns = in.readLong();
-                            results[i].distance_cm = in.readInt();
-                            results[i].distance_sd_cm = in.readInt();
-                            results[i].distance_spread_cm = in.readInt();
+                            results[i].rssiSpread = in.readInt();
+                            results[i].txRate = in.readInt();
+                            results[i].rtt = in.readLong();
+                            results[i].rttStandardDeviation = in.readLong();
+                            results[i].rttSpread = in.readLong();
+                            results[i].distance = in.readInt();
+                            results[i].distanceStandardDeviation = in.readInt();
+                            results[i].distanceSpread = in.readInt();
+                            results[i].burstDuration = in.readInt();
+                            //results[i].LCI.id = in.readInt();
+                            //results[i].LCI.data = in.readString();
+                            //results[i].LCR.id = in.readInt();
+                            //results[i].LCR.data = in.readString();
                         }
 
                         ParcelableRttResults parcelableResults = new ParcelableRttResults(results);
@@ -292,7 +674,70 @@
         public void onAborted();
     }
 
+    private boolean rttParamSanity(RttParams params, int index) {
+        if (mRttCapabilities == null) {
+            if(getRttCapabilities() == null) {
+                Log.e(TAG, "Can not get RTT capabilities");
+                //throw new IllegalStateException("RTT chip is not working");
+            }
+        }
+
+        if (params.deviceType != RTT_PEER_TYPE_AP) {
+            return false;
+        } else if (params.requestType != RTT_TYPE_ONE_SIDED && params.requestType !=
+                RTT_TYPE_TWO_SIDED) {
+            Log.e(TAG, "Request " + index + ": Illegal Request Type: " + params.requestType);
+            return false;
+        } else if (params.requestType == RTT_TYPE_ONE_SIDED &&
+                !mRttCapabilities.oneSidedRttSupported) {
+            Log.e(TAG, "Request " + index + ": One side RTT is not supported");
+            return false;
+        } else if (params.requestType == RTT_TYPE_TWO_SIDED &&
+                !mRttCapabilities.twoSided11McRttSupported) {
+            Log.e(TAG, "Request " + index + ": two side RTT is not supported");
+            return false;
+        } else if ( params.numberBurst <= 0 ) {
+            Log.e(TAG, "Request " + index + ": Illegal number of burst: " + params.numberBurst);
+            return false;
+        } else if (params.numberBurst >  1 && params.interval <= 0) {
+            Log.e(TAG, "Request " + index + ": Illegal interval value: " + params.interval);
+            return false;
+        } else if (params.numSamplesPerBurst <= 0) {
+            Log.e(TAG, "Request " + index + ": Illegal sample number per burst: " +
+                    params.numSamplesPerBurst);
+            return false;
+        } else if (params.numRetriesPerMeasurementFrame < 0 || params.numRetriesPerFTMR < 0) {
+            Log.e(TAG, "Request " + index + ": Illegal retry number");
+            return false;
+        } else if (params.LCIRequest && !mRttCapabilities.lciSupported) {
+            Log.e(TAG, "Request " + index + ": LCI is not supported");
+            return false;
+        } else if (params.LCRRequest && !mRttCapabilities.lcrSupported) {
+            Log.e(TAG, "Request " + index + ": LCR is not supported");
+            return false;
+        } else if (params.burstTimeout <= 0){
+            Log.e(TAG, "Request " + index + ": Illegal burst timeout: " + params.burstTimeout);
+            return false;
+        } else if ((params.preamble & mRttCapabilities.preambleSupported) == 0) {
+            Log.e(TAG, "Request " + index + ": Do not support this preamble: " + params.preamble);
+            return false;
+        } else if ((params.bandwidth & mRttCapabilities.bwSupported) == 0) {
+            Log.e(TAG, "Request " + index + ": Do not support this bandwidth: " + params.bandwidth);
+            return false;
+        }
+
+        return true;
+    }
+
     public void startRanging(RttParams[] params, RttListener listener) {
+        int index  = 0;
+        for(RttParams rttParam : params) {
+            if (!rttParamSanity(rttParam, index)) {
+                throw new IllegalArgumentException("RTT Request Parameter Illegal");
+            }
+            index++;
+        }
+
         validateChannel();
         ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
         sAsyncChannel.sendMessage(CMD_OP_START_RANGING,
@@ -315,12 +760,14 @@
 
     private Context mContext;
     private IRttManager mService;
+    private RttCapabilities mRttCapabilities;
 
     private static final int INVALID_KEY = 0;
     private static int sListenerKey = 1;
 
     private static final SparseArray sListenerMap = new SparseArray();
     private static final Object sListenerMapLock = new Object();
+    private static final Object sCapabilitiesLock = new Object();
 
     private static AsyncChannel sAsyncChannel;
     private static CountDownLatch sConnected;
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index b4f4927..e8a51e3 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -16,8 +16,6 @@
 
 package android.net.wifi;
 
-import android.net.wifi.passpoint.WifiPasspointInfo;
-import android.net.wifi.passpoint.WifiPasspointManager;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -215,11 +213,19 @@
     public int distanceSdCm;
 
     /**
-     * Passpoint ANQP information. This is not fetched automatically.
-     * Use {@link WifiPasspointManager#requestAnqpInfo} to request ANQP info.
-     * {@hide}
+     * Indicates if the scan result represents a passpoint AP
      */
-    public WifiPasspointInfo passpoint;
+    public boolean passpointNetwork;
+
+    /**
+     * Indicates if venue name
+     */
+    public String venueName;
+
+    /**
+     * Indicates operator name
+     */
+    public String operatorFriendlyName;
 
     /**
      * {@hide}
@@ -292,6 +298,7 @@
         this.centerFreq0 = UNSPECIFIED;
         this.centerFreq1 = UNSPECIFIED;
         this.is80211McRTTResponder = false;
+        this.passpointNetwork = false;
     }
 
     /** {@hide} */
@@ -310,6 +317,7 @@
         this.centerFreq0 = UNSPECIFIED;
         this.centerFreq1 = UNSPECIFIED;
         this.is80211McRTTResponder = false;
+        this.passpointNetwork = false;
     }
 
     /** {@hide} */
@@ -329,6 +337,7 @@
         this.centerFreq0 = centerFreq0;
         this.centerFreq1 = centerFreq1;
         this.is80211McRTTResponder = is80211McRTTResponder;
+        this.passpointNetwork = false;
     }
 
     /** copy constructor {@hide} */
@@ -348,13 +357,15 @@
             distanceCm = source.distanceCm;
             distanceSdCm = source.distanceSdCm;
             seen = source.seen;
-            passpoint = source.passpoint;
             autoJoinStatus = source.autoJoinStatus;
             untrusted = source.untrusted;
             numConnection = source.numConnection;
             numUsage = source.numUsage;
             numIpConfigFailures = source.numIpConfigFailures;
             isAutoJoinCandidate = source.isAutoJoinCandidate;
+            passpointNetwork = source.passpointNetwork;
+            venueName = source.venueName;
+            operatorFriendlyName = source.operatorFriendlyName;
         }
     }
 
@@ -388,7 +399,7 @@
         sb.append(", distanceSd: ").append((distanceSdCm != UNSPECIFIED ? distanceSdCm : "?")).
                 append("(cm)");
 
-        sb.append(", passpoint: ").append(passpoint != null ? "yes" : "no");
+        sb.append(", passpoint: ").append(passpointNetwork ? "yes" : "no");
         if (autoJoinStatus != 0) {
             sb.append(", status: ").append(autoJoinStatus);
         }
@@ -431,12 +442,10 @@
         dest.writeInt(numUsage);
         dest.writeInt(numIpConfigFailures);
         dest.writeInt(isAutoJoinCandidate);
-        if (passpoint != null) {
-            dest.writeInt(1);
-            passpoint.writeToParcel(dest, flags);
-        } else {
-            dest.writeInt(0);
-        }
+        dest.writeInt(passpointNetwork ? 1 : 0);
+        dest.writeString(venueName);
+        dest.writeString(operatorFriendlyName);
+
         if (informationElements != null) {
             dest.writeInt(informationElements.length);
             for (int i = 0; i < informationElements.length; i++) {
@@ -478,9 +487,9 @@
                 sr.numUsage = in.readInt();
                 sr.numIpConfigFailures = in.readInt();
                 sr.isAutoJoinCandidate = in.readInt();
-                if (in.readInt() == 1) {
-                    sr.passpoint = WifiPasspointInfo.CREATOR.createFromParcel(in);
-                }
+                sr.passpointNetwork = in.readInt() == 1;
+                sr.venueName = in.readString();
+                sr.operatorFriendlyName = in.readString();
                 int n = in.readInt();
                 if (n != 0) {
                     sr.informationElements = new InformationElement[n];
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
index 6263463..0f73342 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
+++ b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
@@ -26,12 +26,35 @@
  * @hide
  */
 public final class WifiActivityEnergyInfo implements Parcelable {
-    private final long mTimestamp;
-    private final int mStackState;
-    private final int mControllerTxTimeMs;
-    private final int mControllerRxTimeMs;
-    private final int mControllerIdleTimeMs;
-    private final int mControllerEnergyUsed;
+    /**
+     * @hide
+     */
+    public long mTimestamp;
+
+    /**
+     * @hide
+     */
+    public int mStackState;
+
+    /**
+     * @hide
+     */
+    public int mControllerTxTimeMs;
+
+    /**
+     * @hide
+     */
+    public int mControllerRxTimeMs;
+
+    /**
+     * @hide
+     */
+    public int mControllerIdleTimeMs;
+
+    /**
+     * @hide
+     */
+    public int mControllerEnergyUsed;
 
     public static final int STACK_STATE_INVALID = 0;
     public static final int STACK_STATE_STATE_ACTIVE = 1;
@@ -118,6 +141,7 @@
         return (int)mControllerIdleTimeMs;
     }
 
+
     /**
      * product of current(mA), voltage(V) and time(ms)
      * @return energy used
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 7e04f2b..11bdebb 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -973,13 +973,18 @@
             }
         }
 
-        if (FQDN != null) {
-            /* must have a providerFriendlyName */
-            if (providerFriendlyName == null) {
+        if (TextUtils.isEmpty(FQDN) == false) {
+            /* this is passpoint configuration; it must not have an SSID */
+            if (TextUtils.isEmpty(SSID) == false) {
+                return false;
+            }
+            /* this is passpoint configuration; it must have a providerFriendlyName */
+            if (TextUtils.isEmpty(providerFriendlyName)) {
                 return false;
             }
             /* this is passpoint configuration; it must have enterprise config */
-            if (enterpriseConfig == null) {
+            if (enterpriseConfig == null
+                    || enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.NONE ) {
                 return false;
             }
         }
@@ -989,6 +994,16 @@
     }
 
     /**
+     * Identify if this configuration represents a passpoint network
+     */
+    public boolean isPasspoint() {
+        return !TextUtils.isEmpty(FQDN)
+                && !TextUtils.isEmpty(providerFriendlyName)
+                && enterpriseConfig != null
+                && enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE;
+    }
+
+    /**
      * Helper function, identify if a configuration is linked
      * @hide
      */
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 36fc96b..440ad61 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -235,8 +235,10 @@
         public static final int SIM     = 4;
         /** EAP-Authentication and Key Agreement */
         public static final int AKA     = 5;
+        /** EAP-Authentication and Key Agreement Prime */
+        public static final int AKA_PRIME = 6;
         /** @hide */
-        public static final String[] strings = { "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA" };
+        public static final String[] strings = { "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA", "AKA'" };
 
         /** Prevent initialization */
         private Eap() {}
@@ -286,6 +288,7 @@
             case Eap.TTLS:
             case Eap.SIM:
             case Eap.AKA:
+            case Eap.AKA_PRIME:
                 mFields.put(EAP_KEY, Eap.strings[eapMethod]);
                 mFields.put(OPP_KEY_CACHING, "1");
                 break;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index b292c22..275c7d1 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -861,6 +861,14 @@
     public static final int WIFI_FEATURE_TDLS_OFFCHANNEL  = 0x2000;  // Support for TDLS off channel
     /** @hide */
     public static final int WIFI_FEATURE_EPR              = 0x4000;  // Enhanced power reporting
+    /** @hide */
+    public static final int WIFI_FEATURE_AP_STA            = 0x8000;  // Support for AP STA Concurrency
+    /** @hide */
+    public static final int WIFI_FEATURE_LINK_LAYER_STATS  = 0x10000; // Link layer stats collection
+    /** @hide */
+    public static final int WIFI_FEATURE_LOGGER            = 0x20000; // WiFi Logger
+    /** @hide */
+    public static final int WIFI_FEATURE_HAL_EPNO          = 0x40000; // WiFi PNO enhanced
 
     private int getSupportedFeatures() {
         try {
@@ -972,7 +980,7 @@
      * @return true if this adapter supports advanced power/performance counters
      */
     public boolean isEnhancedPowerReportingSupported() {
-        return isFeatureSupported(WIFI_FEATURE_EPR);
+        return isFeatureSupported(WIFI_FEATURE_LINK_LAYER_STATS);
     }
 
     /**
@@ -2598,6 +2606,48 @@
         }
     }
 
+    /**
+     * Set setting for allowing Scans when infrastructure is associated
+     * @hide
+     */
+    public void setAllowScansWhileAssociated(boolean enabled) {
+        try {
+            mService.setAllowScansWhileAssociated(enabled);
+        } catch (RemoteException e) {
 
+        }
+    }
 
+    /**
+     * Get setting for allowing Scans when infrastructure is associated
+     * @hide
+     */
+    public boolean getAllowScansWhileAssociated() {
+        try {
+            return mService.getAllowScansWhileAssociated();
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+
+    /**
+     * Resets all wifi manager settings back to factory defaults.
+     *
+     * @hide
+     */
+    public void factoryReset() {
+        // Enable wifi
+        setWifiEnabled(true);
+        // Delete all Wifi SSIDs
+        List<WifiConfiguration> networks = getConfiguredNetworks();
+        if (networks != null) {
+            for (WifiConfiguration config : networks) {
+                removeNetwork(config.networkId);
+            }
+            saveConfiguration();
+        }
+
+        // Turn mobile hotspot off
+        setWifiApEnabled(null, false);
+    }
 }
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
index b9b17eb..0245a3d 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
@@ -262,7 +262,7 @@
                 for (ScanResult sr : mAnqpRequest)
                     if (sr.BSSID.equals(result.bssid)) {
                         Log.d(TAG, "find hit " + result.bssid);
-                        sr.passpoint = result;
+                        /* sr.passpoint = result; */
                         mAnqpRequest.remove(sr);
                         Log.d(TAG, "mAnqpRequest.len=" + mAnqpRequest.size());
                         break;