Merge "TIF: Add COLUMN_INTERNAL_PROVIDER_FLAGX to TvContract.Programs"
diff --git a/api/current.txt b/api/current.txt
index 4d761e2..76b14ae 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -464,8 +464,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 +555,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 +588,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
@@ -651,9 +652,9 @@
     field public static final int hasCode = 16842764; // 0x101000c
     field public static final 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 deprecated int headerMonthTextAppearance = 16843926; // 0x1010496
     field public static final int headerTimeTextAppearance = 16843935; // 0x101049f
     field public static final int headerYearTextAppearance = 16843928; // 0x1010498
     field public static final int height = 16843093; // 0x1010155
@@ -1094,8 +1095,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 +1115,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,7 +1353,7 @@
     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
@@ -1394,8 +1395,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
@@ -1454,7 +1455,7 @@
     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 yearListSelectorColor = 16843930; // 0x101049a
     field public static final int yesNoPreferenceStyle = 16842896; // 0x1010090
     field public static final int zAdjustment = 16843201; // 0x10101c1
   }
@@ -3431,6 +3432,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 +3463,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 +3980,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 +4029,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 +4318,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 +4327,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);
@@ -5664,11 +5681,13 @@
     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_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";
@@ -7395,6 +7414,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;
@@ -7572,6 +7592,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;
@@ -8645,6 +8666,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
@@ -8824,7 +8846,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;
@@ -15223,6 +15244,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 +15259,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 {
@@ -30178,6 +30203,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();
@@ -38156,34 +38182,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 {
@@ -39116,7 +39142,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);
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 54fff06..66245ff 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -536,8 +536,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 +627,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 +660,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
@@ -723,9 +724,9 @@
     field public static final int hasCode = 16842764; // 0x101000c
     field public static final 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 deprecated int headerMonthTextAppearance = 16843926; // 0x1010496
     field public static final int headerTimeTextAppearance = 16843935; // 0x101049f
     field public static final int headerYearTextAppearance = 16843928; // 0x1010498
     field public static final int height = 16843093; // 0x1010155
@@ -1170,8 +1171,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 +1191,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,7 +1429,7 @@
     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
@@ -1470,8 +1471,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
@@ -1530,7 +1531,7 @@
     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 yearListSelectorColor = 16843930; // 0x101049a
     field public static final int yesNoPreferenceStyle = 16842896; // 0x1010090
     field public static final int zAdjustment = 16843201; // 0x10101c1
   }
@@ -3514,6 +3515,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 +3546,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 +4070,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 +4119,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 +4408,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 +4417,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);
@@ -5768,11 +5785,13 @@
     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_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";
@@ -7601,6 +7620,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;
@@ -7784,6 +7804,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;
@@ -8860,6 +8881,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
@@ -9064,7 +9086,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;
@@ -9264,6 +9285,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 +9301,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";
@@ -16414,6 +16440,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 +16455,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 {
@@ -32537,6 +32567,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();
@@ -40816,34 +40847,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 {
@@ -41776,7 +41807,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);
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/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/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/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 9f81670..6d74905 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -81,7 +81,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 +185,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 +397,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 +431,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);
         }
diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java
index 25153fc..c435ccb 100644
--- a/core/java/android/app/AssistStructure.java
+++ b/core/java/android/app/AssistStructure.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.content.ComponentName;
+import android.content.res.Resources;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.Typeface;
@@ -53,7 +54,7 @@
 
     final ComponentName mActivityComponent;
 
-    final ArrayList<ViewNodeImpl> mRootViews = new ArrayList<>();
+    final ArrayList<WindowNode> mWindowNodes = new ArrayList<>();
 
     ViewAssistStructureImpl mTmpViewAssistStructureImpl = new ViewAssistStructureImpl();
     Bundle mTmpExtras = new Bundle();
@@ -178,7 +179,91 @@
         }
     }
 
-    final static class ViewNodeImpl {
+    /**
+     * Describes a window in the assist data.
+     */
+    static public class WindowNode {
+        final int mX;
+        final int mY;
+        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(assist, view);
+        }
+
+        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;
+
+        final int mId;
+        final String mIdPackage;
+        final String mIdType;
+        final String mIdEntry;
         final int mX;
         final int mY;
         final int mScrollX;
@@ -201,17 +286,34 @@
         final int mFlags;
 
         final String mClassName;
-        final String mContentDescription;
+        final CharSequence mContentDescription;
 
         final ViewNodeTextImpl mText;
         final Bundle mExtras;
 
-        final ViewNodeImpl[] mChildren;
+        final ViewNode[] mChildren;
 
-        ViewNodeImpl(AssistStructure assistStructure, View view, int left, int top,
-                CharSequence contentDescription) {
-            mX = left;
-            mY = top;
+        ViewNode(AssistStructure assistStructure, View view) {
+            mId = view.getId();
+            if (mId > 0 && (mId&0xff000000) != 0 && (mId&0x00ff0000) != 0
+                    && (mId&0x0000ffff) != 0) {
+                String pkg, type, entry;
+                try {
+                    Resources res = view.getResources();
+                    entry = res.getResourceEntryName(mId);
+                    type = res.getResourceTypeName(mId);
+                    pkg = res.getResourcePackageName(mId);
+                } catch (Resources.NotFoundException e) {
+                    entry = type = pkg = null;
+                }
+                mIdPackage = pkg;
+                mIdType = type;
+                mIdEntry = entry;
+            } else {
+                mIdPackage = mIdType = mIdEntry = null;
+            }
+            mX = view.getLeft();
+            mY = view.getTop();
             mScrollX = view.getScrollX();
             mScrollY = view.getScrollY();
             mWidth = view.getWidth();
@@ -249,7 +351,7 @@
             }
             mFlags = flags;
             mClassName = view.getAccessibilityClassName().toString();
-            mContentDescription = contentDescription != null ? contentDescription.toString() : null;
+            mContentDescription = view.getContentDescription();
             final ViewAssistStructureImpl viewData = assistStructure.mTmpViewAssistStructureImpl;
             final Bundle extras = assistStructure.mTmpExtras;
             view.onProvideAssistStructure(viewData, extras);
@@ -269,9 +371,9 @@
                 ViewGroup vg = (ViewGroup)view;
                 final int NCHILDREN = vg.getChildCount();
                 if (NCHILDREN > 0) {
-                    mChildren = new ViewNodeImpl[NCHILDREN];
+                    mChildren = new ViewNode[NCHILDREN];
                     for (int i=0; i<NCHILDREN; i++) {
-                        mChildren[i] = new ViewNodeImpl(assistStructure, vg.getChildAt(i));
+                        mChildren[i] = new ViewNode(assistStructure, vg.getChildAt(i));
                     }
                 } else {
                     mChildren = null;
@@ -281,11 +383,19 @@
             }
         }
 
-        ViewNodeImpl(AssistStructure assistStructure, View view) {
-            this(assistStructure, view, view.getLeft(), view.getTop(), view.getContentDescription());
-        }
-
-        ViewNodeImpl(Parcel in, PooledStringReader preader) {
+        ViewNode(Parcel in, PooledStringReader preader) {
+            mId = in.readInt();
+            if (mId != 0) {
+                mIdEntry = preader.readString();
+                if (mIdEntry != null) {
+                    mIdType = preader.readString();
+                    mIdPackage = preader.readString();
+                } else {
+                    mIdPackage = mIdType = null;
+                }
+            } else {
+                mIdPackage = mIdType = mIdEntry = null;
+            }
             mX = in.readInt();
             mY = in.readInt();
             mScrollX = in.readInt();
@@ -294,7 +404,7 @@
             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);
             } else {
@@ -303,9 +413,9 @@
             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 +423,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 +439,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 +457,141 @@
                 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];
         }
     }
 
@@ -488,12 +601,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 +610,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 +618,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 +673,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 +695,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 +715,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/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 682a468..2797bf0 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -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} 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}.
      *
@@ -411,6 +423,18 @@
         = "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} 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}.
      *
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 80b5e0b..39a70be 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3109,6 +3109,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/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 29befc8..8f17845 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -346,6 +346,11 @@
     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
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 3e5d362..c6d97f1 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -46,32 +46,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 +87,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 +123,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 +170,7 @@
 
     /**
      * Retrieve all applications that are marked as persistent.
-     * 
+     *
      * @return A List&lt;applicationInfo> containing one entry for each persistent
      *         application.
      */
@@ -178,7 +180,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
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..59a16da 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
@@ -1663,21 +1674,46 @@
             = "android.content.pm.extra.VERIFICATION_VERSION_CODE";
 
     /**
-     * The action used to request that the user approve a permission request
-     * from the application.
+     * The action used to request that the user approve a grant permissions
+     * 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 +2220,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
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index c443ff3..e5859d03 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;
         }
     }
 
@@ -410,7 +415,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 +574,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 requried 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 +1274,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 +1313,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 +1820,6 @@
                 }
                 implicitPerms.append(npi.name);
                 pkg.requestedPermissions.add(npi.name);
-                pkg.requestedPermissionsRequired.add(Boolean.TRUE);
             }
         }
         if (implicitPerms != null) {
@@ -1831,7 +1838,6 @@
                 final String perm = spi.newPerms[in];
                 if (!pkg.requestedPermissions.contains(perm)) {
                     pkg.requestedPermissions.add(perm);
-                    pkg.requestedPermissionsRequired.add(Boolean.TRUE);
                 }
             }
         }
@@ -1865,17 +1871,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 +1906,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 +1923,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 +2555,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);
@@ -3116,8 +3108,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;
@@ -4218,7 +4209,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/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index 841b09d..7d8dff3 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -464,6 +464,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{" +
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 06862d7..74b0a1c 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -1516,8 +1516,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);
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e6b39b5..de536bd 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5375,6 +5375,7 @@
             BACKUP_AUTO_RESTORE,
             ENABLED_ACCESSIBILITY_SERVICES,
             ENABLED_NOTIFICATION_LISTENERS,
+            ENABLED_INPUT_METHODS,
             TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
             TOUCH_EXPLORATION_ENABLED,
             ACCESSIBILITY_ENABLED,
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 4902a71..d46b6f5 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,7 @@
                 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
         int mCurWindowFlags = mWindowFlags;
         int mCurWindowPrivateFlags = mWindowPrivateFlags;
-        TypedValue mOutsetBottom;
+        int mOutsetBottomPx;
         final Rect mVisibleInsets = new Rect();
         final Rect mWinFrame = new Rect();
         final Rect mOverscanInsets = new Rect();
@@ -171,8 +168,6 @@
         final Rect mFinalStableInsets = new Rect();
         final Configuration mConfiguration = new Configuration();
 
-        private boolean mIsEmulator;
-        private boolean mIsCircularEmulator;
         private boolean mWindowIsRound;
 
         final WindowManager.LayoutParams mLayout
@@ -637,23 +632,13 @@
                         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;
+                        if (shouldUseBottomOutset) {
+                            mOutsetBottomPx = ScreenShapeHelper.getWindowOutsetBottomPx(
+                                    getResources().getDisplayMetrics(), windowStyle);
                         }
-                        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 +768,14 @@
                             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();
+                            if (mOutsetBottomPx != 0) {
                                 mFinalSystemInsets.bottom =
-                                        ( (int) mOutsetBottom.getDimension(metrics) )
-                                        + mIWallpaperEngine.mDisplayPadding.bottom;
+                                        mIWallpaperEngine.mDisplayPadding.bottom + mOutsetBottomPx;
                             }
                             WindowInsets insets = new WindowInsets(mFinalSystemInsets,
-                                    null, mFinalStableInsets, isRound);
+                                    null, mFinalStableInsets, mWindowIsRound);
                             onApplyWindowInsets(insets);
                         }
 
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/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index e790d4c..234de40 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;
 
@@ -120,8 +120,10 @@
     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
@@ -334,8 +336,6 @@
     /** Set to true once doDie() has been called. */
     private boolean mRemoved;
 
-    private boolean mIsEmulator;
-    private boolean mIsCircularEmulator;
     private final boolean mWindowIsRound;
 
     /**
@@ -392,8 +392,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 +1247,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;
     }
@@ -5590,11 +5588,6 @@
                         mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_WORLD, 200);
                     }
                 }
-
-                // detect emulator
-                mIsEmulator = Build.HARDWARE.contains("goldfish");
-                mIsCircularEmulator =
-                        SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false);
             }
         });
     }
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..7b8a979 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,84 @@
 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;
+    public static final int[] ATTRS_TEXT_COLOR = new int[]{com.android.internal.R.attr.textColor};
 
-    private SimpleDateFormat mYearFormat = new SimpleDateFormat("y", Locale.getDefault());
-    private SimpleDateFormat mDayFormat = new SimpleDateFormat("d", Locale.getDefault());
+    public static final int[] ATTRS_DISABLED_ALPHA = new int[]{
+            com.android.internal.R.attr.disabledAlpha};
 
-    private TextView mDayOfWeekView;
+    private SimpleDateFormat mYearFormat;
+    private SimpleDateFormat mMonthDayFormat;
 
-    /** Layout that contains the current month, day, and year. */
-    private LinearLayout mMonthDayYearLayout;
+    // Top-level container.
+    private ViewGroup mContainer;
 
-    /** Clickable layout that contains the current day and year. */
-    private LinearLayout mMonthAndDayLayout;
+    // Header views.
+    private TextView mHeaderYear;
+    private TextView mHeaderMonthDay;
 
-    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,71 +115,64 @@
         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));
+        }
+
+        // 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);
-
-        final ColorStateList yearBackgroundColor = a.getColorStateList(
-                R.styleable.DatePicker_yearListSelectorColor);
-        mYearPickerView.setYearBackgroundColor(yearBackgroundColor);
+        mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
+        mYearPickerView.setOnYearSelectedListener(mOnYearSelectedListener);
 
         final int yearTextAppearanceResId = a.getResourceId(
                 R.styleable.DatePicker_yearListItemTextAppearance, 0);
@@ -192,170 +180,201 @@
             mYearPickerView.setYearTextAppearance(yearTextAppearanceResId);
         }
 
-        final ColorStateList calendarTextColor = a.getColorStateList(
-                R.styleable.DatePicker_calendarTextColor);
-        mDayPickerView.setCalendarTextColor(calendarTextColor);
+        final int yearActivatedTextAppearanceResId = a.getResourceId(
+                R.styleable.DatePicker_yearListItemActivatedTextAppearance, 0);
+        if (yearActivatedTextAppearanceResId != 0) {
+            mYearPickerView.setYearActivatedTextAppearance(yearActivatedTextAppearanceResId);
+        }
 
-        final ColorStateList calendarDayBackgroundColor = a.getColorStateList(
-                R.styleable.DatePicker_calendarDayBackgroundColor);
-        mDayPickerView.setCalendarDayBackgroundColor(calendarDayBackgroundColor);
+        a.recycle();
 
-        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());
+        final Animation inAnim = new AlphaAnimation(0, 1);
+        inAnim.setDuration(ANIMATION_DURATION);
+        mAnimator.setInAnimation(inAnim);
 
-        final Animation animation = new AlphaAnimation(0.0f, 1.0f);
-        animation.setDuration(ANIMATION_DURATION);
-        mAnimator.setInAnimation(animation);
+        final Animation outAnim = new AlphaAnimation(1, 0);
+        outAnim.setDuration(ANIMATION_DURATION);
+        mAnimator.setOutAnimation(outAnim);
 
-        final Animation animation2 = new AlphaAnimation(1.0f, 0.0f);
-        animation2.setDuration(ANIMATION_DURATION);
-        mAnimator.setOutAnimation(animation2);
+        // Initialize for current locale. This also initializes the date, so no
+        // need to call onDateChanged.
+        onLocaleChanged(mCurrentLocale);
 
-        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 +402,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 +494,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 +530,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 +542,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 +557,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 +592,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 +614,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..4f9f09e
--- /dev/null
+++ b/core/java/android/widget/DayPickerAdapter.java
@@ -0,0 +1,283 @@
+/*
+ * 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.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.util.SparseArray;
+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<SimpleMonthView> mItems = new SparseArray<>();
+
+    private Calendar mSelectedDay = Calendar.getInstance();
+
+    private int mMonthTextAppearance;
+    private int mDayOfWeekTextAppearance;
+    private int mDayTextAppearance;
+
+    private ColorStateList mCalendarTextColor;
+    private ColorStateList mDaySelectorColor;
+    private ColorStateList mDayHighlightColor;
+
+    private OnDaySelectedListener mOnDaySelectedListener;
+
+    private int mFirstDayOfWeek;
+
+    public DayPickerAdapter(Context context) {
+        final TypedArray ta = context.obtainStyledAttributes(new int[] {
+                com.android.internal.R.attr.colorControlHighlight});
+        mDayHighlightColor = ta.getColorStateList(0);
+        ta.recycle();
+    }
+
+    public void setRange(Calendar min, Calendar max) {
+        mMinDate.setTimeInMillis(min.getTimeInMillis());
+        mMaxDate.setTimeInMillis(max.getTimeInMillis());
+
+        // 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);
+            monthView.setFirstDayOfWeek(weekStart);
+        }
+    }
+
+    public int getFirstDayOfWeek() {
+        return mFirstDayOfWeek;
+    }
+
+    /**
+     * Sets the selected day.
+     *
+     * @param day the selected day
+     */
+    public void setSelectedDay(Calendar day) {
+        final int oldPosition = getPositionForDay(mSelectedDay);
+        final int newPosition = getPositionForDay(day);
+
+        // Clear the old position if necessary.
+        if (oldPosition != newPosition) {
+            final SimpleMonthView oldMonthView = mItems.get(oldPosition, null);
+            if (oldMonthView != null) {
+                oldMonthView.setSelectedDay(-1);
+            }
+        }
+
+        // Set the new position.
+        final SimpleMonthView newMonthView = mItems.get(newPosition, null);
+        if (newMonthView != null) {
+            final int dayOfMonth = day.get(Calendar.DAY_OF_MONTH);
+            newMonthView.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() {
+        final int diffYear = mMaxDate.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR);
+        final int diffMonth = mMaxDate.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH);
+        return diffMonth + MONTHS_IN_YEAR * diffYear + 1;
+    }
+
+    @Override
+    public boolean isViewFromObject(View view, Object object) {
+        return view == object;
+    }
+
+    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(Calendar day) {
+        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 SimpleMonthView v = new SimpleMonthView(container.getContext());
+        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.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);
+
+        mItems.put(position, v);
+
+        container.addView(v);
+
+        return v;
+    }
+
+    @Override
+    public void destroyItem(ViewGroup container, int position, Object object) {
+        container.removeView(mItems.get(position));
+
+        mItems.remove(position);
+    }
+
+    @Override
+    public int getItemPosition(Object object) {
+        final int index = mItems.indexOfValue((SimpleMonthView) object);
+        if (index < 0) {
+            return mItems.keyAt(index);
+        }
+        return -1;
+    }
+
+    @Override
+    public CharSequence getPageTitle(int position) {
+        final SimpleMonthView v = mItems.get(position);
+        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);
+                }
+            }
+        }
+    };
+
+    public interface OnDaySelectedListener {
+        public void onDaySelected(DayPickerAdapter view, Calendar day);
+    }
+}
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index 65af45d..a7ae926 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -16,87 +16,177 @@
 
 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);
+        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);
+                }
+            }
+        });
+    }
+
+    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) {
+        // Set the selected day
+        if (setSelected) {
+            mSelectedDay.setTimeInMillis(timeInMillis);
+        }
+
+        final int position = getPositionFromDay(timeInMillis);
+        if (position != getCurrentItem()) {
+            setCurrentItem(position, animate);
+        }
     }
 
     public long getDate() {
@@ -137,7 +227,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 +239,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 +259,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/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..20aa972 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -641,7 +641,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();
@@ -1144,30 +1144,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/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..4e5a39a 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -18,8 +18,8 @@
 
 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;
@@ -29,8 +29,6 @@
 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 +36,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 +50,80 @@
  * 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 final int mMonthTextSize;
-    private final int mDayOfWeekTextSize;
-    private final int mDayTextSize;
-
-    /** Height of the header containing the month and day of week labels. */
-    private final int mMonthHeaderHeight;
+    private static final String DEFAULT_TITLE_FORMAT = "MMMMy";
+    private static final String DAY_OF_WEEK_FORMAT = "EEEEE";
 
     private final TextPaint mMonthPaint = new TextPaint();
     private final TextPaint mDayOfWeekPaint = new TextPaint();
     private final TextPaint mDayPaint = new TextPaint();
-
-    private final Paint mDayBackgroundPaint = new Paint();
-
-    /** Single-letter (when available) formatter for the day of week label. */
-    private SimpleDateFormat mDayFormatter = new SimpleDateFormat("EEEEE", Locale.getDefault());
-
-    // affects the padding on the sides of this view
-    private int mPadding = 0;
-
-    private String mDayOfWeekTypeface;
-    private String mMonthTypeface;
-
-    private int mMonth;
-    private int mYear;
-
-    // Quick reference to the width of this view, matches parent
-    private int mWidth;
-
-    // The height this view should draw at in pixels, set by height param
-    private final int mRowHeight;
-
-    // If this view contains the today
-    private boolean mHasToday = false;
-
-    // Which day is selected [0-6] or -1 if no day is selected
-    private int mActivatedDay = -1;
-
-    // Which day is today [0-6] or -1 if no day is today
-    private int mToday = DEFAULT_SELECTED_DAY;
-
-    // Which day of the week to start on [0-6]
-    private int mWeekStart = DEFAULT_WEEK_START;
-
-    // How many days to display
-    private int mNumDays = DEFAULT_NUM_DAYS;
-
-    // The number of days + a spot for week number if it is displayed
-    private int mNumCells = mNumDays;
-
-    private int mDayOfWeekStart = 0;
-
-    // First enabled day
-    private int mEnabledDayStart = 1;
-
-    // Last enabled day
-    private int mEnabledDayEnd = 31;
+    private final Paint mDaySelectorPaint = new Paint();
+    private final Paint mDayHighlightPaint = new Paint();
 
     private final Calendar mCalendar = Calendar.getInstance();
     private final Calendar mDayLabelCalendar = Calendar.getInstance();
 
     private final MonthViewTouchHelper mTouchHelper;
 
-    private int mNumRows = DEFAULT_NUM_ROWS;
+    private final SimpleDateFormat mTitleFormatter;
+    private final SimpleDateFormat mDayOfWeekFormatter;
 
-    // Optional listener for handling day click actions
+    private CharSequence mTitle;
+
+    private int mMonth;
+    private int mYear;
+
+    private int mPaddedWidth;
+    private int mPaddedHeight;
+
+    private final int mMonthHeight;
+    private final int mDayOfWeekHeight;
+    private final int mDayHeight;
+    private final int mCellWidth;
+    private final int mDaySelectorRadius;
+
+    /** The day of month for the selected day, or -1 if no day is selected. */
+    private int mActivatedDay = -1;
+
+    /**
+     * The day of month for today, or -1 if the today is not in the current
+     * month.
+     */
+    private int mToday = DEFAULT_SELECTED_DAY;
+
+    /** The first day of the week (ex. Calendar.SUNDAY). */
+    private int mWeekStart = DEFAULT_WEEK_START;
+
+    /** The number of days (ex. 28) in the current month. */
+    private int mDaysInMonth;
+
+    /**
+     * The day of week (ex. Calendar.SUNDAY) for the first day of the current
+     * month.
+     */
+    private int mDayOfWeekStart;
+
+    /** The day of month for the first (inclusive) enabled day. */
+    private int mEnabledDayStart = 1;
+
+    /** The day of month for the last (inclusive) enabled day. */
+    private int mEnabledDayEnd = 31;
+
+    /** The number of week rows needed to display the current month. */
+    private int mNumWeeks = MAX_WEEKS_IN_MONTH;
+
+    /** 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 mTouchedDay = -1;
+
     public SimpleMonthView(Context context) {
         this(context, null);
     }
@@ -155,64 +140,123 @@
         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);
-
-        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);
+        mMonthHeight = res.getDimensionPixelSize(R.dimen.date_picker_month_height);
+        mDayOfWeekHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_of_week_height);
+        mDayHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_height);
+        mCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width);
+        mDaySelectorRadius = res.getDimensionPixelSize(R.dimen.date_picker_day_selector_radius);
 
         // 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);
+    }
+
+    /**
+     * 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) {
+        applyTextAppearance(mMonthPaint, resId);
+        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 +274,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 +295,124 @@
     @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) {
         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 touchedDay = getDayAtLocation(event.getX(), event.getY());
+                if (mTouchedDay != touchedDay) {
+                    mTouchedDay = touchedDay;
+                    invalidate();
                 }
                 break;
+
+            case MotionEvent.ACTION_UP:
+                final int clickedDay = getDayAtLocation(event.getX(), event.getY());
+                onDayClicked(clickedDay);
+                // Fall through.
+            case MotionEvent.ACTION_CANCEL:
+                // Reset touched day on stream end.
+                mTouchedDay = -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);
+
+        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 float cellWidthHalf = mPaddedWidth / (DAYS_IN_WEEK * 2);
+
+        // Vertically centered within the cell height.
+        final float lineHeight = mDayOfWeekPaint.ascent() + mDayOfWeekPaint.descent();
+        final float y = mMonthHeight + (mDayOfWeekHeight - lineHeight) / 2f;
+
+        for (int i = 0; i < DAYS_IN_WEEK; i++) {
+            final int calendarDay = (i + mWeekStart) % DAYS_IN_WEEK;
+            mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay);
+
+            final String dayLabel = mDayOfWeekFormatter.format(mDayLabelCalendar.getTime());
+            final float x = (2 * i + 1) * cellWidthHalf;
+            canvas.drawText(dayLabel, x, y, mDayOfWeekPaint);
+        }
+    }
+
+    /**
+     * Draws the month days.
+     */
+    private void drawDays(Canvas canvas) {
+        final int cellWidthHalf = mPaddedWidth / (DAYS_IN_WEEK * 2);
+
+        // Vertically centered within the cell height.
+        final float halfLineHeight = (mDayPaint.ascent() + mDayPaint.descent()) / 2;
+        float centerY = mMonthHeight + mDayOfWeekHeight + mDayHeight / 2f;
+
+        for (int day = 1, j = findDayOffset(); day <= mDaysInMonth; day++) {
+            final int x = (2 * j + 1) * cellWidthHalf;
+            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(x, centerY, mDaySelectorRadius, mDaySelectorPaint);
+            } else if (mTouchedDay == day) {
+                stateMask |= StateSet.VIEW_STATE_PRESSED;
+
+                // Adjust the circle to be centered on the row.
+                canvas.drawCircle(x, centerY, 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);
+            }
+            mDayPaint.setColor(dayTextColor);
+
+            canvas.drawText("" + day, x, centerY - halfLineHeight, mDayPaint);
+
+            j++;
+
+            if (j == DAYS_IN_WEEK) {
+                j = 0;
+                centerY += mDayHeight;
+            }
+        }
     }
 
     private static boolean isValidDayOfWeek(int day) {
@@ -288,18 +424,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 +480,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 +498,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,154 +540,118 @@
     }
 
     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 = mDayHeight * mNumWeeks + mDayOfWeekHeight + mMonthHeight
+                + getPaddingTop() + getPaddingBottom();
+        final int preferredWidth = mCellWidth * 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;
+        mPaddedWidth = w - getPaddingLeft() - getPaddingRight();
+        mPaddedHeight = w - getPaddingTop() - getPaddingBottom();
 
         // 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 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) or -1 if the position wasn't in a
+     *         valid day
      */
-    private int getDayFromLocation(float x, float y) {
-        int dayStart = mPadding;
-        if (x < dayStart || x > mWidth - mPadding) {
+    private int getDayAtLocation(float x, float y) {
+        final int paddedX = (int) (x - getPaddingLeft() + 0.5f);
+        if (paddedX < 0 || paddedX >= mPaddedWidth) {
             return -1;
         }
-        // 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 headerHeight = mMonthHeight + mDayOfWeekHeight;
+        final int paddedY = (int) (y - getPaddingTop() + 0.5f);
+        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 day the day of the month
+     * @param outBounds the rect to populate with bounds
+     */
+    private boolean getBoundsForDay(int day, Rect outBounds) {
+        if (day < 1 || day > mDaysInMonth) {
+            return false;
+        }
+
+        final int index = day - 1 + findDayOffset();
+        final int row = index / DAYS_IN_WEEK;
+        final int col = index % DAYS_IN_WEEK;
+
+        final int headerHeight = mMonthHeight + mDayOfWeekHeight;
+        final int paddedY = row * mDayHeight + headerHeight;
+        final int paddedX = col * mPaddedWidth;
+
+        final int y = paddedY + getPaddingTop();
+        final int x = paddedX + getPaddingLeft();
+
+        final int cellHeight = mDayHeight;
+        final int cellWidth = mPaddedWidth / DAYS_IN_WEEK;
+        outBounds.set(x, y, (x + cellWidth), (y + cellHeight));
+
+        return true;
+    }
+
+    /**
      * 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 void onDayClicked(int day) {
         if (mOnDayClickListener != null) {
             Calendar date = Calendar.getInstance();
             date.set(mYear, mMonth, day);
@@ -530,44 +663,6 @@
     }
 
     /**
-     * @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.
-     *
-     * @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
-     */
-    boolean restoreAccessibilityFocus(Calendar day) {
-        if ((day.get(Calendar.YEAR) != mYear) || (day.get(Calendar.MONTH) != mMonth) ||
-                (day.get(Calendar.DAY_OF_MONTH) > mNumCells)) {
-            return false;
-        }
-        mTouchHelper.setFocusedVirtualView(day.get(Calendar.DAY_OF_MONTH));
-        return true;
-    }
-
-    /**
      * Provides a virtual view hierarchy for interfacing with an accessibility
      * service.
      */
@@ -581,24 +676,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 = getDayAtLocation(x, y);
             if (day >= 0) {
                 return day;
             }
@@ -607,7 +687,7 @@
 
         @Override
         protected void getVisibleVirtualViews(IntArray virtualViewIds) {
-            for (int day = 1; day <= mNumCells; day++) {
+            for (int day = 1; day <= mDaysInMonth; day++) {
                 virtualViewIds.add(day);
             }
         }
@@ -619,11 +699,20 @@
 
         @Override
         protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) {
-            getItemBounds(virtualViewId, mTempRect);
+            final boolean hasBounds = getBoundsForDay(virtualViewId, mTempRect);
+
+            if (!hasBounds) {
+                // The day is invalid, kill the node.
+                mTempRect.setEmpty();
+                node.setContentDescription("");
+                node.setBoundsInParent(mTempRect);
+                node.setVisibleToUser(false);
+                return;
+            }
 
             node.setContentDescription(getItemDescription(virtualViewId));
             node.setBoundsInParent(mTempRect);
-            node.addAction(AccessibilityNodeInfo.ACTION_CLICK);
+            node.addAction(AccessibilityAction.ACTION_CLICK);
 
             if (virtualViewId == mActivatedDay) {
                 node.setSelected(true);
@@ -636,7 +725,7 @@
                 Bundle arguments) {
             switch (action) {
                 case AccessibilityNodeInfo.ACTION_CLICK:
-                    onDayClick(virtualViewId);
+                    onDayClicked(virtualViewId);
                     return true;
             }
 
@@ -644,26 +733,6 @@
         }
 
         /**
-         * Calculates the bounding rectangle of a given time object.
-         *
-         * @param day The day to calculate bounds for
-         * @param rect The rectangle in which to store the bounds
-         */
-        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));
-
-            rect.set(x, y, (x + cellWidth), (y + cellHeight));
-        }
-
-        /**
          * 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.
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/YearPickerView.java b/core/java/android/widget/YearPickerView.java
index 6f0465f..7bd502e 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,187 @@
         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();
-    }
-
-    public void init(DatePickerController controller) {
-        mController = controller;
-        mController.registerOnDateChangedListener(this);
-
-        updateAdapterData();
-
-        onDateChanged();
-    }
-
-    public void setYearBackgroundColor(ColorStateList yearBackgroundColor) {
-        mYearActivatedColor = yearBackgroundColor.getColorForState(
-                StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0);
-        invalidate();
+        mAdapter.setRange(min, max);
     }
 
     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 void setYearActivatedTextAppearance(int resId) {
+        mAdapter.setItemActivatedTextAppearance(resId);
     }
 
-    @Override
-    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-        mController.tryVibrate();
-        if (position != mSelectedPosition) {
-            mSelectedPosition = position;
-            mAdapter.notifyDataSetChanged();
-        }
-        mController.onYearSelected(mAdapter.getItem(position));
-    }
+    private static class YearAdapter extends BaseAdapter {
+        private static final int ITEM_LAYOUT = R.layout.year_label_text_view;
 
-    private class YearAdapter extends ArrayAdapter<Integer> {
+        private final LayoutInflater mInflater;
+
+        private int mActivatedYear;
+        private int mMinYear;
+        private int mCount;
+
         private int mItemTextAppearanceResId;
+        private int mItemActivatedTextAppearanceResId;
 
-        public YearAdapter(Context context, int resource) {
-            super(context, resource);
+        public YearAdapter(Context context) {
+            mInflater = LayoutInflater.from(context);
         }
 
-        @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);
+        public void setRange(Calendar minDate, Calendar maxDate) {
+            final int minYear = minDate.get(Calendar.YEAR);
+            final int count = maxDate.get(Calendar.YEAR) - minYear + 1;
 
-            final int year = getItem(position);
-            final boolean selected = mController.getSelectedDay().get(Calendar.YEAR) == year;
-            v.setActivated(selected);
+            if (mMinYear != minYear || mCount != count) {
+                mMinYear = minYear;
+                mCount = count;
+                notifyDataSetInvalidated();
+            }
+        }
 
-            return v;
+        public boolean setSelection(int year) {
+            if (mActivatedYear != year) {
+                mActivatedYear = year;
+                notifyDataSetChanged();
+                return true;
+            }
+            return false;
         }
 
         public void setItemTextAppearance(int resId) {
             mItemTextAppearanceResId = resId;
+            notifyDataSetChanged();
         }
-    }
 
-    public void postSetSelectionCentered(final int position) {
-        postSetSelectionFromTop(position, mViewSize / 2 - mChildSize / 2);
-    }
+        public void setItemActivatedTextAppearance(int resId) {
+            mItemActivatedTextAppearanceResId = resId;
+            notifyDataSetChanged();
+        }
 
-    public void postSetSelectionFromTop(final int position, final int offset) {
-        post(new Runnable() {
+        @Override
+        public int getCount() {
+            return mCount;
+        }
 
-            @Override
-            public void run() {
-                setSelectionFromTop(position, offset);
-                requestLayout();
+        @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) {
+            if (convertView == null) {
+                convertView = mInflater.inflate(ITEM_LAYOUT, parent, false);
             }
-        });
+
+            final int year = getYearForPosition(position);
+            final boolean activated = mActivatedYear == year;
+
+            final int textAppearanceResId;
+            if (activated && mItemActivatedTextAppearanceResId != 0) {
+                textAppearanceResId = mItemActivatedTextAppearanceResId;
+            } else {
+                textAppearanceResId = mItemTextAppearanceResId;
+            }
+
+            final TextView v = (TextView) convertView;
+            v.setText("" + year);
+            v.setTextAppearance(v.getContext(), textAppearanceResId);
+            v.setActivated(activated);
+            return v;
+        }
+
+        @Override
+        public int getItemViewType(int position) {
+            return 0;
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            return 1;
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return false;
+        }
+
+        @Override
+        public boolean areAllItemsEnabled() {
+            return true;
+        }
+
+        @Override
+        public boolean isEnabled(int position) {
+            return true;
+        }
     }
 
     public int getFirstPositionOffset() {
@@ -177,22 +250,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/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/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/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index f916e6f..8018942 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -137,7 +137,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 +162,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 +187,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 +200,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 +339,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;
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 441af15..34de6c5 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];
@@ -581,6 +580,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 +699,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");
@@ -811,6 +811,19 @@
         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);
+
     initArgs.version = JNI_VERSION_1_4;
     initArgs.options = mOptions.editArray();
     initArgs.nOptions = mOptions.size();
@@ -825,13 +838,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/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/res/res/color/date_picker_header_text_material.xml b/core/res/res/color/date_picker_header_text_material.xml
index cda894b..baa8958 100644
--- a/core/res/res/color/date_picker_header_text_material.xml
+++ b/core/res/res/color/date_picker_header_text_material.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/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/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/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_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..98ef1dd
--- /dev/null
+++ b/core/res/res/layout/date_picker_view_animator_material.xml
@@ -0,0 +1,38 @@
+<?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:paddingStart="@dimen/day_picker_padding_horizontal"
+        android:paddingEnd="@dimen/day_picker_padding_horizontal"
+        android:paddingTop="@dimen/day_picker_padding_top" />
+
+    <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/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-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-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 269afba..e7c6bee 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -203,8 +203,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>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 15797dd..b5576c5 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" />
@@ -4443,46 +4443,42 @@
     </declare-styleable>
 
     <declare-styleable name="DatePicker">
-        <!-- The first year (inclusive), for example "1940".
-             {@deprecated Use minDate instead.} -->
+        <!-- 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.} -->
+        <!-- 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. -->
         <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 +4488,19 @@
             <!-- Date picker with calendar to select the date. -->
             <enum name="calendar" value="2" />
         </attr>
+
+        <!-- @deprecated The text appearance for the month (ex. May) in the selected date header. -->
+        <attr name="headerMonthTextAppearance" format="reference" />
+        <!-- @deprecated The text appearance for the day of month (ex. 28) in the selected date header. -->
+        <attr name="headerDayOfMonthTextAppearance" format="reference" />
+        <!-- The text appearance for the year (ex. 2014) in the selected date header. -->
+        <attr name="headerYearTextAppearance" format="reference" />
+        <!-- @deprecated The background color for the header's day of week. -->
+        <attr name="dayOfWeekBackground" format="color" />
+        <!-- @deprecated The text color for the header's day of week. -->
+        <attr name="dayOfWeekTextAppearance" format="reference" />
+        <!-- @deprecated The list year's selected circle color in the list. -->
+        <attr name="yearListSelectorColor" format="color" />
     </declare-styleable>
 
     <declare-styleable name="TwoLineListItem">
@@ -4709,35 +4718,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">
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c0b2cbe..aefb67c 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1039,6 +1039,10 @@
          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"/>
+
     <!-- 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
@@ -1169,8 +1173,8 @@
              @hide -->
         <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
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/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/public.xml b/core/res/res/values/public.xml
index e507b3d..5e4b039 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2651,4 +2651,5 @@
 
   <public type="attr" name="colorBackgroundFloating" />
 
+  <public type="attr" name="extractNativeLibs" />
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 19cae03..7672e93 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5042,18 +5042,6 @@
     <!-- 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] -->
@@ -5079,7 +5067,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_material.xml b/core/res/res/values/styles_material.xml
index a8ab18d..f1f7462 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -372,11 +372,13 @@
     <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>
@@ -406,14 +408,27 @@
     </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="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/date_picker_header_text_material</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 +502,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 +631,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">
@@ -647,11 +662,11 @@
         <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="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 +675,10 @@
         <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="internalLayout">@layout/date_picker_material</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="yearListItemActivatedTextAppearance">@style/TextAppearance.Material.DatePicker.List.YearLabel.Activated</item>
+        <item name="headerBackground">#ff555555</item>
     </style>
 
     <style name="Widget.Material.ActivityChooserView" parent="Widget.ActivityChooserView">
@@ -1021,24 +1029,18 @@
     <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="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="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..c4e9e8e 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1988,7 +1988,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 +1999,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" />
@@ -2039,19 +2036,24 @@
   <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" />
@@ -2115,7 +2117,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 +2168,10 @@
   <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="dimen" name="day_picker_padding_top"/>
+  <java-symbol type="dimen" name="date_picker_day_of_week_height"/>
 </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/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/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/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index f935bb1..f0b07a6 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -36,6 +36,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;
@@ -713,6 +714,8 @@
         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.addBlob(KeymasterDefs.KM_TAG_RSA_PUBLIC_EXPONENT,
+                RSAKeyGenParameterSpec.F4.toByteArray());
 
         KeyCharacteristics outCharacteristics = new KeyCharacteristics();
         int result = mKeyStore.generateKey(name, args, 0, outCharacteristics);
@@ -750,6 +753,8 @@
         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_RSA_PUBLIC_EXPONENT,
+                RSAKeyGenParameterSpec.F4.toByteArray());
 
         KeyCharacteristics outCharacteristics = new KeyCharacteristics();
         int result = mKeyStore.generateKey(name, args, 0, outCharacteristics);
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/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index cf1b441..b887855 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -1223,6 +1223,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 +1234,6 @@
                 }
                 mServiceHandler.obtainMessage(ServiceHandler.DO_NOTIFY_SESSION_CREATED, args)
                         .sendToTarget();
-                session.tune(TvContract.buildChannelUriForPassthroughInput(getHardwareInputId()));
             }
 
             @Override
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/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/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/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 34e4beb..d234701 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -257,8 +257,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Geen netwerk nie"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi af"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Geen gestoorde netwerke beskikbaar nie"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Saai uit"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Saai tans uit"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Onbenoemde toestel"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Gereed om uit te saai"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 104067a..2bcb2c1 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -261,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 9ac2a64..ee3f826 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -257,8 +257,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"No Network"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Off"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"No saved networks available"</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">"Unnamed device"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Ready to cast"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 9ac2a64..ee3f826 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -257,8 +257,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"No Network"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Off"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"No saved networks available"</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">"Unnamed device"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Ready to cast"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 9ac2a64..ee3f826 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -257,8 +257,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"No Network"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Off"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"No saved networks available"</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">"Unnamed device"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Ready to cast"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 398e0b0..18d8f76 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -257,8 +257,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 66a7df0..5bf8d13 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -257,8 +257,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Tidak Ada Jaringan"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi Mati"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Jaringan yang tersimpan tak tersedia"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"Transmisi"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Melakukan transmisi"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Perangkat tanpa nama"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Siap melakukan transmisi"</string>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index b5e1bfe..6ae0c71 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -257,8 +257,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 a767983..f7268c2 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -255,8 +255,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-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index b6feaa2..58e5239 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -257,8 +257,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">"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-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index 71247d9..3d5d213 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -257,8 +257,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">"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-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index becf69d..ef864de 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -257,8 +257,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-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 493c9e7..f60a85a 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -257,8 +257,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Walang Network"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Naka-off ang Wi-Fi"</string>
     <string name="quick_settings_wifi_detail_empty_text" msgid="2831702993995222755">"Walang available na naka-save na mga network"</string>
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"I-cast"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"Nagka-cast"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"Walang pangalang device"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"Handang mag-cast"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 78eadee..6636693 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -259,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/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/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/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
index ca08319..56801e5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java
@@ -124,9 +124,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) {
@@ -209,6 +206,7 @@
                 mLightDismissDrawable : mDarkDismissDrawable);
         mDismissButton.setContentDescription(String.format(mDismissContentDescription,
                 t.activityLabel));
+        mMoveTaskButton.setVisibility((mConfig.multiStackEnabled) ? View.VISIBLE : View.INVISIBLE);
     }
 
     /** Unbinds the bar view from the task */
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index 8562288..e8e942c 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -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,49 @@
      */
     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.
+     *
+     * @hide
+     * @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
+     */
+    public 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;
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/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/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 11c3ea6..fa1a1f1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -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;
@@ -3066,7 +3067,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];
@@ -6530,7 +6531,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 +6551,7 @@
             pid = tlsIdentity.pid;
         }
 
-        return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true);
+        return checkComponentPermission(permission, pid, uid, -1, true);
     }
 
     /**
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/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/pm/BasePermission.java b/services/core/java/com/android/server/pm/BasePermission.java
index 4f27408..ec290ef 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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 52411bf..668dd1a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -54,7 +54,6 @@
 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;
@@ -249,6 +248,9 @@
     private static final boolean DEBUG_DEXOPT = false;
     private static final boolean DEBUG_ABI_SELECTION = false;
 
+    private static final boolean RUNTIME_PERMISSIONS_ENABLED =
+            SystemProperties.getInt("ro.runtime.premissions.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;
@@ -321,10 +323,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 permission. */
+    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;
@@ -994,6 +1014,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.
@@ -1214,6 +1243,32 @@
         }
     }
 
+    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);
+            }
+        }
+    }
+
     Bundle extrasForInstallResult(PackageInstalledInfo res) {
         Bundle extras = null;
         switch (res.returnCode) {
@@ -1243,7 +1298,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 +1348,7 @@
         mOnlyCore = onlyCore;
         mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
         mMetrics = new DisplayMetrics();
-        mSettings = new Settings(context);
+        mSettings = new Settings(mContext, 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 +1429,7 @@
                     mSettings.mPermissions.put(perm.name, bp);
                 }
                 if (perm.gids != null) {
-                    bp.gids = appendInts(bp.gids, perm.gids);
+                    bp.setGids(perm.gids, perm.perUser);
                 }
             }
 
@@ -1832,14 +1887,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;
             }
 
@@ -1895,26 +1944,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;
+
+        PermissionsState permissionsState = ps.getPermissionsState();
+
+        final int[] gids = permissionsState.computeGids(userId);
+        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,
+        return PackageParser.generatePackageInfo(p, gids, flags,
+                ps.firstInstallTime, ps.lastUpdateTime, permissions,
                 state, userId);
     }
 
@@ -1986,6 +2030,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 +2047,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 +2434,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 +2474,7 @@
                 }
             }
         }
+
         return PackageManager.PERMISSION_DENIED;
     }
 
@@ -2620,120 +2681,114 @@
         }
     }
 
-    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 (!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");
+
         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;
+            final SettingBase 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: {
+                    killSettingPackagesForUser(sb, userId, KILL_APP_REASON_GIDS_CHANGED);
+                } break;
             }
+
+            // Not critical if that is lost - app has to request again.
+            mSettings.writeRuntimePermissionsForUserLPr(userId, false);
+
+            return true;
         }
     }
 
     @Override
-    public void revokePermission(String packageName, String permissionName) {
-        int changedAppId = -1;
+    public boolean revokePermission(String packageName, String name, int userId) {
+        if (!sUserManager.exists(userId)) {
+            return false;
+        }
+
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.GRANT_REVOKE_PERMISSIONS,
+                "revokePermission");
+
+        enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
+                "revokePermission");
 
         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;
+            final SettingBase 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;
-            }
-        }
 
-        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);
-                }
+            final PermissionsState permissionsState = sb.getPermissionsState();
+
+            if (permissionsState.revokeRuntimePermission(bp, userId) ==
+                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                return false;
             }
+
+            killSettingPackagesForUser(sb, userId, KILL_APP_REASON_PERMISSIONS_REVOKED);
+
+            // Critical, after this call all should never have the permission.
+            mSettings.writeRuntimePermissionsForUserLPr(userId, true);
+
+            return true;
         }
     }
 
@@ -2794,6 +2849,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 />
@@ -3875,9 +3970,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 {
@@ -6853,36 +6949,42 @@
 
     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;
+
+        PermissionsState permissionsState = ps.getPermissionsState();
+        PermissionsState origPermissions = permissionsState;
+
         boolean changedPermission = false;
 
         if (replace) {
             ps.permissionsFixed = false;
-            if (gp == ps) {
-                origPermissions = new ArraySet<String>(gp.grantedPermissions);
-                gp.grantedPermissions.clear();
-                gp.gids = mGlobalGids;
-            }
+            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) {
@@ -6894,10 +6996,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<>();
@@ -6905,65 +7008,108 @@
                 }
                 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 ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                        // For modern system apps dangerous permissions are install time ones.
+                        grant = GRANT_INSTALL;
+                    } else {
+                        if (origPermissions.hasInstallPermission(bp.name)) {
+                            // For legacy apps that became modern, install becomes runtime.
+                            grant = GRANT_UPGRADE;
+                        } 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 (grant != GRANT_DENIED) {
                 if (!isSystemApp(ps) && ps.permissionsFixed) {
                     // 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) {
+                            changedPermission = true;
+                        }
+                    } break;
+
+                    case GRANT_RUNTIME: {
+                        // Grant previously granted runtime permissions.
+                        for (int userId : UserManagerService.getInstance().getUserIds()) {
+                            // Make sure runtime permissions are loaded.
+                            if (origPermissions.hasRuntimePermission(bp.name, userId)) {
+                                if (permissionsState.grantRuntimePermission(bp, userId) !=
+                                        PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                                    changedPermission = true;
+                                }
+                            }
+                        }
+                    } break;
+
+                    case GRANT_UPGRADE: {
+                        // Grant runtime permissions for a previously held install permission.
+                        permissionsState.revokeInstallPermission(bp);
+                        for (int userId : UserManagerService.getInstance().getUserIds()) {
+                            // Make sure runtime permissions are loaded.
+                            if (permissionsState.grantRuntimePermission(bp, userId) !=
+                                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                                changedPermission = true;
+                            }
+                        }
+                    } 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)) {
+                if (permissionsState.revokeInstallPermission(bp) !=
+                        PermissionsState.PERMISSION_OPERATION_FAILURE) {
                     changedPermission = true;
-                    gp.gids = removeInts(gp.gids, bp.gids);
                     Slog.i(TAG, "Un-granting permission " + perm
                             + " from package " + pkg.packageName
                             + " (protectionLevel=" + bp.protectionLevel
@@ -6990,7 +7136,6 @@
             // changed.
             ps.permissionsFixed = true;
         }
-        ps.haveGids = true;
     }
 
     private boolean isNewPlatformPermissionForPackage(String perm, PackageParser.Package pkg) {
@@ -7011,7 +7156,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)
@@ -7026,10 +7171,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.
@@ -7063,7 +7205,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;
     }
@@ -10821,11 +10963,26 @@
                         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 == userId) {
+                                // If gids changed for this user, kill all affected packages.
+                                killSettingPackagesForUser(deletedPs, userIdToKill,
+                                        KILL_APP_REASON_GIDS_CHANGED);
+                            } else if (userIdToKill == UserHandle.USER_ALL) {
+                                // If gids changed for all users, kill them all - done.
+                                killSettingPackagesForUser(deletedPs, userIdToKill,
+                                        KILL_APP_REASON_GIDS_CHANGED);
+                                break;
+                            }
                         }
                     }
                     clearPackagePreferredActivitiesLPw(deletedPs.name, UserHandle.USER_ALL);
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 06d842a..889164c 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() {
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 4b8ca42..9e8b3df 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -29,7 +29,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
@@ -93,7 +93,6 @@
     PackageSignatures signatures = new PackageSignatures();
 
     boolean permissionsFixed;
-    boolean haveGids;
 
     PackageKeySetData keySetData = new PackageKeySetData();
 
@@ -147,7 +146,6 @@
         signatures = new PackageSignatures(base.signatures);
 
         permissionsFixed = base.permissionsFixed;
-        haveGids = base.haveGids;
         userState.clear();
         for (int i=0; i<base.userState.size(); i++) {
             userState.put(base.userState.keyAt(i),
@@ -160,7 +158,6 @@
         installerPackageName = base.installerPackageName;
 
         keySetData = new PackageKeySetData(base.keySetData);
-
     }
 
     void init(File codePath, File resourcePath, String legacyNativeLibraryPathString,
@@ -201,9 +198,7 @@
      * Make a shallow copy of this package settings.
      */
     public void copyFrom(PackageSettingBase base) {
-        grantedPermissions = base.grantedPermissions;
-        gids = base.gids;
-
+        getPermissionsState().copyFrom(base.getPermissionsState());
         primaryCpuAbiString = base.primaryCpuAbiString;
         secondaryCpuAbiString = base.secondaryCpuAbiString;
         cpuAbiOverrideString = base.cpuAbiOverrideString;
@@ -212,7 +207,6 @@
         lastUpdateTime = base.lastUpdateTime;
         signatures = base.signatures;
         permissionsFixed = base.permissionsFixed;
-        haveGids = base.haveGids;
         userState.clear();
         for (int i=0; i<base.userState.size(); i++) {
             userState.put(base.userState.keyAt(i), base.userState.valueAt(i));
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..3e0e342
--- /dev/null
+++ b/services/core/java/com/android/server/pm/PermissionsState.java
@@ -0,0 +1,526 @@
+/*
+ * 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;
+
+    private static final int[] USERS_ALL = {UserHandle.USER_ALL};
+
+    private 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) {
+        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) {
+        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/GrantedPermissions.java b/services/core/java/com/android/server/pm/SettingBase.java
similarity index 74%
rename from services/core/java/com/android/server/pm/GrantedPermissions.java
rename to services/core/java/com/android/server/pm/SettingBase.java
index e87546c..d350c09 100644
--- a/services/core/java/com/android/server/pm/GrantedPermissions.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -19,27 +19,26 @@
 import android.content.pm.ApplicationInfo;
 import android.util.ArraySet;
 
-class GrantedPermissions {
+abstract class SettingBase {
     int pkgFlags;
     int pkgPrivateFlags;
 
-    ArraySet<String> grantedPermissions = new ArraySet<String>();
+    private final PermissionsState mPermissionsState;
 
-    int[] gids;
-
-    GrantedPermissions(int pkgFlags, int pkgPrivateFlags) {
+    SettingBase(int pkgFlags, int pkgPrivateFlags) {
         setFlags(pkgFlags);
         setPrivateFlags(pkgPrivateFlags);
+        mPermissionsState = new PermissionsState();
     }
 
-    @SuppressWarnings("unchecked")
-    GrantedPermissions(GrantedPermissions base) {
+    SettingBase(SettingBase base) {
         pkgFlags = base.pkgFlags;
-        grantedPermissions = new ArraySet<>(base.grantedPermissions);
+        pkgPrivateFlags = base.pkgPrivateFlags;
+        mPermissionsState = new PermissionsState(base.mPermissionsState);
+    }
 
-        if (base.gids != null) {
-            gids = base.gids.clone();
-        }
+    public PermissionsState getPermissionsState() {
+        return mPermissionsState;
     }
 
     void setFlags(int pkgFlags) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b820d7e8..82aa74a 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -25,6 +25,7 @@
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.PACKAGE_INFO_GID;
 
+import android.content.Context;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ResolveInfo;
@@ -33,17 +34,27 @@
 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.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 +62,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;
@@ -134,6 +144,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,6 +154,9 @@
     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 =
@@ -161,6 +176,11 @@
     private static final String ATTR_INSTALLED = "inst";
     private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
 
+    private final Object mLock;
+    private final Context mContext;
+
+    private final RuntimePermissionPersistence mRuntimePermissionsPersistence;
+
     private final File mSettingsFilename;
     private final File mBackupSettingsFilename;
     private final File mPackageListFilename;
@@ -257,11 +277,16 @@
 
     public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages);
 
-    Settings(Context context) {
-        this(context, Environment.getDataDirectory());
+    Settings(Context context, Object lock) {
+        this(context, Environment.getDataDirectory(), lock);
     }
 
-    Settings(Context context, File dataDir) {
+    Settings(Context context, File dataDir, Object lock) {
+        mContext = context;
+        mLock = lock;
+
+        mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
+
         mSystemDir = new File(dataDir, "system");
         mSystemDir.mkdirs();
         FileUtils.setPermissions(mSystemDir.toString(),
@@ -460,7 +485,7 @@
                         bp.pendingInfo.packageName = newPkg;
                     }
                     bp.uid = 0;
-                    bp.gids = null;
+                    bp.setGids(null, false);
                 }
             }
         }
@@ -468,9 +493,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) {
@@ -589,7 +614,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) {
@@ -732,45 +757,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) {
@@ -895,7 +935,17 @@
     }
 
     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);
     }
 
     private File getUserPackagesStateBackupFile(int userId) {
@@ -912,15 +962,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);
         }
     }
 
@@ -1360,6 +1404,7 @@
                         }
                         serializer.endTag(null, TAG_DISABLED_COMPONENTS);
                     }
+
                     serializer.endTag(null, TAG_PACKAGE);
                 }
             }
@@ -1403,6 +1448,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() {
@@ -1594,13 +1691,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");
             }
 
@@ -1614,7 +1705,7 @@
                     serializer.endTag(null, "cleaning-package");
                 }
             }
-            
+
             if (mRenamedPackages.size() > 0) {
                 for (Map.Entry<String, String> e : mRenamedPackages.entrySet()) {
                     serializer.startTag(null, "renamed-package");
@@ -1623,7 +1714,7 @@
                     serializer.endTag(null, "renamed-package");
                 }
             }
-            
+
             mKeySetManagerService.writeKeySetManagerServiceLPr(serializer);
 
             serializer.endTag(null, "packages");
@@ -1662,7 +1753,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)
@@ -1718,6 +1809,8 @@
             }
 
             writeAllUsersPackageRestrictionsLPr();
+
+            writeAllRuntimePermissionsLPr();
             return;
 
         } catch(XmlPullParserException e) {
@@ -1770,26 +1863,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");
     }
 
@@ -1840,19 +1919,7 @@
         }
         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());
         }
 
         writeSigningKeySetsLPr(serializer, pkg.keySetData);
@@ -2161,9 +2228,11 @@
         } else {
             if (users == null) {
                 readPackageRestrictionsLPr(0);
+                mRuntimePermissionsPersistence.readStateForUserSyncLPr(UserHandle.USER_OWNER);
             } else {
                 for (UserInfo user : users) {
                     readPackageRestrictionsLPr(user.id);
+                    mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id);
                 }
             }
         }
@@ -2537,7 +2606,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);
@@ -2643,6 +2712,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
@@ -2651,9 +2721,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());
@@ -2711,7 +2780,7 @@
             if (primaryCpuAbiString == null && legacyCpuAbiString != null) {
                 primaryCpuAbiString = legacyCpuAbiString;
             }
-;
+
             version = parser.getAttributeValue(null, "version");
             if (version != null) {
                 try {
@@ -2902,7 +2971,6 @@
                     packageSetting.installStatus = PackageSettingBase.PKG_INSTALL_COMPLETE;
                 }
             }
-
             int outerDepth = parser.getDepth();
             int type;
             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
@@ -2919,8 +2987,9 @@
                     readEnabledComponentsLPw(packageSetting, parser, 0);
                 } else if (tagName.equals("sigs")) {
                     packageSetting.signatures.readXml(parser, mPastSignatures);
-                } else if (tagName.equals("perms")) {
-                    readGrantedPermissionsLPw(parser, packageSetting.grantedPermissions);
+                } else if (tagName.equals(TAG_PERMISSIONS)) {
+                    readInstallPermissionsLPr(parser,
+                            packageSetting.getPermissionsState());
                     packageSetting.permissionsFixed = true;
                 } else if (tagName.equals("proper-signing-keyset")) {
                     long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
@@ -3039,7 +3108,6 @@
                     "Error in package manager settings: package " + name + " has bad userId "
                             + idStr + " at " + parser.getPositionDescription());
         }
-        ;
 
         if (su != null) {
             int outerDepth = parser.getDepth();
@@ -3054,47 +3122,18 @@
                 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());
                     XmlUtils.skipCurrentTag(parser);
                 }
             }
-
         } 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();
@@ -3126,6 +3165,8 @@
         file = getUserPackagesStateBackupFile(userId);
         file.delete();
         removeCrossProfileIntentFiltersLPw(userId);
+
+        mRuntimePermissionsPersistence.onUserRemoved(userId);
     }
 
     void removeCrossProfileIntentFiltersLPw(int userId) {
@@ -3317,7 +3358,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];
@@ -3414,8 +3455,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);
         }
@@ -3525,10 +3566,15 @@
         }
         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(" 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=");
@@ -3546,6 +3592,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:");
@@ -3561,12 +3615,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) {
@@ -3652,7 +3700,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));
@@ -3688,14 +3737,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);
@@ -3730,4 +3786,373 @@
             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) {
+            mHandler.removeMessages(userId);
+            writePermissionsSync(userId);
+        }
+
+        public void writePermissionsForUserAsyncLPr(int userId) {
+            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 {
+            parser.next();
+            skipEmptyTextTags(parser);
+            if (!accept(parser, XmlPullParser.START_TAG, TAG_RUNTIME_PERMISSIONS)) {
+                return;
+            }
+
+            parser.next();
+
+            while (parsePackageLPr(parser, userId)
+                    || parseSharedUserLPr(parser, userId)) {
+                parser.next();
+            }
+
+            skipEmptyTextTags(parser);
+            expect(parser, XmlPullParser.END_TAG, TAG_RUNTIME_PERMISSIONS);
+        }
+
+        private boolean parsePackageLPr(XmlPullParser parser, int userId)
+                throws IOException, XmlPullParserException {
+            skipEmptyTextTags(parser);
+            if (!accept(parser, XmlPullParser.START_TAG, TAG_PACKAGE)) {
+                return false;
+            }
+
+            String name = parser.getAttributeValue(null, ATTR_NAME);
+
+            parser.next();
+
+            PackageSetting ps = mPackages.get(name);
+            if (ps != null) {
+                while (parsePermissionLPr(parser, ps.getPermissionsState(), userId)) {
+                    parser.next();
+                }
+            }
+
+            skipEmptyTextTags(parser);
+            expect(parser, XmlPullParser.END_TAG, TAG_PACKAGE);
+
+            return true;
+        }
+
+        private boolean parseSharedUserLPr(XmlPullParser parser, int userId)
+                throws IOException, XmlPullParserException {
+            skipEmptyTextTags(parser);
+            if (!accept(parser, XmlPullParser.START_TAG, TAG_SHARED_USER)) {
+                return false;
+            }
+
+            String name = parser.getAttributeValue(null, ATTR_NAME);
+
+            parser.next();
+
+            SharedUserSetting sus = mSharedUsers.get(name);
+            if (sus != null) {
+                while (parsePermissionLPr(parser, sus.getPermissionsState(), userId)) {
+                    parser.next();
+                }
+            }
+
+            skipEmptyTextTags(parser);
+            expect(parser, XmlPullParser.END_TAG, TAG_SHARED_USER);
+
+            return true;
+        }
+
+        private boolean parsePermissionLPr(XmlPullParser parser, PermissionsState permissionsState,
+                int userId) throws IOException, XmlPullParserException {
+            skipEmptyTextTags(parser);
+            if (!accept(parser, XmlPullParser.START_TAG, TAG_ITEM)) {
+                return false;
+            }
+
+            String name = parser.getAttributeValue(null, ATTR_NAME);
+
+            parser.next();
+
+            BasePermission bp = mPermissions.get(name);
+            if (bp != null) {
+                if (permissionsState.grantRuntimePermission(bp, userId) ==
+                        PermissionsState.PERMISSION_OPERATION_FAILURE) {
+                    Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name);
+                }
+            } else {
+                Slog.w(PackageManagerService.TAG, "Unknown permission:" + name);
+            }
+
+            skipEmptyTextTags(parser);
+            expect(parser, XmlPullParser.END_TAG, TAG_ITEM);
+
+            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;
+                }
+            } else if (parser.getName() != null) {
+                return false;
+            }
+            return true;
+        }
+
+        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/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 930ef9a..be6550c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4025,7 +4025,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 +4044,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/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/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4c80b07..45e122c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -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;
 
@@ -3700,41 +3701,70 @@
         }
     }
 
-    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();
+            int pos = windows.size() - 1;
+            while (pos >= 0) {
+                WindowState win = windows.get(pos);
+                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 +3834,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 +3920,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
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index bcd8d42..ae2c54b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -564,6 +564,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) {
@@ -843,8 +847,6 @@
 
             mSystemServiceManager.startService(TwilightService.class);
 
-            mSystemServiceManager.startService(UiModeManagerService.class);
-
             mSystemServiceManager.startService(JobSchedulerService.class);
 
             if (!disableNonCoreServices) {
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..4dc1131 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(), 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(), 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(), 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(), getContext().getFilesDir(), new Object());
         assertEquals(true, settings.readLPw(null, null, 0, false));
 
         // Enable/Disable a package
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..67a8c2b 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -191,13 +191,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();
     }
 
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
index 2ad23ed5..bdc1276 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/AssistVisualizer.java
@@ -47,12 +47,12 @@
     public void setAssistStructure(AssistStructure as) {
         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());
             }
         }
         invalidate();
@@ -79,9 +79,8 @@
         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);
             }
         }
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/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;