Merge "CEC: API setSystemAudioMode"
diff --git a/Android.mk b/Android.mk
index 2462d73..70e965b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -245,6 +245,7 @@
 	core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
 	core/java/android/view/IApplicationToken.aidl \
 	core/java/android/view/IAssetAtlas.aidl \
+	core/java/android/view/IGraphicsStats.aidl \
 	core/java/android/view/IInputFilter.aidl \
 	core/java/android/view/IInputFilterHost.aidl \
 	core/java/android/view/IOnKeyguardExitResult.aidl \
diff --git a/api/current.txt b/api/current.txt
index d0a445b..ce6bbfa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -304,8 +304,8 @@
     field public static final int alphabeticShortcut = 16843235; // 0x10101e3
     field public static final int alwaysDrawnWithCache = 16842991; // 0x10100ef
     field public static final int alwaysRetainTaskState = 16843267; // 0x1010203
-    field public static final int amPmBackgroundColor = 16843941; // 0x10104a5
-    field public static final int amPmTextColor = 16843940; // 0x10104a4
+    field public static final deprecated int amPmBackgroundColor = 16843941; // 0x10104a5
+    field public static final deprecated int amPmTextColor = 16843940; // 0x10104a4
     field public static final int ambientShadowAlpha = 16843966; // 0x10104be
     field public static final int angle = 16843168; // 0x10101a0
     field public static final int animateFirstView = 16843477; // 0x10102d5
@@ -330,6 +330,7 @@
     field public static final int autoStart = 16843445; // 0x10102b5
     field public static final deprecated int autoText = 16843114; // 0x101016a
     field public static final int autoUrlDetect = 16843404; // 0x101028c
+    field public static final int autoVerify = 16844010; // 0x10104ea
     field public static final int background = 16842964; // 0x10100d4
     field public static final int backgroundDimAmount = 16842802; // 0x1010032
     field public static final int backgroundDimEnabled = 16843295; // 0x101021f
@@ -651,13 +652,13 @@
     field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
     field public static final int hardwareAccelerated = 16843475; // 0x10102d3
     field public static final int hasCode = 16842764; // 0x101000c
-    field public static final int headerAmPmTextAppearance = 16843936; // 0x10104a0
+    field public static final deprecated int headerAmPmTextAppearance = 16843936; // 0x10104a0
     field public static final int headerBackground = 16843055; // 0x101012f
     field public static final deprecated int headerDayOfMonthTextAppearance = 16843927; // 0x1010497
     field public static final int headerDividersEnabled = 16843310; // 0x101022e
     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 deprecated int headerTimeTextAppearance = 16843935; // 0x101049f
+    field public static final deprecated int headerYearTextAppearance = 16843928; // 0x1010498
     field public static final int height = 16843093; // 0x1010155
     field public static final int hideOnContentScroll = 16843843; // 0x1010443
     field public static final int hint = 16843088; // 0x1010150
@@ -1456,7 +1457,7 @@
     field public static final int x = 16842924; // 0x10100ac
     field public static final int xlargeScreens = 16843455; // 0x10102bf
     field public static final int y = 16842925; // 0x10100ad
-    field public static final int yearListItemTextAppearance = 16843929; // 0x1010499
+    field public static final deprecated int yearListItemTextAppearance = 16843929; // 0x1010499
     field public static final deprecated int yearListSelectorColor = 16843930; // 0x101049a
     field public static final int yesNoPreferenceStyle = 16842896; // 0x1010090
     field public static final int zAdjustment = 16843201; // 0x10101c1
@@ -8253,6 +8254,8 @@
     field public static final int NO_MATCH_CATEGORY = -4; // 0xfffffffc
     field public static final int NO_MATCH_DATA = -2; // 0xfffffffe
     field public static final int NO_MATCH_TYPE = -1; // 0xffffffff
+    field public static final java.lang.String SCHEME_HTTP = "http";
+    field public static final java.lang.String SCHEME_HTTPS = "https";
     field public static final int SYSTEM_HIGH_PRIORITY = 1000; // 0x3e8
     field public static final int SYSTEM_LOW_PRIORITY = -1000; // 0xfffffc18
   }
@@ -8832,6 +8835,25 @@
     field public java.lang.String targetPackage;
   }
 
+  public final class IntentFilterVerificationInfo implements android.os.Parcelable {
+    ctor public IntentFilterVerificationInfo();
+    ctor public IntentFilterVerificationInfo(java.lang.String, java.lang.String[]);
+    ctor public IntentFilterVerificationInfo(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    ctor public IntentFilterVerificationInfo(android.os.Parcel);
+    method public int describeContents();
+    method public java.lang.String[] getDomains();
+    method public java.lang.String getDomainsString();
+    method public java.lang.String getPackageName();
+    method public int getStatus();
+    method public java.lang.String getStatusString();
+    method public static java.lang.String getStatusStringFromValue(int);
+    method public void readFromXml(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void setStatus(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    method public void writeToXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
+    field public static final android.os.Parcelable.Creator<android.content.pm.IntentFilterVerificationInfo> CREATOR;
+  }
+
   public class LabeledIntent extends android.content.Intent {
     ctor public LabeledIntent(android.content.Intent, java.lang.String, int, int);
     ctor public LabeledIntent(android.content.Intent, java.lang.String, java.lang.CharSequence, int);
@@ -9060,6 +9082,7 @@
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
     method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public abstract java.lang.String getNameForUid(int);
@@ -11138,6 +11161,7 @@
     field public static final int JPEG = 256; // 0x100
     field public static final int NV16 = 16; // 0x10
     field public static final int NV21 = 17; // 0x11
+    field public static final int PRIVATE = 34; // 0x22
     field public static final int RAW10 = 37; // 0x25
     field public static final int RAW_SENSOR = 32; // 0x20
     field public static final int RGB_565 = 4; // 0x4
@@ -22511,6 +22535,7 @@
     field public static java.lang.String DIRECTORY_RINGTONES;
     field public static final java.lang.String MEDIA_BAD_REMOVAL = "bad_removal";
     field public static final java.lang.String MEDIA_CHECKING = "checking";
+    field public static final java.lang.String MEDIA_EJECTING = "ejecting";
     field public static final java.lang.String MEDIA_MOUNTED = "mounted";
     field public static final java.lang.String MEDIA_MOUNTED_READ_ONLY = "mounted_ro";
     field public static final java.lang.String MEDIA_NOFS = "nofs";
@@ -26924,6 +26949,10 @@
     method public static android.renderscript.Element A_8(android.renderscript.RenderScript);
     method public static android.renderscript.Element BOOLEAN(android.renderscript.RenderScript);
     method public static android.renderscript.Element ELEMENT(android.renderscript.RenderScript);
+    method public static android.renderscript.Element F16(android.renderscript.RenderScript);
+    method public static android.renderscript.Element F16_2(android.renderscript.RenderScript);
+    method public static android.renderscript.Element F16_3(android.renderscript.RenderScript);
+    method public static android.renderscript.Element F16_4(android.renderscript.RenderScript);
     method public static android.renderscript.Element F32(android.renderscript.RenderScript);
     method public static android.renderscript.Element F32_2(android.renderscript.RenderScript);
     method public static android.renderscript.Element F32_3(android.renderscript.RenderScript);
@@ -27022,6 +27051,7 @@
     method public static android.renderscript.Element.DataType valueOf(java.lang.String);
     method public static final android.renderscript.Element.DataType[] values();
     enum_constant public static final android.renderscript.Element.DataType BOOLEAN;
+    enum_constant public static final android.renderscript.Element.DataType FLOAT_16;
     enum_constant public static final android.renderscript.Element.DataType FLOAT_32;
     enum_constant public static final android.renderscript.Element.DataType FLOAT_64;
     enum_constant public static final android.renderscript.Element.DataType MATRIX_2X2;
@@ -27295,11 +27325,14 @@
     method public static android.renderscript.RenderScript create(android.content.Context);
     method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType);
     method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType, int);
+    method public static android.renderscript.RenderScript createMultiContext(android.content.Context, android.renderscript.RenderScript.ContextType, int, int);
     method public void destroy();
     method public void finish();
     method public final android.content.Context getApplicationContext();
     method public android.renderscript.RenderScript.RSErrorHandler getErrorHandler();
     method public android.renderscript.RenderScript.RSMessageHandler getMessageHandler();
+    method public static long getMinorID();
+    method public static void releaseAllContexts();
     method public void sendMessage(int, int[]);
     method public void setErrorHandler(android.renderscript.RenderScript.RSErrorHandler);
     method public void setMessageHandler(android.renderscript.RenderScript.RSMessageHandler);
@@ -27381,9 +27414,12 @@
   public class Script extends android.renderscript.BaseObj {
     method public void bindAllocation(android.renderscript.Allocation, int);
     method protected android.renderscript.Script.FieldID createFieldID(int, android.renderscript.Element);
+    method protected android.renderscript.Script.InvokeID createInvokeID(int);
     method protected android.renderscript.Script.KernelID createKernelID(int, int, android.renderscript.Element, android.renderscript.Element);
     method protected void forEach(int, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.FieldPacker);
     method protected void forEach(int, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.FieldPacker, android.renderscript.Script.LaunchOptions);
+    method protected void forEach(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.FieldPacker);
+    method protected void forEach(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.FieldPacker, android.renderscript.Script.LaunchOptions);
     method public boolean getVarB(int);
     method public double getVarD(int);
     method public float getVarF(int);
@@ -27421,6 +27457,9 @@
   public static final class Script.FieldID extends android.renderscript.BaseObj {
   }
 
+  public static final class Script.InvokeID extends android.renderscript.BaseObj {
+  }
+
   public static final class Script.KernelID extends android.renderscript.BaseObj {
   }
 
@@ -27631,6 +27670,8 @@
     method public static android.renderscript.Type createX(android.renderscript.RenderScript, android.renderscript.Element, int);
     method public static android.renderscript.Type createXY(android.renderscript.RenderScript, android.renderscript.Element, int, int);
     method public static android.renderscript.Type createXYZ(android.renderscript.RenderScript, android.renderscript.Element, int, int, int);
+    method public int getArray(int);
+    method public int getArrayCount();
     method public int getCount();
     method public android.renderscript.Element getElement();
     method public int getX();
@@ -27644,6 +27685,7 @@
   public static class Type.Builder {
     ctor public Type.Builder(android.renderscript.RenderScript, android.renderscript.Element);
     method public android.renderscript.Type create();
+    method public android.renderscript.Type.Builder setArray(int, int);
     method public android.renderscript.Type.Builder setFaces(boolean);
     method public android.renderscript.Type.Builder setMipmaps(boolean);
     method public android.renderscript.Type.Builder setX(int);
@@ -30468,6 +30510,7 @@
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
     method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public java.lang.String getNameForUid(int);
@@ -37776,7 +37819,6 @@
     method public boolean shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String);
     field public static final int ERROR_AUTHENTICATION = -4; // 0xfffffffc
     field public static final int ERROR_BAD_URL = -12; // 0xfffffff4
-    field public static final int ERROR_BLOCKED = -16; // 0xfffffff0
     field public static final int ERROR_CONNECT = -6; // 0xfffffffa
     field public static final int ERROR_FAILED_SSL_HANDSHAKE = -11; // 0xfffffff5
     field public static final int ERROR_FILE = -13; // 0xfffffff3
@@ -38139,7 +38181,7 @@
     field protected android.database.Cursor mDataCursor;
   }
 
-  public class AnalogClock extends android.view.View {
+  public deprecated class AnalogClock extends android.view.View {
     ctor public AnalogClock(android.content.Context);
     ctor public AnalogClock(android.content.Context, android.util.AttributeSet);
     ctor public AnalogClock(android.content.Context, android.util.AttributeSet, int);
@@ -40200,12 +40242,16 @@
     ctor public TimePicker(android.content.Context, android.util.AttributeSet);
     ctor public TimePicker(android.content.Context, android.util.AttributeSet, int);
     ctor public TimePicker(android.content.Context, android.util.AttributeSet, int, int);
-    method public java.lang.Integer getCurrentHour();
-    method public java.lang.Integer getCurrentMinute();
+    method public deprecated java.lang.Integer getCurrentHour();
+    method public deprecated java.lang.Integer getCurrentMinute();
+    method public int getHour();
+    method public int getMinute();
     method public boolean is24HourView();
-    method public void setCurrentHour(java.lang.Integer);
-    method public void setCurrentMinute(java.lang.Integer);
+    method public deprecated void setCurrentHour(java.lang.Integer);
+    method public deprecated void setCurrentMinute(java.lang.Integer);
+    method public void setHour(int);
     method public void setIs24HourView(java.lang.Boolean);
+    method public void setMinute(int);
     method public void setOnTimeChangedListener(android.widget.TimePicker.OnTimeChangedListener);
   }
 
diff --git a/api/system-current.txt b/api/system-current.txt
index 0e3047a..6053603 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -103,6 +103,7 @@
     field public static final java.lang.String INSTALL_LOCATION_PROVIDER = "android.permission.INSTALL_LOCATION_PROVIDER";
     field public static final java.lang.String INSTALL_PACKAGES = "android.permission.INSTALL_PACKAGES";
     field public static final java.lang.String INSTALL_SHORTCUT = "com.android.launcher.permission.INSTALL_SHORTCUT";
+    field public static final java.lang.String INTENT_FILTER_VERIFICATION_AGENT = "android.permission.INTENT_FILTER_VERIFICATION_AGENT";
     field public static final java.lang.String INTERACT_ACROSS_USERS = "android.permission.INTERACT_ACROSS_USERS";
     field public static final java.lang.String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW";
     field public static final java.lang.String INTERNET = "android.permission.INTERNET";
@@ -375,8 +376,8 @@
     field public static final int alphabeticShortcut = 16843235; // 0x10101e3
     field public static final int alwaysDrawnWithCache = 16842991; // 0x10100ef
     field public static final int alwaysRetainTaskState = 16843267; // 0x1010203
-    field public static final int amPmBackgroundColor = 16843941; // 0x10104a5
-    field public static final int amPmTextColor = 16843940; // 0x10104a4
+    field public static final deprecated int amPmBackgroundColor = 16843941; // 0x10104a5
+    field public static final deprecated int amPmTextColor = 16843940; // 0x10104a4
     field public static final int ambientShadowAlpha = 16843966; // 0x10104be
     field public static final int angle = 16843168; // 0x10101a0
     field public static final int animateFirstView = 16843477; // 0x10102d5
@@ -401,6 +402,7 @@
     field public static final int autoStart = 16843445; // 0x10102b5
     field public static final deprecated int autoText = 16843114; // 0x101016a
     field public static final int autoUrlDetect = 16843404; // 0x101028c
+    field public static final int autoVerify = 16844010; // 0x10104ea
     field public static final int background = 16842964; // 0x10100d4
     field public static final int backgroundDimAmount = 16842802; // 0x1010032
     field public static final int backgroundDimEnabled = 16843295; // 0x101021f
@@ -722,13 +724,13 @@
     field public static final int hapticFeedbackEnabled = 16843358; // 0x101025e
     field public static final int hardwareAccelerated = 16843475; // 0x10102d3
     field public static final int hasCode = 16842764; // 0x101000c
-    field public static final int headerAmPmTextAppearance = 16843936; // 0x10104a0
+    field public static final deprecated int headerAmPmTextAppearance = 16843936; // 0x10104a0
     field public static final int headerBackground = 16843055; // 0x101012f
     field public static final deprecated int headerDayOfMonthTextAppearance = 16843927; // 0x1010497
     field public static final int headerDividersEnabled = 16843310; // 0x101022e
     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 deprecated int headerTimeTextAppearance = 16843935; // 0x101049f
+    field public static final deprecated int headerYearTextAppearance = 16843928; // 0x1010498
     field public static final int height = 16843093; // 0x1010155
     field public static final int hideOnContentScroll = 16843843; // 0x1010443
     field public static final int hint = 16843088; // 0x1010150
@@ -1531,7 +1533,7 @@
     field public static final int x = 16842924; // 0x10100ac
     field public static final int xlargeScreens = 16843455; // 0x10102bf
     field public static final int y = 16842925; // 0x10100ad
-    field public static final int yearListItemTextAppearance = 16843929; // 0x1010499
+    field public static final deprecated int yearListItemTextAppearance = 16843929; // 0x1010499
     field public static final deprecated int yearListSelectorColor = 16843930; // 0x101049a
     field public static final int yesNoPreferenceStyle = 16842896; // 0x1010090
     field public static final int zAdjustment = 16843201; // 0x10101c1
@@ -8175,6 +8177,7 @@
     field public static final java.lang.String ACTION_INSERT = "android.intent.action.INSERT";
     field public static final java.lang.String ACTION_INSERT_OR_EDIT = "android.intent.action.INSERT_OR_EDIT";
     field public static final java.lang.String ACTION_INSTALL_PACKAGE = "android.intent.action.INSTALL_PACKAGE";
+    field public static final java.lang.String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
     field public static final java.lang.String ACTION_LOCALE_CHANGED = "android.intent.action.LOCALE_CHANGED";
     field public static final java.lang.String ACTION_MAIN = "android.intent.action.MAIN";
     field public static final java.lang.String ACTION_MANAGED_PROFILE_ADDED = "android.intent.action.MANAGED_PROFILE_ADDED";
@@ -8472,6 +8475,8 @@
     field public static final int NO_MATCH_CATEGORY = -4; // 0xfffffffc
     field public static final int NO_MATCH_DATA = -2; // 0xfffffffe
     field public static final int NO_MATCH_TYPE = -1; // 0xffffffff
+    field public static final java.lang.String SCHEME_HTTP = "http";
+    field public static final java.lang.String SCHEME_HTTPS = "https";
     field public static final int SYSTEM_HIGH_PRIORITY = 1000; // 0x3e8
     field public static final int SYSTEM_LOW_PRIORITY = -1000; // 0xfffffc18
   }
@@ -9070,6 +9075,25 @@
     field public java.lang.String targetPackage;
   }
 
+  public final class IntentFilterVerificationInfo implements android.os.Parcelable {
+    ctor public IntentFilterVerificationInfo();
+    ctor public IntentFilterVerificationInfo(java.lang.String, java.lang.String[]);
+    ctor public IntentFilterVerificationInfo(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    ctor public IntentFilterVerificationInfo(android.os.Parcel);
+    method public int describeContents();
+    method public java.lang.String[] getDomains();
+    method public java.lang.String getDomainsString();
+    method public java.lang.String getPackageName();
+    method public int getStatus();
+    method public java.lang.String getStatusString();
+    method public static java.lang.String getStatusStringFromValue(int);
+    method public void readFromXml(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public void setStatus(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    method public void writeToXml(org.xmlpull.v1.XmlSerializer) throws java.io.IOException;
+    field public static final android.os.Parcelable.Creator<android.content.pm.IntentFilterVerificationInfo> CREATOR;
+  }
+
   public class LabeledIntent extends android.content.Intent {
     ctor public LabeledIntent(android.content.Intent, java.lang.String, int, int);
     ctor public LabeledIntent(android.content.Intent, java.lang.String, java.lang.CharSequence, int);
@@ -9304,6 +9328,7 @@
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
     method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public abstract java.lang.String getNameForUid(int);
@@ -11422,6 +11447,7 @@
     field public static final int JPEG = 256; // 0x100
     field public static final int NV16 = 16; // 0x10
     field public static final int NV21 = 17; // 0x11
+    field public static final int PRIVATE = 34; // 0x22
     field public static final int RAW10 = 37; // 0x25
     field public static final int RAW_SENSOR = 32; // 0x20
     field public static final int RGB_565 = 4; // 0x4
@@ -24373,6 +24399,7 @@
     field public static java.lang.String DIRECTORY_RINGTONES;
     field public static final java.lang.String MEDIA_BAD_REMOVAL = "bad_removal";
     field public static final java.lang.String MEDIA_CHECKING = "checking";
+    field public static final java.lang.String MEDIA_EJECTING = "ejecting";
     field public static final java.lang.String MEDIA_MOUNTED = "mounted";
     field public static final java.lang.String MEDIA_MOUNTED_READ_ONLY = "mounted_ro";
     field public static final java.lang.String MEDIA_NOFS = "nofs";
@@ -27889,6 +27916,108 @@
     field public static final java.lang.String SIZE = "_size";
   }
 
+  public abstract class SearchIndexableData {
+    ctor public SearchIndexableData();
+    ctor public SearchIndexableData(android.content.Context);
+    field public java.lang.String className;
+    field public android.content.Context context;
+    field public boolean enabled;
+    field public int iconResId;
+    field public java.lang.String intentAction;
+    field public java.lang.String intentTargetClass;
+    field public java.lang.String intentTargetPackage;
+    field public java.lang.String key;
+    field public java.util.Locale locale;
+    field public java.lang.String packageName;
+    field public int rank;
+    field public int userId;
+  }
+
+  public class SearchIndexableResource extends android.provider.SearchIndexableData {
+    ctor public SearchIndexableResource(int, int, java.lang.String, int);
+    ctor public SearchIndexableResource(android.content.Context);
+    field public int xmlResId;
+  }
+
+  public class SearchIndexablesContract {
+    ctor public SearchIndexablesContract();
+    field public static final int COLUMN_INDEX_NON_INDEXABLE_KEYS_KEY_VALUE = 0; // 0x0
+    field public static final int COLUMN_INDEX_RAW_CLASS_NAME = 7; // 0x7
+    field public static final int COLUMN_INDEX_RAW_ENTRIES = 4; // 0x4
+    field public static final int COLUMN_INDEX_RAW_ICON_RESID = 8; // 0x8
+    field public static final int COLUMN_INDEX_RAW_INTENT_ACTION = 9; // 0x9
+    field public static final int COLUMN_INDEX_RAW_INTENT_TARGET_CLASS = 11; // 0xb
+    field public static final int COLUMN_INDEX_RAW_INTENT_TARGET_PACKAGE = 10; // 0xa
+    field public static final int COLUMN_INDEX_RAW_KEY = 12; // 0xc
+    field public static final int COLUMN_INDEX_RAW_KEYWORDS = 5; // 0x5
+    field public static final int COLUMN_INDEX_RAW_RANK = 0; // 0x0
+    field public static final int COLUMN_INDEX_RAW_SCREEN_TITLE = 6; // 0x6
+    field public static final int COLUMN_INDEX_RAW_SUMMARY_OFF = 3; // 0x3
+    field public static final int COLUMN_INDEX_RAW_SUMMARY_ON = 2; // 0x2
+    field public static final int COLUMN_INDEX_RAW_TITLE = 1; // 0x1
+    field public static final int COLUMN_INDEX_RAW_USER_ID = 13; // 0xd
+    field public static final int COLUMN_INDEX_XML_RES_CLASS_NAME = 2; // 0x2
+    field public static final int COLUMN_INDEX_XML_RES_ICON_RESID = 3; // 0x3
+    field public static final int COLUMN_INDEX_XML_RES_INTENT_ACTION = 4; // 0x4
+    field public static final int COLUMN_INDEX_XML_RES_INTENT_TARGET_CLASS = 6; // 0x6
+    field public static final int COLUMN_INDEX_XML_RES_INTENT_TARGET_PACKAGE = 5; // 0x5
+    field public static final int COLUMN_INDEX_XML_RES_RANK = 0; // 0x0
+    field public static final int COLUMN_INDEX_XML_RES_RESID = 1; // 0x1
+    field public static final java.lang.String INDEXABLES_RAW = "indexables_raw";
+    field public static final java.lang.String[] INDEXABLES_RAW_COLUMNS;
+    field public static final java.lang.String INDEXABLES_RAW_PATH = "settings/indexables_raw";
+    field public static final java.lang.String INDEXABLES_XML_RES = "indexables_xml_res";
+    field public static final java.lang.String[] INDEXABLES_XML_RES_COLUMNS;
+    field public static final java.lang.String INDEXABLES_XML_RES_PATH = "settings/indexables_xml_res";
+    field public static final java.lang.String NON_INDEXABLES_KEYS = "non_indexables_key";
+    field public static final java.lang.String[] NON_INDEXABLES_KEYS_COLUMNS;
+    field public static final java.lang.String NON_INDEXABLES_KEYS_PATH = "settings/non_indexables_key";
+    field public static final java.lang.String PROVIDER_INTERFACE = "android.content.action.SEARCH_INDEXABLES_PROVIDER";
+  }
+
+  public static class SearchIndexablesContract.BaseColumns {
+    field public static final java.lang.String COLUMN_CLASS_NAME = "className";
+    field public static final java.lang.String COLUMN_ICON_RESID = "iconResId";
+    field public static final java.lang.String COLUMN_INTENT_ACTION = "intentAction";
+    field public static final java.lang.String COLUMN_INTENT_TARGET_CLASS = "intentTargetClass";
+    field public static final java.lang.String COLUMN_INTENT_TARGET_PACKAGE = "intentTargetPackage";
+    field public static final java.lang.String COLUMN_RANK = "rank";
+  }
+
+  public static final class SearchIndexablesContract.NonIndexableKey extends android.provider.SearchIndexablesContract.BaseColumns {
+    field public static final java.lang.String COLUMN_KEY_VALUE = "key";
+    field public static final java.lang.String MIME_TYPE = "vnd.android.cursor.dir/non_indexables_key";
+  }
+
+  public static final class SearchIndexablesContract.RawData extends android.provider.SearchIndexablesContract.BaseColumns {
+    field public static final java.lang.String COLUMN_ENTRIES = "entries";
+    field public static final java.lang.String COLUMN_KEY = "key";
+    field public static final java.lang.String COLUMN_KEYWORDS = "keywords";
+    field public static final java.lang.String COLUMN_SCREEN_TITLE = "screenTitle";
+    field public static final java.lang.String COLUMN_SUMMARY_OFF = "summaryOff";
+    field public static final java.lang.String COLUMN_SUMMARY_ON = "summaryOn";
+    field public static final java.lang.String COLUMN_TITLE = "title";
+    field public static final java.lang.String COLUMN_USER_ID = "user_id";
+    field public static final java.lang.String MIME_TYPE = "vnd.android.cursor.dir/indexables_raw";
+  }
+
+  public static final class SearchIndexablesContract.XmlResource extends android.provider.SearchIndexablesContract.BaseColumns {
+    field public static final java.lang.String COLUMN_XML_RESID = "xmlResId";
+    field public static final java.lang.String MIME_TYPE = "vnd.android.cursor.dir/indexables_xml_res";
+  }
+
+  public abstract class SearchIndexablesProvider extends android.content.ContentProvider {
+    ctor public SearchIndexablesProvider();
+    method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
+    method public java.lang.String getType(android.net.Uri);
+    method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
+    method public android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
+    method public abstract android.database.Cursor queryNonIndexableKeys(java.lang.String[]);
+    method public abstract android.database.Cursor queryRawData(java.lang.String[]);
+    method public abstract android.database.Cursor queryXmlResources(java.lang.String[]);
+    method public final int update(android.net.Uri, android.content.ContentValues, java.lang.String, java.lang.String[]);
+  }
+
   public class SearchRecentSuggestions {
     ctor public SearchRecentSuggestions(android.content.Context, java.lang.String, int);
     method public void clearHistory();
@@ -28799,6 +28928,10 @@
     method public static android.renderscript.Element A_8(android.renderscript.RenderScript);
     method public static android.renderscript.Element BOOLEAN(android.renderscript.RenderScript);
     method public static android.renderscript.Element ELEMENT(android.renderscript.RenderScript);
+    method public static android.renderscript.Element F16(android.renderscript.RenderScript);
+    method public static android.renderscript.Element F16_2(android.renderscript.RenderScript);
+    method public static android.renderscript.Element F16_3(android.renderscript.RenderScript);
+    method public static android.renderscript.Element F16_4(android.renderscript.RenderScript);
     method public static android.renderscript.Element F32(android.renderscript.RenderScript);
     method public static android.renderscript.Element F32_2(android.renderscript.RenderScript);
     method public static android.renderscript.Element F32_3(android.renderscript.RenderScript);
@@ -28897,6 +29030,7 @@
     method public static android.renderscript.Element.DataType valueOf(java.lang.String);
     method public static final android.renderscript.Element.DataType[] values();
     enum_constant public static final android.renderscript.Element.DataType BOOLEAN;
+    enum_constant public static final android.renderscript.Element.DataType FLOAT_16;
     enum_constant public static final android.renderscript.Element.DataType FLOAT_32;
     enum_constant public static final android.renderscript.Element.DataType FLOAT_64;
     enum_constant public static final android.renderscript.Element.DataType MATRIX_2X2;
@@ -29170,11 +29304,14 @@
     method public static android.renderscript.RenderScript create(android.content.Context);
     method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType);
     method public static android.renderscript.RenderScript create(android.content.Context, android.renderscript.RenderScript.ContextType, int);
+    method public static android.renderscript.RenderScript createMultiContext(android.content.Context, android.renderscript.RenderScript.ContextType, int, int);
     method public void destroy();
     method public void finish();
     method public final android.content.Context getApplicationContext();
     method public android.renderscript.RenderScript.RSErrorHandler getErrorHandler();
     method public android.renderscript.RenderScript.RSMessageHandler getMessageHandler();
+    method public static long getMinorID();
+    method public static void releaseAllContexts();
     method public void sendMessage(int, int[]);
     method public void setErrorHandler(android.renderscript.RenderScript.RSErrorHandler);
     method public void setMessageHandler(android.renderscript.RenderScript.RSMessageHandler);
@@ -29256,9 +29393,12 @@
   public class Script extends android.renderscript.BaseObj {
     method public void bindAllocation(android.renderscript.Allocation, int);
     method protected android.renderscript.Script.FieldID createFieldID(int, android.renderscript.Element);
+    method protected android.renderscript.Script.InvokeID createInvokeID(int);
     method protected android.renderscript.Script.KernelID createKernelID(int, int, android.renderscript.Element, android.renderscript.Element);
     method protected void forEach(int, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.FieldPacker);
     method protected void forEach(int, android.renderscript.Allocation, android.renderscript.Allocation, android.renderscript.FieldPacker, android.renderscript.Script.LaunchOptions);
+    method protected void forEach(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.FieldPacker);
+    method protected void forEach(int, android.renderscript.Allocation[], android.renderscript.Allocation, android.renderscript.FieldPacker, android.renderscript.Script.LaunchOptions);
     method public boolean getVarB(int);
     method public double getVarD(int);
     method public float getVarF(int);
@@ -29296,6 +29436,9 @@
   public static final class Script.FieldID extends android.renderscript.BaseObj {
   }
 
+  public static final class Script.InvokeID extends android.renderscript.BaseObj {
+  }
+
   public static final class Script.KernelID extends android.renderscript.BaseObj {
   }
 
@@ -29506,6 +29649,8 @@
     method public static android.renderscript.Type createX(android.renderscript.RenderScript, android.renderscript.Element, int);
     method public static android.renderscript.Type createXY(android.renderscript.RenderScript, android.renderscript.Element, int, int);
     method public static android.renderscript.Type createXYZ(android.renderscript.RenderScript, android.renderscript.Element, int, int, int);
+    method public int getArray(int);
+    method public int getArrayCount();
     method public int getCount();
     method public android.renderscript.Element getElement();
     method public int getX();
@@ -29519,6 +29664,7 @@
   public static class Type.Builder {
     ctor public Type.Builder(android.renderscript.RenderScript, android.renderscript.Element);
     method public android.renderscript.Type create();
+    method public android.renderscript.Type.Builder setArray(int, int);
     method public android.renderscript.Type.Builder setFaces(boolean);
     method public android.renderscript.Type.Builder setMipmaps(boolean);
     method public android.renderscript.Type.Builder setX(int);
@@ -32904,6 +33050,7 @@
     method public java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public java.lang.String getInstallerPackageName(java.lang.String);
     method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
+    method public java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public java.lang.String getNameForUid(int);
@@ -40320,7 +40467,6 @@
     method public boolean shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String);
     field public static final int ERROR_AUTHENTICATION = -4; // 0xfffffffc
     field public static final int ERROR_BAD_URL = -12; // 0xfffffff4
-    field public static final int ERROR_BLOCKED = -16; // 0xfffffff0
     field public static final int ERROR_CONNECT = -6; // 0xfffffffa
     field public static final int ERROR_FAILED_SSL_HANDSHAKE = -11; // 0xfffffff5
     field public static final int ERROR_FILE = -13; // 0xfffffff3
@@ -40878,7 +41024,7 @@
     field protected android.database.Cursor mDataCursor;
   }
 
-  public class AnalogClock extends android.view.View {
+  public deprecated class AnalogClock extends android.view.View {
     ctor public AnalogClock(android.content.Context);
     ctor public AnalogClock(android.content.Context, android.util.AttributeSet);
     ctor public AnalogClock(android.content.Context, android.util.AttributeSet, int);
@@ -42939,12 +43085,16 @@
     ctor public TimePicker(android.content.Context, android.util.AttributeSet);
     ctor public TimePicker(android.content.Context, android.util.AttributeSet, int);
     ctor public TimePicker(android.content.Context, android.util.AttributeSet, int, int);
-    method public java.lang.Integer getCurrentHour();
-    method public java.lang.Integer getCurrentMinute();
+    method public deprecated java.lang.Integer getCurrentHour();
+    method public deprecated java.lang.Integer getCurrentMinute();
+    method public int getHour();
+    method public int getMinute();
     method public boolean is24HourView();
-    method public void setCurrentHour(java.lang.Integer);
-    method public void setCurrentMinute(java.lang.Integer);
+    method public deprecated void setCurrentHour(java.lang.Integer);
+    method public deprecated void setCurrentMinute(java.lang.Integer);
+    method public void setHour(int);
     method public void setIs24HourView(java.lang.Boolean);
+    method public void setMinute(int);
     method public void setOnTimeChangedListener(android.widget.TimePicker.OnTimeChangedListener);
   }
 
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 6d74905..6ec5457 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -36,6 +36,7 @@
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
+import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.KeySet;
 import android.content.pm.ManifestDigest;
 import android.content.pm.PackageInfo;
@@ -1309,6 +1310,45 @@
     }
 
     @Override
+    public void verifyIntentFilter(int id, int verificationCode, List<String> outFailedDomains) {
+        try {
+            mPM.verifyIntentFilter(id, verificationCode, outFailedDomains);
+        } catch (RemoteException e) {
+            // Should never happen!
+        }
+    }
+
+    @Override
+    public int getIntentVerificationStatus(String packageName, int userId) {
+        try {
+            return mPM.getIntentVerificationStatus(packageName, userId);
+        } catch (RemoteException e) {
+            // Should never happen!
+            return PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+        }
+    }
+
+    @Override
+    public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
+        try {
+            return mPM.updateIntentVerificationStatus(packageName, status, userId);
+        } catch (RemoteException e) {
+            // Should never happen!
+            return false;
+        }
+    }
+
+    @Override
+    public List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) {
+        try {
+            return mPM.getIntentFilterVerifications(packageName);
+        } catch (RemoteException e) {
+            // Should never happen!
+            return null;
+        }
+    }
+
+    @Override
     public void setInstallerPackageName(String targetPackage,
             String installerPackageName) {
         try {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 59fe490..a0f40f6 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -402,13 +402,7 @@
                 new CachedServiceFetcher<StorageManager>() {
             @Override
             public StorageManager createService(ContextImpl ctx) {
-                try {
-                    return new StorageManager(
-                            ctx.getContentResolver(), ctx.mMainThread.getHandler().getLooper());
-                } catch (RemoteException rex) {
-                    Log.e(TAG, "Failed to create StorageManager", rex);
-                    return null;
-                }
+                return new StorageManager(ctx, ctx.mMainThread.getHandler().getLooper());
             }});
 
         registerService(Context.TELEPHONY_SERVICE, TelephonyManager.class,
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index cf6619f..cbb0f51 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3165,6 +3165,22 @@
     }
 
     /**
+     * Start Quick Contact on the managed profile for the current user, if the policy allows.
+     * @hide
+     */
+    public void startManagedQuickContact(String actualLookupKey, long actualContactId,
+            Intent originalIntent) {
+        if (mService != null) {
+            try {
+                mService.startManagedQuickContact(
+                        actualLookupKey, actualContactId, originalIntent);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
      * Called by the profile owner of a managed profile so that some intents sent in the managed
      * profile can also be resolved in the parent, or vice versa.
      * Only activity intents are supported.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9ca52e5..73b0684 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -189,6 +189,7 @@
     void setCrossProfileCallerIdDisabled(in ComponentName who, boolean disabled);
     boolean getCrossProfileCallerIdDisabled(in ComponentName who);
     boolean getCrossProfileCallerIdDisabledForUser(int userId);
+    void startManagedQuickContact(String lookupKey, long contactId, in Intent originalIntent);
 
     void setTrustAgentConfiguration(in ComponentName admin, in ComponentName agent,
             in PersistableBundle args);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 030b770..7a99a79 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1911,6 +1911,19 @@
     public static final String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
 
     /**
+     * Broadcast Action: Sent to the system intent filter verifier when an intent filter
+     * needs to be verified. The data contains the filter data hosts to be verified against.
+     * <p class="note">
+     * This is a protected intent that can only be sent by the system.
+     * </p>
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
+
+    /**
      * Broadcast Action: Resources for a set of packages (which were
      * previously unavailable) are currently
      * available since the media on which they exist is available.
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 1240a23..590d791 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -16,10 +16,12 @@
 
 package android.content;
 
+import android.content.pm.PackageParser;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.PatternMatcher;
+import android.text.TextUtils;
 import android.util.AndroidException;
 import android.util.Log;
 import android.util.Printer;
@@ -150,6 +152,7 @@
     private static final String CAT_STR = "cat";
     private static final String NAME_STR = "name";
     private static final String ACTION_STR = "action";
+    private static final String AUTO_VERIFY_STR = "autoVerify";
 
     /**
      * The filter {@link #setPriority} value at which system high-priority
@@ -247,6 +250,19 @@
      */
     public static final int NO_MATCH_CATEGORY = -4;
 
+    /**
+     * HTTP scheme.
+     *
+     * @see #addDataScheme(String)
+     */
+    public static final String SCHEME_HTTP = "http";
+    /**
+     * HTTPS scheme.
+     *
+     * @see #addDataScheme(String)
+     */
+    public static final String SCHEME_HTTPS = "https";
+
     private int mPriority;
     private final ArrayList<String> mActions;
     private ArrayList<String> mCategories = null;
@@ -257,6 +273,13 @@
     private ArrayList<String> mDataTypes = null;
     private boolean mHasPartialTypes = false;
 
+    private static final int STATE_VERIFY_AUTO         = 0x00000001;
+    private static final int STATE_NEED_VERIFY         = 0x00000010;
+    private static final int STATE_NEED_VERIFY_CHECKED = 0x00000100;
+    private static final int STATE_VERIFIED            = 0x00001000;
+
+    private int mVerifyState;
+
     // These functions are the start of more optimized code for managing
     // the string sets...  not yet implemented.
 
@@ -326,7 +349,7 @@
         public MalformedMimeTypeException(String name) {
             super(name);
         }
-    };
+    }
 
     /**
      * Create a new IntentFilter instance with a specified action and MIME
@@ -421,6 +444,7 @@
             mDataPaths = new ArrayList<PatternMatcher>(o.mDataPaths);
         }
         mHasPartialTypes = o.mHasPartialTypes;
+        mVerifyState = o.mVerifyState;
     }
 
     /**
@@ -452,6 +476,94 @@
     }
 
     /**
+     * Set whether this filter will needs to be automatically verified against its data URIs or not.
+     * The default is false.
+     *
+     * The verification would need to happen only and only if the Intent action is
+     * {@link android.content.Intent#ACTION_VIEW} and the Intent category is
+     * {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent data scheme
+     * is "http" or "https".
+     *
+     * True means that the filter will need to use its data URIs to be verified.
+     *
+     * @param autoVerify The new autoVerify value.
+     *
+     * @see #getAutoVerify()
+     * @see #addAction(String)
+     * @see #getAction(int)
+     * @see #addCategory(String)
+     * @see #getCategory(int)
+     * @see #addDataScheme(String)
+     * @see #getDataScheme(int)
+     *
+     * @hide
+     */
+    public final void setAutoVerify(boolean autoVerify) {
+        mVerifyState &= ~STATE_VERIFY_AUTO;
+        if (autoVerify) mVerifyState |= STATE_VERIFY_AUTO;
+    }
+
+    /**
+     * Return if this filter will needs to be automatically verified again its data URIs or not.
+     *
+     * @return True if the filter will needs to be automatically verified. False otherwise.
+     *
+     * @see #setAutoVerify(boolean)
+     *
+     * @hide
+     */
+    public final boolean getAutoVerify() {
+        return ((mVerifyState & STATE_VERIFY_AUTO) == 1);
+    }
+
+    /**
+     * Return if this filter needs to be automatically verified again its data URIs or not.
+     *
+     * @return True if the filter needs to be automatically verified. False otherwise.
+     *
+     * This will check if if the Intent action is {@link android.content.Intent#ACTION_VIEW} and
+     * the Intent category is {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent
+     * data scheme is "http" or "https".
+     *
+     * @see #setAutoVerify(boolean)
+     *
+     * @hide
+     */
+    public final boolean needsVerification() {
+        return hasAction(Intent.ACTION_VIEW) &&
+                hasCategory(Intent.CATEGORY_BROWSABLE) &&
+                (hasDataScheme(SCHEME_HTTP) || hasDataScheme(SCHEME_HTTPS)) &&
+                getAutoVerify();
+    }
+
+    /**
+     * Return if this filter has been verified
+     *
+     * @return true if the filter has been verified or if autoVerify is false.
+     *
+     * @hide
+     */
+    public final boolean isVerified() {
+        if ((mVerifyState & STATE_NEED_VERIFY_CHECKED) == STATE_NEED_VERIFY_CHECKED) {
+            return ((mVerifyState & STATE_NEED_VERIFY) == STATE_NEED_VERIFY);
+        }
+        return false;
+    }
+
+    /**
+     * Set if this filter has been verified
+     *
+     * @param verified true if this filter has been verified. False otherwise.
+     *
+     * @hide
+     */
+    public void setVerified(boolean verified) {
+        mVerifyState |= STATE_NEED_VERIFY_CHECKED;
+        mVerifyState &= ~STATE_VERIFIED;
+        if (verified) mVerifyState |= STATE_VERIFIED;
+    }
+
+    /**
      * Add a new Intent action to match against.  If any actions are included
      * in the filter, then an Intent's action must be one of those values for
      * it to match.  If no actions are included, the Intent action is ignored.
@@ -1333,6 +1445,7 @@
      * Write the contents of the IntentFilter as an XML stream.
      */
     public void writeToXml(XmlSerializer serializer) throws IOException {
+        serializer.attribute(null, AUTO_VERIFY_STR, Boolean.toString(getAutoVerify()));
         int N = countActions();
         for (int i=0; i<N; i++) {
             serializer.startTag(null, ACTION_STR);
@@ -1407,6 +1520,9 @@
 
     public void readFromXml(XmlPullParser parser) throws XmlPullParserException,
             IOException {
+        String autoVerify = parser.getAttributeValue(null, AUTO_VERIFY_STR);
+        setAutoVerify(TextUtils.isEmpty(autoVerify) ? false : Boolean.getBoolean(autoVerify));
+
         int outerDepth = parser.getDepth();
         int type;
         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1548,6 +1664,11 @@
                     sb.append(", mHasPartialTypes="); sb.append(mHasPartialTypes);
             du.println(sb.toString());
         }
+        {
+            sb.setLength(0);
+            sb.append(prefix); sb.append("AutoVerify="); sb.append(getAutoVerify());
+            du.println(sb.toString());
+        }
     }
 
     public static final Parcelable.Creator<IntentFilter> CREATOR
@@ -1614,6 +1735,7 @@
         }
         dest.writeInt(mPriority);
         dest.writeInt(mHasPartialTypes ? 1 : 0);
+        dest.writeInt(getAutoVerify() ? 1 : 0);
     }
 
     /**
@@ -1680,6 +1802,7 @@
         }
         mPriority = source.readInt();
         mHasPartialTypes = source.readInt() > 0;
+        setAutoVerify(source.readInt() > 0);
     }
 
     private final boolean findMimeType(String type) {
@@ -1724,4 +1847,27 @@
 
         return false;
     }
+
+    /**
+     * @hide
+     */
+    public ArrayList<String> getHostsList() {
+        ArrayList<String> result = new ArrayList<>();
+        Iterator<IntentFilter.AuthorityEntry> it = authoritiesIterator();
+        if (it != null) {
+            while (it.hasNext()) {
+                IntentFilter.AuthorityEntry entry = it.next();
+                result.add(entry.getHost());
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @hide
+     */
+    public String[] getHosts() {
+        ArrayList<String> list = getHostsList();
+        return list.toArray(new String[list.size()]);
+    }
 }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index c6d97f1..66b0d1a 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -31,6 +31,7 @@
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
+import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.KeySet;
 import android.content.pm.PackageInfo;
@@ -436,6 +437,11 @@
     void verifyPendingInstall(int id, int verificationCode);
     void extendVerificationTimeout(int id, int verificationCodeAtTimeout, long millisecondsToDelay);
 
+    void verifyIntentFilter(int id, int verificationCode, in List<String> outFailedDomains);
+    int getIntentVerificationStatus(String packageName, int userId);
+    boolean updateIntentVerificationStatus(String packageName, int status, int userId);
+    List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName);
+
     VerifierDeviceIdentity getVerifierDeviceIdentity();
 
     boolean isFirstBoot();
diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.aidl b/core/java/android/content/pm/IntentFilterVerificationInfo.aidl
new file mode 100644
index 0000000..00220e5
--- /dev/null
+++ b/core/java/android/content/pm/IntentFilterVerificationInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+parcelable IntentFilterVerificationInfo;
diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.java b/core/java/android/content/pm/IntentFilterVerificationInfo.java
new file mode 100644
index 0000000..60cb4a8
--- /dev/null
+++ b/core/java/android/content/pm/IntentFilterVerificationInfo.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * The {@link com.android.server.pm.PackageManagerService} maintains some
+ * {@link IntentFilterVerificationInfo}s for each domain / package / class name per user.
+ *
+ * @hide
+ */
+public final class IntentFilterVerificationInfo implements Parcelable {
+    private static final String TAG = IntentFilterVerificationInfo.class.getName();
+
+    private static final String TAG_DOMAIN = "domain";
+    private static final String ATTR_DOMAIN_NAME = "name";
+    private static final String ATTR_PACKAGE_NAME = "packageName";
+    private static final String ATTR_STATUS = "status";
+
+    private String[] mDomains;
+    private String mPackageName;
+    private int mMainStatus;
+
+    public IntentFilterVerificationInfo() {
+        mPackageName = null;
+        mDomains = new String[0];
+        mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+    }
+
+    public IntentFilterVerificationInfo(String packageName, String[] domains) {
+        mPackageName = packageName;
+        mDomains = domains;
+        mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+    }
+
+    public IntentFilterVerificationInfo(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        readFromXml(parser);
+    }
+
+    public IntentFilterVerificationInfo(Parcel source) {
+        readFromParcel(source);
+    }
+
+    public String[] getDomains() {
+        return mDomains;
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public int getStatus() {
+        return mMainStatus;
+    }
+
+    public void setStatus(int s) {
+        if (s >= INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED &&
+                s <= INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
+            mMainStatus = s;
+        } else {
+            Log.w(TAG, "Trying to set a non supported status: " + s);
+        }
+    }
+
+    public String getDomainsString() {
+        StringBuilder sb = new StringBuilder();
+        for (String str : mDomains) {
+            if (sb.length() > 0) {
+                sb.append(" ");
+            }
+            sb.append(str);
+        }
+        return sb.toString();
+    }
+
+    String getStringFromXml(XmlPullParser parser, String attribute, String defaultValue) {
+        String value = parser.getAttributeValue(null, attribute);
+        if (value == null) {
+            String msg = "Missing element under " + TAG +": " + attribute + " at " +
+                    parser.getPositionDescription();
+            Log.w(TAG, msg);
+            return defaultValue;
+        } else {
+            return value;
+        }
+    }
+
+    int getIntFromXml(XmlPullParser parser, String attribute, int defaultValue) {
+        String value = parser.getAttributeValue(null, attribute);
+        if (TextUtils.isEmpty(value)) {
+            String msg = "Missing element under " + TAG +": " + attribute + " at " +
+                    parser.getPositionDescription();
+            Log.w(TAG, msg);
+            return defaultValue;
+        } else {
+            return Integer.parseInt(value);
+        }
+    }
+
+    public void readFromXml(XmlPullParser parser) throws XmlPullParserException,
+            IOException {
+        mPackageName = getStringFromXml(parser, ATTR_PACKAGE_NAME, null);
+        if (mPackageName == null) {
+            Log.e(TAG, "Package name cannot be null!");
+        }
+        int status = getIntFromXml(parser, ATTR_STATUS, -1);
+        if (status == -1) {
+            Log.e(TAG, "Unknown status value: " + status);
+        }
+        mMainStatus = status;
+
+        ArrayList<String> list = new ArrayList<>();
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG
+                    || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals(TAG_DOMAIN)) {
+                String name = getStringFromXml(parser, ATTR_DOMAIN_NAME, null);
+                if (!TextUtils.isEmpty(name)) {
+                    if (list == null) {
+                        list = new ArrayList<>();
+                    }
+                    list.add(name);
+                }
+            } else {
+                Log.w(TAG, "Unknown tag parsing IntentFilter: " + tagName);
+            }
+            XmlUtils.skipCurrentTag(parser);
+        }
+
+        mDomains = list.toArray(new String[list.size()]);
+    }
+
+    public void writeToXml(XmlSerializer serializer) throws IOException {
+        serializer.attribute(null, ATTR_PACKAGE_NAME, mPackageName);
+        serializer.attribute(null, ATTR_STATUS, String.valueOf(mMainStatus));
+        for (String str : mDomains) {
+            serializer.startTag(null, TAG_DOMAIN);
+            serializer.attribute(null, ATTR_DOMAIN_NAME, str);
+            serializer.endTag(null, TAG_DOMAIN);
+        }
+    }
+
+    public String getStatusString() {
+        return getStatusStringFromValue(mMainStatus);
+    }
+
+    public static String getStatusStringFromValue(int val) {
+        switch (val) {
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK       : return "ask";
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS    : return "always";
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER     : return "never";
+            default:
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED : return "undefined";
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    private void readFromParcel(Parcel source) {
+        mPackageName = source.readString();
+        mMainStatus = source.readInt();
+        mDomains = source.readStringArray();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mPackageName);
+        dest.writeInt(mMainStatus);
+        dest.writeStringArray(mDomains);
+    }
+
+    public static final Creator<IntentFilterVerificationInfo> CREATOR =
+            new Creator<IntentFilterVerificationInfo>() {
+                public IntentFilterVerificationInfo createFromParcel(Parcel source) {
+                    return new IntentFilterVerificationInfo(source);
+                }
+                public IntentFilterVerificationInfo[] newArray(int size) {
+                    return new IntentFilterVerificationInfo[size];
+                }
+            };
+
+}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f0d1da9..46d6ffb3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -969,6 +969,60 @@
     public static final int VERIFICATION_REJECT = -1;
 
     /**
+     * Used as the {@code verificationCode} argument for
+     * {@link PackageManager#verifyIntentFilter} to indicate that the calling
+     * IntentFilter Verifier confirms that the IntentFilter is verified.
+     *
+     * @hide
+     */
+    public static final int INTENT_FILTER_VERIFICATION_SUCCESS = 1;
+
+    /**
+     * Used as the {@code verificationCode} argument for
+     * {@link PackageManager#verifyIntentFilter} to indicate that the calling
+     * IntentFilter Verifier confirms that the IntentFilter is NOT verified.
+     *
+     * @hide
+     */
+    public static final int INTENT_FILTER_VERIFICATION_FAILURE = -1;
+
+    /**
+     * Internal status code to indicate that an IntentFilter verification result is not specified.
+     *
+     * @hide
+     */
+    public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED = 0;
+
+    /**
+     * Used as the {@code status} argument for {@link PackageManager#updateIntentVerificationStatus}
+     * to indicate that the User will always be prompted the Intent Disambiguation Dialog if there
+     * are two or more Intent resolved for the IntentFilter's domain(s).
+     *
+     * @hide
+     */
+    public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK = 1;
+
+    /**
+     * Used as the {@code status} argument for {@link PackageManager#updateIntentVerificationStatus}
+     * to indicate that the User will never be prompted the Intent Disambiguation Dialog if there
+     * are two or more resolution of the Intent. The default App for the domain(s) specified in the
+     * IntentFilter will also ALWAYS be used.
+     *
+     * @hide
+     */
+    public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS = 2;
+
+    /**
+     * Used as the {@code status} argument for {@link PackageManager#updateIntentVerificationStatus}
+     * to indicate that the User may be prompted the Intent Disambiguation Dialog if there
+     * are two or more Intent resolved. The default App for the domain(s) specified in the
+     * IntentFilter will also NEVER be presented to the User.
+     *
+     * @hide
+     */
+    public static final int INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER = 3;
+
+    /**
      * Can be used as the {@code millisecondsToDelay} argument for
      * {@link PackageManager#extendVerificationTimeout}. This is the
      * maximum time {@code PackageManager} waits for the verification
@@ -1680,8 +1734,52 @@
             = "android.content.pm.extra.VERIFICATION_VERSION_CODE";
 
     /**
-     * The action used to request that the user approve a grant permissions
-     * request from the application.
+     * Extra field name for the ID of a intent filter pending verification. Passed to
+     * an intent filter verifier and is used to call back to
+     * {@link PackageManager#verifyIntentFilter(int, int)}
+     *
+     * @hide
+     */
+    public static final String EXTRA_INTENT_FILTER_VERIFICATION_ID
+            = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_ID";
+
+    /**
+     * Extra field name for the scheme used for an intent filter pending verification. Passed to
+     * an intent filter verifier and is used to construct the URI to verify against.
+     *
+     * Usually this is "https"
+     *
+     * @hide
+     */
+    public static final String EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME
+            = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_URI_SCHEME";
+
+    /**
+     * Extra field name for the host names to be used for an intent filter pending verification.
+     * Passed to an intent filter verifier and is used to construct the URI to verify the
+     * intent filter.
+     *
+     * This is a space delimited list of hosts.
+     *
+     * @hide
+     */
+    public static final String EXTRA_INTENT_FILTER_VERIFICATION_HOSTS
+            = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_HOSTS";
+
+    /**
+     * Extra field name for the package name to be used for an intent filter pending verification.
+     * Passed to an intent filter verifier and is used to check the verification responses coming
+     * from the hosts. Each host response will need to include the package name of APK containing
+     * the intent filter.
+     *
+     * @hide
+     */
+    public static final String EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME
+            = "android.content.pm.extra.INTENT_FILTER_VERIFICATION_PACKAGE_NAME";
+
+    /**
+     * The action used to request that the user approve a permission request
+     * from the application.
      *
      * @hide
      */
@@ -3461,6 +3559,85 @@
             int verificationCodeAtTimeout, long millisecondsToDelay);
 
     /**
+     * Allows a package listening to the
+     * {@link Intent#ACTION_INTENT_FILTER_NEEDS_VERIFICATION intent filter verification
+     * broadcast} to respond to the package manager. The response must include
+     * the {@code verificationCode} which is one of
+     * {@link PackageManager#INTENT_FILTER_VERIFICATION_SUCCESS} or
+     * {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}.
+     *
+     * @param verificationId pending package identifier as passed via the
+     *            {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra.
+     * @param verificationCode either {@link PackageManager#INTENT_FILTER_VERIFICATION_SUCCESS}
+     *            or {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}.
+     * @param outFailedDomains a list of failed domains if the verificationCode is
+     *            {@link PackageManager#INTENT_FILTER_VERIFICATION_FAILURE}, otherwise null;
+     * @throws SecurityException if the caller does not have the
+     *            INTENT_FILTER_VERIFICATION_AGENT permission.
+     *
+     * @hide
+     */
+    public abstract void verifyIntentFilter(int verificationId, int verificationCode,
+            List<String> outFailedDomains);
+
+    /**
+     * Get the status of a Domain Verification Result for an IntentFilter. This is
+     * related to the {@link android.content.IntentFilter#setAutoVerify(boolean)} and
+     * {@link android.content.IntentFilter#getAutoVerify()}
+     *
+     * This is used by the ResolverActivity to change the status depending on what the User select
+     * in the Disambiguation Dialog and also used by the Settings App for changing the default App
+     * for a domain.
+     *
+     * @param packageName The package name of the Activity associated with the IntentFilter.
+     * @param userId The user id.
+     *
+     * @return The status to set to. This can be
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK} or
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS} or
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER} or
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED}
+     *
+     * @hide
+     */
+    public abstract int getIntentVerificationStatus(String packageName, int userId);
+
+    /**
+     * Allow to change the status of a Intent Verification status for all IntentFilter of an App.
+     * This is related to the {@link android.content.IntentFilter#setAutoVerify(boolean)} and
+     * {@link android.content.IntentFilter#getAutoVerify()}
+     *
+     * This is used by the ResolverActivity to change the status depending on what the User select
+     * in the Disambiguation Dialog and also used by the Settings App for changing the default App
+     * for a domain.
+     *
+     * @param packageName The package name of the Activity associated with the IntentFilter.
+     * @param status The status to set to. This can be
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK} or
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS} or
+     *              {@link #INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER}
+     * @param userId The user id.
+     *
+     * @return true if the status has been set. False otherwise.
+     *
+     * @hide
+     */
+    public abstract boolean updateIntentVerificationStatus(String packageName, int status,
+            int userId);
+
+    /**
+     * Get the list of IntentFilterVerificationInfo for a specific package and User.
+     *
+     * @param packageName the package name. When this parameter is set to a non null value,
+     *                    the results will be filtered by the package name provided.
+     *                    Otherwise, there will be no filtering and it will return a list
+     *                    corresponding for all packages for the provided userId.
+     * @return a list of IntentFilterVerificationInfo for a specific package and User.
+     */
+    public abstract List<IntentFilterVerificationInfo> getIntentFilterVerifications(
+            String packageName);
+
+    /**
      * Change the installer associated with a given package.  There are limitations
      * on how the installer package can be changed; in particular:
      * <ul>
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 212cf6d..e20057d 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3149,7 +3149,7 @@
 
             if (parser.getName().equals("intent-filter")) {
                 ActivityIntentInfo intent = new ActivityIntentInfo(a);
-                if (!parseIntent(res, parser, attrs, true, intent, outError)) {
+                if (!parseIntent(res, parser, attrs, true, true, intent, outError)) {
                     return null;
                 }
                 if (intent.countActions() == 0) {
@@ -3161,7 +3161,7 @@
                 }
             } else if (!receiver && parser.getName().equals("preferred")) {
                 ActivityIntentInfo intent = new ActivityIntentInfo(a);
-                if (!parseIntent(res, parser, attrs, false, intent, outError)) {
+                if (!parseIntent(res, parser, attrs, false, false, intent, outError)) {
                     return null;
                 }
                 if (intent.countActions() == 0) {
@@ -3341,7 +3341,7 @@
 
             if (parser.getName().equals("intent-filter")) {
                 ActivityIntentInfo intent = new ActivityIntentInfo(a);
-                if (!parseIntent(res, parser, attrs, true, intent, outError)) {
+                if (!parseIntent(res, parser, attrs, true, true, intent, outError)) {
                     return null;
                 }
                 if (intent.countActions() == 0) {
@@ -3521,7 +3521,7 @@
 
             if (parser.getName().equals("intent-filter")) {
                 ProviderIntentInfo intent = new ProviderIntentInfo(outInfo);
-                if (!parseIntent(res, parser, attrs, true, intent, outError)) {
+                if (!parseIntent(res, parser, attrs, true, false, intent, outError)) {
                     return false;
                 }
                 outInfo.intents.add(intent);
@@ -3780,7 +3780,7 @@
 
             if (parser.getName().equals("intent-filter")) {
                 ServiceIntentInfo intent = new ServiceIntentInfo(s);
-                if (!parseIntent(res, parser, attrs, true, intent, outError)) {
+                if (!parseIntent(res, parser, attrs, true, false, intent, outError)) {
                     return null;
                 }
 
@@ -3981,7 +3981,7 @@
             = "http://schemas.android.com/apk/res/android";
 
     private boolean parseIntent(Resources res, XmlPullParser parser, AttributeSet attrs,
-            boolean allowGlobs, IntentInfo outInfo, String[] outError)
+            boolean allowGlobs, boolean allowAutoVerify, IntentInfo outInfo, String[] outError)
             throws XmlPullParserException, IOException {
 
         TypedArray sa = res.obtainAttributes(attrs,
@@ -4006,6 +4006,12 @@
         outInfo.banner = sa.getResourceId(
                 com.android.internal.R.styleable.AndroidManifestIntentFilter_banner, 0);
 
+        if (allowAutoVerify) {
+            outInfo.setAutoVerify(sa.getBoolean(
+                    com.android.internal.R.styleable.AndroidManifestIntentFilter_autoVerify,
+                    false));
+        }
+
         sa.recycle();
 
         int outerDepth = parser.getDepth();
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index a9c7be3..92b8055 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -37,10 +37,14 @@
     public ArraySet<String> disabledComponents;
     public ArraySet<String> enabledComponents;
 
+    public int domainVerificationStatus;
+
     public PackageUserState() {
         installed = true;
         hidden = false;
         enabled = COMPONENT_ENABLED_STATE_DEFAULT;
+        domainVerificationStatus =
+                PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
     }
 
     public PackageUserState(PackageUserState o) {
@@ -51,9 +55,10 @@
         hidden = o.hidden;
         lastDisableAppCaller = o.lastDisableAppCaller;
         disabledComponents = o.disabledComponents != null
-                ? new ArraySet<String>(o.disabledComponents) : null;
+                ? new ArraySet<>(o.disabledComponents) : null;
         enabledComponents = o.enabledComponents != null
-                ? new ArraySet<String>(o.enabledComponents) : null;
+                ? new ArraySet<>(o.enabledComponents) : null;
         blockUninstall = o.blockUninstall;
+        domainVerificationStatus = o.domainVerificationStatus;
     }
 }
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index fe3aec9..7b141f0 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -143,6 +143,11 @@
      */
     public boolean system;
 
+    /**
+     * @hide Does the associated IntentFilter needs verification ?
+     */
+    public boolean filterNeedsVerification;
+
     private ComponentInfo getComponentInfo() {
         if (activityInfo != null) return activityInfo;
         if (serviceInfo != null) return serviceInfo;
@@ -283,6 +288,7 @@
         resolvePackageName = orig.resolvePackageName;
         system = orig.system;
         targetUserId = orig.targetUserId;
+        filterNeedsVerification = orig.filterNeedsVerification;
     }
 
     public String toString() {
@@ -344,6 +350,7 @@
         dest.writeInt(targetUserId);
         dest.writeInt(system ? 1 : 0);
         dest.writeInt(noResourceId ? 1 : 0);
+        dest.writeInt(filterNeedsVerification ? 1 : 0);
     }
 
     public static final Creator<ResolveInfo> CREATOR
@@ -389,6 +396,7 @@
         targetUserId = source.readInt();
         system = source.readInt() != 0;
         noResourceId = source.readInt() != 0;
+        filterNeedsVerification = source.readInt() != 0;
     }
     
     public static class DisplayNameComparator
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 3f18519..ff3de2b 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -51,6 +51,8 @@
     public static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
     /** @hide */
     public static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
+    /** @hide */
+    public static final long TB_IN_BYTES = GB_IN_BYTES * 1024;
 
     /**
      * Special UID value used when collecting {@link NetworkStatsHistory} for
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 975bfc2..2db976e 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -18,16 +18,11 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
-import android.os.storage.IMountService;
+import android.os.storage.StorageManager;
 import android.os.storage.StorageVolume;
-import android.text.TextUtils;
 import android.util.Log;
 
-import com.google.android.collect.Lists;
-
 import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
 
 /**
  * Provides access to environment variables.
@@ -36,11 +31,9 @@
     private static final String TAG = "Environment";
 
     private static final String ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE";
-    private static final String ENV_EMULATED_STORAGE_SOURCE = "EMULATED_STORAGE_SOURCE";
-    private static final String ENV_EMULATED_STORAGE_TARGET = "EMULATED_STORAGE_TARGET";
-    private static final String ENV_MEDIA_STORAGE = "MEDIA_STORAGE";
-    private static final String ENV_SECONDARY_STORAGE = "SECONDARY_STORAGE";
     private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
+    private static final String ENV_ANDROID_DATA = "ANDROID_DATA";
+    private static final String ENV_ANDROID_STORAGE = "ANDROID_STORAGE";
     private static final String ENV_OEM_ROOT = "OEM_ROOT";
     private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT";
 
@@ -57,12 +50,10 @@
     public static final String DIRECTORY_ANDROID = DIR_ANDROID;
 
     private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
+    private static final File DIR_ANDROID_DATA = getDirectory(ENV_ANDROID_DATA, "/data");
+    private static final File DIR_ANDROID_STORAGE = getDirectory(ENV_ANDROID_STORAGE, "/storage");
     private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem");
     private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor");
-    private static final File DIR_MEDIA_STORAGE = getDirectory(ENV_MEDIA_STORAGE, "/data/media");
-
-    private static final String CANONCIAL_EMULATED_STORAGE_TARGET = getCanonicalPathOrNull(
-            ENV_EMULATED_STORAGE_TARGET);
 
     private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled";
 
@@ -81,73 +72,24 @@
 
     /** {@hide} */
     public static class UserEnvironment {
-        // TODO: generalize further to create package-specific environment
-
-        /** External storage dirs, as visible to vold */
-        private final File[] mExternalDirsForVold;
-        /** External storage dirs, as visible to apps */
-        private final File[] mExternalDirsForApp;
-        /** Primary emulated storage dir for direct access */
-        private final File mEmulatedDirForDirect;
+        private final int mUserId;
 
         public UserEnvironment(int userId) {
-            // See storage config details at http://source.android.com/tech/storage/
-            String rawExternalStorage = System.getenv(ENV_EXTERNAL_STORAGE);
-            String rawEmulatedSource = System.getenv(ENV_EMULATED_STORAGE_SOURCE);
-            String rawEmulatedTarget = System.getenv(ENV_EMULATED_STORAGE_TARGET);
+            mUserId = userId;
+        }
 
-            String rawMediaStorage = System.getenv(ENV_MEDIA_STORAGE);
-            if (TextUtils.isEmpty(rawMediaStorage)) {
-                rawMediaStorage = "/data/media";
+        public File[] getExternalDirs() {
+            final StorageVolume[] volumes = StorageManager.getVolumeList(mUserId);
+            final File[] dirs = new File[volumes.length];
+            for (int i = 0; i < volumes.length; i++) {
+                dirs[i] = volumes[i].getPathFile();
             }
-
-            ArrayList<File> externalForVold = Lists.newArrayList();
-            ArrayList<File> externalForApp = Lists.newArrayList();
-
-            if (!TextUtils.isEmpty(rawEmulatedTarget)) {
-                // Device has emulated storage; external storage paths should have
-                // userId burned into them.
-                final String rawUserId = Integer.toString(userId);
-                final File emulatedSourceBase = new File(rawEmulatedSource);
-                final File emulatedTargetBase = new File(rawEmulatedTarget);
-                final File mediaBase = new File(rawMediaStorage);
-
-                // /storage/emulated/0
-                externalForVold.add(buildPath(emulatedSourceBase, rawUserId));
-                externalForApp.add(buildPath(emulatedTargetBase, rawUserId));
-                // /data/media/0
-                mEmulatedDirForDirect = buildPath(mediaBase, rawUserId);
-
-            } else {
-                // Device has physical external storage; use plain paths.
-                if (TextUtils.isEmpty(rawExternalStorage)) {
-                    Log.w(TAG, "EXTERNAL_STORAGE undefined; falling back to default");
-                    rawExternalStorage = "/storage/sdcard0";
-                }
-
-                // /storage/sdcard0
-                externalForVold.add(new File(rawExternalStorage));
-                externalForApp.add(new File(rawExternalStorage));
-                // /data/media
-                mEmulatedDirForDirect = new File(rawMediaStorage);
-            }
-
-            // Splice in any secondary storage paths, but only for owner
-            final String rawSecondaryStorage = System.getenv(ENV_SECONDARY_STORAGE);
-            if (!TextUtils.isEmpty(rawSecondaryStorage) && userId == UserHandle.USER_OWNER) {
-                for (String secondaryPath : rawSecondaryStorage.split(":")) {
-                    externalForVold.add(new File(secondaryPath));
-                    externalForApp.add(new File(secondaryPath));
-                }
-            }
-
-            mExternalDirsForVold = externalForVold.toArray(new File[externalForVold.size()]);
-            mExternalDirsForApp = externalForApp.toArray(new File[externalForApp.size()]);
+            return dirs;
         }
 
         @Deprecated
         public File getExternalStorageDirectory() {
-            return mExternalDirsForApp[0];
+            return getExternalDirs()[0];
         }
 
         @Deprecated
@@ -155,60 +97,36 @@
             return buildExternalStoragePublicDirs(type)[0];
         }
 
-        public File[] getExternalDirsForVold() {
-            return mExternalDirsForVold;
-        }
-
-        public File[] getExternalDirsForApp() {
-            return mExternalDirsForApp;
-        }
-
-        public File getMediaDir() {
-            return mEmulatedDirForDirect;
-        }
-
         public File[] buildExternalStoragePublicDirs(String type) {
-            return buildPaths(mExternalDirsForApp, type);
+            return buildPaths(getExternalDirs(), type);
         }
 
         public File[] buildExternalStorageAndroidDataDirs() {
-            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA);
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA);
         }
 
         public File[] buildExternalStorageAndroidObbDirs() {
-            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_OBB);
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_OBB);
         }
 
         public File[] buildExternalStorageAppDataDirs(String packageName) {
-            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA, packageName);
-        }
-
-        public File[] buildExternalStorageAppDataDirsForVold(String packageName) {
-            return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_DATA, packageName);
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA, packageName);
         }
 
         public File[] buildExternalStorageAppMediaDirs(String packageName) {
-            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_MEDIA, packageName);
-        }
-
-        public File[] buildExternalStorageAppMediaDirsForVold(String packageName) {
-            return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_MEDIA, packageName);
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_MEDIA, packageName);
         }
 
         public File[] buildExternalStorageAppObbDirs(String packageName) {
-            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_OBB, packageName);
-        }
-
-        public File[] buildExternalStorageAppObbDirsForVold(String packageName) {
-            return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_OBB, packageName);
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_OBB, packageName);
         }
 
         public File[] buildExternalStorageAppFilesDirs(String packageName) {
-            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA, packageName, DIR_FILES);
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA, packageName, DIR_FILES);
         }
 
         public File[] buildExternalStorageAppCacheDirs(String packageName) {
-            return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_DATA, packageName, DIR_CACHE);
+            return buildPaths(getExternalDirs(), DIR_ANDROID, DIR_DATA, packageName, DIR_CACHE);
         }
     }
 
@@ -220,6 +138,11 @@
         return DIR_ANDROID_ROOT;
     }
 
+    /** {@hide} */
+    public static File getStorageDirectory() {
+        return DIR_ANDROID_STORAGE;
+    }
+
     /**
      * Return root directory of the "oem" partition holding OEM customizations,
      * if any. If present, the partition is mounted read-only.
@@ -270,17 +193,6 @@
     }
 
     /**
-     * Return directory used for internal media storage, which is protected by
-     * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE}.
-     *
-     * @hide
-     */
-    public static File getMediaStorageDirectory() {
-        throwIfUserRequired();
-        return sCurrentUser.getMediaDir();
-    }
-
-    /**
      * Return the system directory for a user. This is for use by system services to store
      * files relating to the user. This directory will be automatically deleted when the user
      * is removed.
@@ -389,7 +301,7 @@
      */
     public static File getExternalStorageDirectory() {
         throwIfUserRequired();
-        return sCurrentUser.getExternalDirsForApp()[0];
+        return sCurrentUser.getExternalDirs()[0];
     }
 
     /** {@hide} */
@@ -402,18 +314,6 @@
         return buildPath(getLegacyExternalStorageDirectory(), DIR_ANDROID, DIR_OBB);
     }
 
-    /** {@hide} */
-    public static File getEmulatedStorageSource(int userId) {
-        // /mnt/shell/emulated/0
-        return new File(System.getenv(ENV_EMULATED_STORAGE_SOURCE), String.valueOf(userId));
-    }
-
-    /** {@hide} */
-    public static File getEmulatedStorageObbSource() {
-        // /mnt/shell/emulated/obb
-        return new File(System.getenv(ENV_EMULATED_STORAGE_SOURCE), DIR_OBB);
-    }
-
     /**
      * Standard directory in which to place any audio files that should be
      * in the regular list of music for the user.
@@ -683,6 +583,13 @@
     public static final String MEDIA_UNMOUNTABLE = "unmountable";
 
     /**
+     * Storage state if the media is in the process of being ejected.
+     *
+     * @see #getExternalStorageState(File)
+     */
+    public static final String MEDIA_EJECTING = "ejecting";
+
+    /**
      * Returns the current state of the primary "external" storage device.
      * 
      * @see #getExternalStorageDirectory()
@@ -693,7 +600,7 @@
      *         {@link #MEDIA_BAD_REMOVAL}, or {@link #MEDIA_UNMOUNTABLE}.
      */
     public static String getExternalStorageState() {
-        final File externalDir = sCurrentUser.getExternalDirsForApp()[0];
+        final File externalDir = sCurrentUser.getExternalDirs()[0];
         return getExternalStorageState(externalDir);
     }
 
@@ -716,17 +623,12 @@
      *         {@link #MEDIA_BAD_REMOVAL}, or {@link #MEDIA_UNMOUNTABLE}.
      */
     public static String getExternalStorageState(File path) {
-        final StorageVolume volume = getStorageVolume(path);
+        final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId());
         if (volume != null) {
-            final IMountService mountService = IMountService.Stub.asInterface(
-                    ServiceManager.getService("mount"));
-            try {
-                return mountService.getVolumeState(volume.getPath());
-            } catch (RemoteException e) {
-            }
+            return volume.getState();
+        } else {
+            return MEDIA_UNKNOWN;
         }
-
-        return Environment.MEDIA_UNKNOWN;
     }
 
     /**
@@ -738,7 +640,7 @@
      */
     public static boolean isExternalStorageRemovable() {
         if (isStorageDisabled()) return false;
-        final File externalDir = sCurrentUser.getExternalDirsForApp()[0];
+        final File externalDir = sCurrentUser.getExternalDirs()[0];
         return isExternalStorageRemovable(externalDir);
     }
 
@@ -753,7 +655,7 @@
      *             device.
      */
     public static boolean isExternalStorageRemovable(File path) {
-        final StorageVolume volume = getStorageVolume(path);
+        final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId());
         if (volume != null) {
             return volume.isRemovable();
         } else {
@@ -771,7 +673,7 @@
      */
     public static boolean isExternalStorageEmulated() {
         if (isStorageDisabled()) return false;
-        final File externalDir = sCurrentUser.getExternalDirsForApp()[0];
+        final File externalDir = sCurrentUser.getExternalDirs()[0];
         return isExternalStorageEmulated(externalDir);
     }
 
@@ -784,7 +686,7 @@
      *             device.
      */
     public static boolean isExternalStorageEmulated(File path) {
-        final StorageVolume volume = getStorageVolume(path);
+        final StorageVolume volume = StorageManager.getStorageVolume(path, UserHandle.myUserId());
         if (volume != null) {
             return volume.isEmulated();
         } else {
@@ -797,19 +699,6 @@
         return path == null ? new File(defaultPath) : new File(path);
     }
 
-    private static String getCanonicalPathOrNull(String variableName) {
-        String path = System.getenv(variableName);
-        if (path == null) {
-            return null;
-        }
-        try {
-            return new File(path).getCanonicalPath();
-        } catch (IOException e) {
-            Log.w(TAG, "Unable to resolve canonical path for " + path);
-            return null;
-        }
-    }
-
     /** {@hide} */
     public static void setUserRequired(boolean userRequired) {
         sUserRequired = userRequired;
@@ -856,28 +745,6 @@
         return SystemProperties.getBoolean("config.disable_storage", false);
     }
 
-    private static StorageVolume getStorageVolume(File path) {
-        try {
-            path = path.getCanonicalFile();
-        } catch (IOException e) {
-            return null;
-        }
-
-        try {
-            final IMountService mountService = IMountService.Stub.asInterface(
-                    ServiceManager.getService("mount"));
-            final StorageVolume[] volumes = mountService.getVolumeList();
-            for (StorageVolume volume : volumes) {
-                if (FileUtils.contains(volume.getPathFile(), path)) {
-                    return volume;
-                }
-            }
-        } catch (RemoteException e) {
-        }
-
-        return null;
-    }
-
     /**
      * If the given path exists on emulated external storage, return the
      * translated backing path hosted on internal storage. This bypasses any
@@ -891,26 +758,7 @@
      * @hide
      */
     public static File maybeTranslateEmulatedPathToInternal(File path) {
-        // Fast return if not emulated, or missing variables
-        if (!Environment.isExternalStorageEmulated()
-                || CANONCIAL_EMULATED_STORAGE_TARGET == null) {
-            return path;
-        }
-
-        try {
-            final String rawPath = path.getCanonicalPath();
-            if (rawPath.startsWith(CANONCIAL_EMULATED_STORAGE_TARGET)) {
-                final File internalPath = new File(DIR_MEDIA_STORAGE,
-                        rawPath.substring(CANONCIAL_EMULATED_STORAGE_TARGET.length()));
-                if (internalPath.exists()) {
-                    return internalPath;
-                }
-            }
-        } catch (IOException e) {
-            Log.w(TAG, "Failed to resolve canonical path for " + path);
-        }
-
-        // Unable to translate to internal path; use original
+        // TODO: bring back this optimization
         return path;
     }
 }
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 0a724a1..b302f95 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -369,6 +369,23 @@
      * {@link File#getCanonicalFile()} to avoid symlink or path traversal
      * attacks.
      */
+    public static boolean contains(File[] dirs, File file) {
+        for (File dir : dirs) {
+            if (contains(dir, file)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Test if a file lives under the given directory, either as a direct child
+     * or a distant grandchild.
+     * <p>
+     * Both files <em>must</em> have been resolved using
+     * {@link File#getCanonicalFile()} to avoid symlink or path traversal
+     * attacks.
+     */
     public static boolean contains(File dir, File file) {
         if (file == null) return false;
 
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 0de9c70..355ec8c 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -637,10 +637,8 @@
             if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
                 argsForZygote.add("--enable-assert");
             }
-            if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER) {
-                argsForZygote.add("--mount-external-multiuser");
-            } else if (mountExternal == Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL) {
-                argsForZygote.add("--mount-external-multiuser-all");
+            if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
+                argsForZygote.add("--mount-external-default");
             }
             argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
 
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 6209c2a..fef12d1 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -757,12 +757,13 @@
                 return _result;
             }
 
-            public StorageVolume[] getVolumeList() throws RemoteException {
+            public StorageVolume[] getVolumeList(int userId) throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
                 StorageVolume[] _result;
                 try {
                     _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt(userId);
                     mRemote.transact(Stub.TRANSACTION_getVolumeList, _data, _reply, 0);
                     _reply.readException();
                     _result = _reply.createTypedArray(StorageVolume.CREATOR);
@@ -1308,7 +1309,8 @@
                 }
                 case TRANSACTION_getVolumeList: {
                     data.enforceInterface(DESCRIPTOR);
-                    StorageVolume[] result = getVolumeList();
+                    int userId = data.readInt();
+                    StorageVolume[] result = getVolumeList(userId);
                     reply.writeNoException();
                     reply.writeTypedArray(result, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                     return true;
@@ -1630,7 +1632,7 @@
     /**
      * Returns list of all mountable volumes.
      */
-    public StorageVolume[] getVolumeList() throws RemoteException;
+    public StorageVolume[] getVolumeList(int userId) throws RemoteException;
 
     /**
      * Gets the path on the filesystem for the ASEC container itself.
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 2785ee8..532bf2c 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -18,19 +18,23 @@
 
 import static android.net.TrafficStats.MB_IN_BYTES;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.os.Environment;
+import android.os.FileUtils;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.SparseArray;
 
+import libcore.util.EmptyArray;
+
 import com.android.internal.util.Preconditions;
 
 import java.io.File;
@@ -60,6 +64,7 @@
 public class StorageManager {
     private static final String TAG = "StorageManager";
 
+    private final Context mContext;
     private final ContentResolver mResolver;
 
     /*
@@ -311,8 +316,9 @@
      *
      * @hide
      */
-    public StorageManager(ContentResolver resolver, Looper tgtLooper) throws RemoteException {
-        mResolver = resolver;
+    public StorageManager(Context context, Looper tgtLooper) {
+        mContext = context;
+        mResolver = context.getContentResolver();
         mTgtLooper = tgtLooper;
         mMountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));
         if (mMountService == null) {
@@ -548,17 +554,46 @@
         return null;
     }
 
+    /** {@hide} */
+    public @Nullable StorageVolume getStorageVolume(File file) {
+        return getStorageVolume(getVolumeList(), file);
+    }
+
+    /** {@hide} */
+    public static @Nullable StorageVolume getStorageVolume(File file, int userId) {
+        return getStorageVolume(getVolumeList(userId), file);
+    }
+
+    /** {@hide} */
+    private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) {
+        File canonicalFile = null;
+        try {
+            canonicalFile = file.getCanonicalFile();
+        } catch (IOException ignored) {
+            canonicalFile = null;
+        }
+        for (StorageVolume volume : volumes) {
+            if (volume.getPathFile().equals(file)) {
+                return volume;
+            }
+            if (FileUtils.contains(volume.getPathFile(), canonicalFile)) {
+                return volume;
+            }
+        }
+        return null;
+    }
+
     /**
      * Gets the state of a volume via its mountpoint.
      * @hide
      */
-    public String getVolumeState(String mountPoint) {
-         if (mMountService == null) return Environment.MEDIA_REMOVED;
-        try {
-            return mMountService.getVolumeState(mountPoint);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get volume state", e);
-            return null;
+    @Deprecated
+    public @NonNull String getVolumeState(String mountPoint) {
+        final StorageVolume vol = getStorageVolume(new File(mountPoint));
+        if (vol != null) {
+            return vol.getState();
+        } else {
+            return Environment.MEDIA_UNKNOWN;
         }
     }
 
@@ -566,20 +601,22 @@
      * Returns list of all mountable volumes.
      * @hide
      */
-    public StorageVolume[] getVolumeList() {
-        if (mMountService == null) return new StorageVolume[0];
+    public @NonNull StorageVolume[] getVolumeList() {
         try {
-            Parcelable[] list = mMountService.getVolumeList();
-            if (list == null) return new StorageVolume[0];
-            int length = list.length;
-            StorageVolume[] result = new StorageVolume[length];
-            for (int i = 0; i < length; i++) {
-                result[i] = (StorageVolume)list[i];
-            }
-            return result;
+            return mMountService.getVolumeList(mContext.getUserId());
         } catch (RemoteException e) {
-            Log.e(TAG, "Failed to get volume list", e);
-            return null;
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /** {@hide} */
+    public static @NonNull StorageVolume[] getVolumeList(int userId) {
+        final IMountService mountService = IMountService.Stub.asInterface(
+                ServiceManager.getService("mount"));
+        try {
+            return mountService.getVolumeList(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
         }
     }
 
@@ -587,9 +624,9 @@
      * Returns list of paths for all mountable volumes.
      * @hide
      */
-    public String[] getVolumePaths() {
+    @Deprecated
+    public @NonNull String[] getVolumePaths() {
         StorageVolume[] volumes = getVolumeList();
-        if (volumes == null) return null;
         int count = volumes.length;
         String[] paths = new String[count];
         for (int i = 0; i < count; i++) {
@@ -599,21 +636,21 @@
     }
 
     /** {@hide} */
-    public StorageVolume getPrimaryVolume() {
+    public @NonNull StorageVolume getPrimaryVolume() {
         return getPrimaryVolume(getVolumeList());
     }
 
     /** {@hide} */
-    public static StorageVolume getPrimaryVolume(StorageVolume[] volumes) {
+    public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) {
         for (StorageVolume volume : volumes) {
             if (volume.isPrimary()) {
                 return volume;
             }
         }
-        Log.w(TAG, "No primary storage defined");
-        return null;
+        throw new IllegalStateException("Missing primary storage");
     }
 
+    /** {@hide} */
     private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
     private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES;
     private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 06565f1..0c391ca 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -17,6 +17,7 @@
 package android.os.storage;
 
 import android.content.Context;
+import android.net.TrafficStats;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
@@ -34,52 +35,58 @@
  */
 public class StorageVolume implements Parcelable {
 
-    // TODO: switch to more durable token
-    private int mStorageId;
+    private final String mId;
+    private final int mStorageId;
 
     private final File mPath;
     private final int mDescriptionId;
     private final boolean mPrimary;
     private final boolean mRemovable;
     private final boolean mEmulated;
-    private final int mMtpReserveSpace;
+    private final long mMtpReserveSize;
     private final boolean mAllowMassStorage;
     /** Maximum file size for the storage, or zero for no limit */
     private final long mMaxFileSize;
     /** When set, indicates exclusive ownership of this volume */
     private final UserHandle mOwner;
 
-    private String mUuid;
-    private String mUserLabel;
-    private String mState;
+    private final String mUuid;
+    private final String mUserLabel;
+    private final String mState;
 
     // StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING,
     // ACTION_MEDIA_NOFS, ACTION_MEDIA_MOUNTED, ACTION_MEDIA_SHARED, ACTION_MEDIA_UNSHARED,
     // ACTION_MEDIA_BAD_REMOVAL, ACTION_MEDIA_UNMOUNTABLE and ACTION_MEDIA_EJECT broadcasts.
     public static final String EXTRA_STORAGE_VOLUME = "storage_volume";
 
-    public StorageVolume(File path, int descriptionId, boolean primary, boolean removable,
-            boolean emulated, int mtpReserveSpace, boolean allowMassStorage, long maxFileSize,
-            UserHandle owner) {
+    public StorageVolume(String id, int storageId, File path, int descriptionId, boolean primary,
+            boolean removable, boolean emulated, long mtpReserveSize, boolean allowMassStorage,
+            long maxFileSize, UserHandle owner, String uuid, String userLabel, String state) {
+        mId = id;
+        mStorageId = storageId;
         mPath = path;
         mDescriptionId = descriptionId;
         mPrimary = primary;
         mRemovable = removable;
         mEmulated = emulated;
-        mMtpReserveSpace = mtpReserveSpace;
+        mMtpReserveSize = mtpReserveSize;
         mAllowMassStorage = allowMassStorage;
         mMaxFileSize = maxFileSize;
         mOwner = owner;
+        mUuid = uuid;
+        mUserLabel = userLabel;
+        mState = state;
     }
 
     private StorageVolume(Parcel in) {
+        mId = in.readString();
         mStorageId = in.readInt();
         mPath = new File(in.readString());
         mDescriptionId = in.readInt();
         mPrimary = in.readInt() != 0;
         mRemovable = in.readInt() != 0;
         mEmulated = in.readInt() != 0;
-        mMtpReserveSpace = in.readInt();
+        mMtpReserveSize = in.readLong();
         mAllowMassStorage = in.readInt() != 0;
         mMaxFileSize = in.readLong();
         mOwner = in.readParcelable(null);
@@ -88,10 +95,8 @@
         mState = in.readString();
     }
 
-    public static StorageVolume fromTemplate(StorageVolume template, File path, UserHandle owner) {
-        return new StorageVolume(path, template.mDescriptionId, template.mPrimary,
-                template.mRemovable, template.mEmulated, template.mMtpReserveSpace,
-                template.mAllowMassStorage, template.mMaxFileSize, owner);
+    public String getId() {
+        return mId;
     }
 
     /**
@@ -153,15 +158,6 @@
     }
 
     /**
-     * Do not call this unless you are MountService
-     */
-    public void setStorageId(int index) {
-        // storage ID is 0x00010001 for primary storage,
-        // then 0x00020001, 0x00030001, etc. for secondary storages
-        mStorageId = ((index + 1) << 16) + 1;
-    }
-
-    /**
      * Number of megabytes of space to leave unallocated by MTP.
      * MTP will subtract this value from the free space it reports back
      * to the host via GetStorageInfo, and will not allow new files to
@@ -174,7 +170,7 @@
      * @return MTP reserve space
      */
     public int getMtpReserveSpace() {
-        return mMtpReserveSpace;
+        return (int) (mMtpReserveSize / TrafficStats.MB_IN_BYTES);
     }
 
     /**
@@ -199,10 +195,6 @@
         return mOwner;
     }
 
-    public void setUuid(String uuid) {
-        mUuid = uuid;
-    }
-
     public String getUuid() {
         return mUuid;
     }
@@ -222,18 +214,10 @@
         }
     }
 
-    public void setUserLabel(String userLabel) {
-        mUserLabel = userLabel;
-    }
-
     public String getUserLabel() {
         return mUserLabel;
     }
 
-    public void setState(String state) {
-        mState = state;
-    }
-
     public String getState() {
         return mState;
     }
@@ -262,13 +246,14 @@
     public void dump(IndentingPrintWriter pw) {
         pw.println("StorageVolume:");
         pw.increaseIndent();
+        pw.printPair("mId", mId);
         pw.printPair("mStorageId", mStorageId);
         pw.printPair("mPath", mPath);
         pw.printPair("mDescriptionId", mDescriptionId);
         pw.printPair("mPrimary", mPrimary);
         pw.printPair("mRemovable", mRemovable);
         pw.printPair("mEmulated", mEmulated);
-        pw.printPair("mMtpReserveSpace", mMtpReserveSpace);
+        pw.printPair("mMtpReserveSize", mMtpReserveSize);
         pw.printPair("mAllowMassStorage", mAllowMassStorage);
         pw.printPair("mMaxFileSize", mMaxFileSize);
         pw.printPair("mOwner", mOwner);
@@ -297,13 +282,14 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mId);
         parcel.writeInt(mStorageId);
         parcel.writeString(mPath.toString());
         parcel.writeInt(mDescriptionId);
         parcel.writeInt(mPrimary ? 1 : 0);
         parcel.writeInt(mRemovable ? 1 : 0);
         parcel.writeInt(mEmulated ? 1 : 0);
-        parcel.writeInt(mMtpReserveSpace);
+        parcel.writeLong(mMtpReserveSize);
         parcel.writeInt(mAllowMassStorage ? 1 : 0);
         parcel.writeLong(mMaxFileSize);
         parcel.writeParcelable(mOwner, flags);
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index e4a6f07..bf7f3cb 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -18,6 +18,7 @@
 
 import android.accounts.Account;
 import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
 import android.content.ActivityNotFoundException;
 import android.content.ContentProviderClient;
 import android.content.ContentProviderOperation;
@@ -1628,7 +1629,6 @@
          */
         public static final String CONTENT_VCARD_TYPE = "text/x-vcard";
 
-
         /**
          * Mimimal ID for corp contacts returned from
          * {@link PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI}.
@@ -1638,6 +1638,14 @@
         public static long ENTERPRISE_CONTACT_ID_BASE = 1000000000; // slightly smaller than 2 ** 30
 
         /**
+         * Prefix for corp contacts returned from
+         * {@link PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI}.
+         *
+         * @hide
+         */
+        public static String ENTERPRISE_CONTACT_LOOKUP_PREFIX = "c-";
+
+        /**
          * Return TRUE if a contact ID is from the contacts provider on the enterprise profile.
          *
          * {@link PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI} may return such a contact.
@@ -5032,9 +5040,17 @@
          *     is from the corp profile, use
          *     {@link ContactsContract.Contacts#isEnterpriseContactId(long)}.
          *     </li>
+         *     <li>
+         *     Corp contacts will get artificial {@link #LOOKUP_KEY}s too.
+         *     </li>
          * </ul>
          * <p>
-         * This URI does NOT support selection nor order-by.
+         * A contact lookup URL built by
+         * {@link ContactsContract.Contacts#getLookupUri(long, String)}
+         * with an {@link #_ID} and a {@link #LOOKUP_KEY} returned by this API can be passed to
+         * {@link ContactsContract.QuickContact#showQuickContact} even if a contact is from the
+         * corp profile.
+         * </p>
          *
          * <pre>
          * Uri lookupUri = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI,
@@ -6025,10 +6041,18 @@
             *     a contact
             *     is from the corp profile, use
             *     {@link ContactsContract.Contacts#isEnterpriseContactId(long)}.
-            *     </li>
-            * </ul>
-            * <p>
-            * This URI does NOT support selection nor order-by.
+             *     </li>
+             *     <li>
+             *     Corp contacts will get artificial {@link #LOOKUP_KEY}s too.
+             *     </li>
+             * </ul>
+             * <p>
+             * A contact lookup URL built by
+             * {@link ContactsContract.Contacts#getLookupUri(long, String)}
+             * with an {@link #_ID} and a {@link #LOOKUP_KEY} returned by this API can be passed to
+             * {@link ContactsContract.QuickContact#showQuickContact} even if a contact is from the
+             * corp profile.
+             * </p>
             *
             * <pre>
             * Uri lookupUri = Uri.withAppendedPath(Email.ENTERPRISE_CONTENT_LOOKUP_URI,
@@ -8182,6 +8206,9 @@
          */
         public static final int MODE_LARGE = 3;
 
+        /** @hide */
+        public static final int MODE_DEFAULT = MODE_LARGE;
+
         /**
          * Constructs the QuickContacts intent with a view's rect.
          * @hide
@@ -8224,6 +8251,7 @@
             // Launch pivot dialog through intent for now
             final Intent intent = new Intent(ACTION_QUICK_CONTACT).addFlags(intentFlags);
 
+            // NOTE: This logic and rebuildManagedQuickContactsIntent() must be in sync.
             intent.setData(lookupUri);
             intent.setSourceBounds(target);
             intent.putExtra(EXTRA_MODE, mode);
@@ -8232,6 +8260,30 @@
         }
 
         /**
+         * Constructs a QuickContacts intent based on an incoming intent for DevicePolicyManager
+         * to strip off anything not necessary.
+         * 
+         * @hide
+         */
+        public static Intent rebuildManagedQuickContactsIntent(String lookupKey, long contactId,
+                Intent originalIntent) {
+            final Intent intent = new Intent(ACTION_QUICK_CONTACT);
+            // Rebuild the URI from a lookup key and a contact ID.
+            intent.setData(Contacts.getLookupUri(contactId, lookupKey));
+
+            // Copy flags and always set NEW_TASK because it won't have a parent activity.
+            intent.setFlags(originalIntent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
+
+            // Copy extras.
+            intent.setSourceBounds(originalIntent.getSourceBounds());
+            intent.putExtra(EXTRA_MODE, originalIntent.getIntExtra(EXTRA_MODE, MODE_DEFAULT));
+            intent.putExtra(EXTRA_EXCLUDE_MIMES,
+                    originalIntent.getStringArrayExtra(EXTRA_EXCLUDE_MIMES));
+            return intent;
+        }
+
+
+        /**
          * Trigger a dialog that lists the various methods of interacting with
          * the requested {@link Contacts} entry. This may be based on available
          * {@link ContactsContract.Data} rows under that contact, and may also
@@ -8259,7 +8311,7 @@
             // Trigger with obtained rectangle
             Intent intent = composeQuickContactsIntent(context, target, lookupUri, mode,
                     excludeMimes);
-            startActivityWithErrorToast(context, intent);
+            ContactsInternal.startQuickContactWithErrorToast(context, intent);
         }
 
         /**
@@ -8292,7 +8344,7 @@
                 String[] excludeMimes) {
             Intent intent = composeQuickContactsIntent(context, target, lookupUri, mode,
                     excludeMimes);
-            startActivityWithErrorToast(context, intent);
+            ContactsInternal.startQuickContactWithErrorToast(context, intent);
         }
 
         /**
@@ -8325,10 +8377,10 @@
             // Use MODE_LARGE instead of accepting mode as a parameter. The different mode
             // values defined in ContactsContract only affect very old implementations
             // of QuickContacts.
-            Intent intent = composeQuickContactsIntent(context, target, lookupUri, MODE_LARGE,
+            Intent intent = composeQuickContactsIntent(context, target, lookupUri, MODE_DEFAULT,
                     excludeMimes);
             intent.putExtra(EXTRA_PRIORITIZED_MIMETYPE, prioritizedMimeType);
-            startActivityWithErrorToast(context, intent);
+            ContactsInternal.startQuickContactWithErrorToast(context, intent);
         }
 
         /**
@@ -8363,19 +8415,10 @@
             // Use MODE_LARGE instead of accepting mode as a parameter. The different mode
             // values defined in ContactsContract only affect very old implementations
             // of QuickContacts.
-            Intent intent = composeQuickContactsIntent(context, target, lookupUri, MODE_LARGE,
+            Intent intent = composeQuickContactsIntent(context, target, lookupUri, MODE_DEFAULT,
                     excludeMimes);
             intent.putExtra(EXTRA_PRIORITIZED_MIMETYPE, prioritizedMimeType);
-            startActivityWithErrorToast(context, intent);
-        }
-
-        private static void startActivityWithErrorToast(Context context, Intent intent) {
-            try {
-              context.startActivity(intent);
-            } catch (ActivityNotFoundException e) {
-                Toast.makeText(context, com.android.internal.R.string.quick_contacts_not_available,
-                                Toast.LENGTH_SHORT).show();
-            }
+            ContactsInternal.startQuickContactWithErrorToast(context, intent);
         }
     }
 
diff --git a/core/java/android/provider/ContactsInternal.java b/core/java/android/provider/ContactsInternal.java
new file mode 100644
index 0000000..059a603
--- /dev/null
+++ b/core/java/android/provider/ContactsInternal.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package android.provider;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ActivityNotFoundException;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.content.UriMatcher;
+import android.net.Uri;
+import android.os.Process;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.widget.Toast;
+
+import java.util.List;
+
+/**
+ * Contacts related internal methods.
+ *
+ * @hide
+ */
+public class ContactsInternal {
+    private ContactsInternal() {
+    }
+
+    /** URI matcher used to parse contact URIs. */
+    private static final UriMatcher sContactsUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+
+    private static final int CONTACTS_URI_LOOKUP_ID = 1000;
+
+    static {
+        // Contacts URI matching table
+        final UriMatcher matcher = sContactsUriMatcher;
+        matcher.addURI(ContactsContract.AUTHORITY, "contacts/lookup/*/#", CONTACTS_URI_LOOKUP_ID);
+    }
+
+    /**
+     * Called by {@link ContactsContract} to star Quick Contact, possibly on the managed profile.
+     */
+    public static void startQuickContactWithErrorToast(Context context, Intent intent) {
+        final Uri uri = intent.getData();
+
+        final int match = sContactsUriMatcher.match(uri);
+        switch (match) {
+            case CONTACTS_URI_LOOKUP_ID: {
+                if (maybeStartManagedQuickContact(context, intent)) {
+                    return; // Request handled by DPM.  Just return here.
+                }
+                break;
+            }
+        }
+        // Launch on the current profile.
+        startQuickContactWithErrorToastForUser(context, intent, Process.myUserHandle());
+    }
+
+    public static void startQuickContactWithErrorToastForUser(Context context, Intent intent,
+            UserHandle user) {
+        try {
+            context.startActivityAsUser(intent, user);
+        } catch (ActivityNotFoundException e) {
+            Toast.makeText(context, com.android.internal.R.string.quick_contacts_not_available,
+                    Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    /**
+     * If the URI in {@code intent} is of a corp contact, launch quick contact on the managed
+     * profile.
+     *
+     * @return the URI in {@code intent} is of a corp contact thus launched on the managed profile.
+     */
+    private static boolean maybeStartManagedQuickContact(Context context, Intent originalIntent) {
+        final Uri uri = originalIntent.getData();
+
+        // Decompose into an ID and a lookup key.
+        final List<String> pathSegments = uri.getPathSegments();
+        final long contactId = ContentUris.parseId(uri);
+        final String lookupKey = pathSegments.get(2);
+
+        // See if it has a corp lookupkey.
+        if (TextUtils.isEmpty(lookupKey)
+                || !lookupKey.startsWith(
+                        ContactsContract.Contacts.ENTERPRISE_CONTACT_LOOKUP_PREFIX)) {
+            return false; // It's not a corp lookup key.
+        }
+
+        // Launch Quick Contact on the managed profile, if the policy allows.
+        final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+        final String actualLookupKey = lookupKey.substring(
+                ContactsContract.Contacts.ENTERPRISE_CONTACT_LOOKUP_PREFIX.length());
+        final long actualContactId =
+                (contactId - ContactsContract.Contacts.ENTERPRISE_CONTACT_ID_BASE);
+
+        dpm.startManagedQuickContact(actualLookupKey, actualContactId, originalIntent);
+        return true;
+    }
+}
diff --git a/core/java/android/provider/SearchIndexableData.java b/core/java/android/provider/SearchIndexableData.java
index 7b9d1ea..5e0a76d 100644
--- a/core/java/android/provider/SearchIndexableData.java
+++ b/core/java/android/provider/SearchIndexableData.java
@@ -16,6 +16,7 @@
 
 package android.provider;
 
+import android.annotation.SystemApi;
 import android.content.Context;
 
 import java.util.Locale;
@@ -27,6 +28,7 @@
  *
  * @hide
  */
+@SystemApi
 public abstract class SearchIndexableData {
 
     /**
diff --git a/core/java/android/provider/SearchIndexableResource.java b/core/java/android/provider/SearchIndexableResource.java
index c807df2..1eb1734 100644
--- a/core/java/android/provider/SearchIndexableResource.java
+++ b/core/java/android/provider/SearchIndexableResource.java
@@ -16,6 +16,7 @@
 
 package android.provider;
 
+import android.annotation.SystemApi;
 import android.content.Context;
 
 /**
@@ -31,6 +32,7 @@
  *
  * @hide
  */
+@SystemApi
 public class SearchIndexableResource extends SearchIndexableData {
 
     /**
diff --git a/core/java/android/provider/SearchIndexablesContract.java b/core/java/android/provider/SearchIndexablesContract.java
index 1b5f72a..93ac7f6 100644
--- a/core/java/android/provider/SearchIndexablesContract.java
+++ b/core/java/android/provider/SearchIndexablesContract.java
@@ -16,6 +16,7 @@
 
 package android.provider;
 
+import android.annotation.SystemApi;
 import android.content.ContentResolver;
 
 /**
@@ -23,6 +24,7 @@
  *
  * @hide
  */
+@SystemApi
 public class SearchIndexablesContract {
 
     /**
@@ -234,7 +236,7 @@
     /**
      * The base columns.
      */
-    private static class BaseColumns {
+    public static class BaseColumns {
         private BaseColumns() {
         }
 
diff --git a/core/java/android/provider/SearchIndexablesProvider.java b/core/java/android/provider/SearchIndexablesProvider.java
index 9c8f6d0..3120e54 100644
--- a/core/java/android/provider/SearchIndexablesProvider.java
+++ b/core/java/android/provider/SearchIndexablesProvider.java
@@ -16,6 +16,7 @@
 
 package android.provider;
 
+import android.annotation.SystemApi;
 import android.content.ContentProvider;
 import android.content.ContentValues;
 import android.content.Context;
@@ -61,6 +62,7 @@
  *
  * @hide
  */
+@SystemApi
 public abstract class SearchIndexablesProvider extends ContentProvider {
     private static final String TAG = "IndexablesProvider";
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fb51528..8e5d245 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6097,7 +6097,7 @@
        public static final String PACKAGE_VERIFIER_SETTING_VISIBLE = "verifier_setting_visible";
 
        /**
-        * Run package verificaiton on apps installed through ADB/ADT/USB
+        * Run package verification on apps installed through ADB/ADT/USB
         * 1 = perform package verification on ADB installs (default)
         * 0 = bypass package verification on ADB installs
         * @hide
diff --git a/core/java/android/security/IKeystoreService.aidl b/core/java/android/security/IKeystoreService.aidl
index 14b5748..579cdbe 100644
--- a/core/java/android/security/IKeystoreService.aidl
+++ b/core/java/android/security/IKeystoreService.aidl
@@ -73,4 +73,6 @@
     OperationResult update(IBinder token, in KeymasterArguments params, in byte[] input);
     OperationResult finish(IBinder token, in KeymasterArguments params, in byte[] signature);
     int abort(IBinder handle);
+    boolean isOperationAuthorized(IBinder token);
+    int addAuthToken(in byte[] authToken);
 }
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 1bdaef0..7d2e1ef 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -356,6 +356,8 @@
             ints[DESCENT] = desc;
             objects[0] = reflowed.getLineDirections(i);
 
+            ints[HYPHEN] = reflowed.getHyphen(i);
+
             if (mEllipsize) {
                 ints[ELLIPSIS_START] = reflowed.getEllipsisStart(i);
                 ints[ELLIPSIS_COUNT] = reflowed.getEllipsisCount(i);
@@ -631,6 +633,14 @@
         return mBottomPadding;
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public int getHyphen(int line) {
+        return mInts.getValue(line, HYPHEN);
+    }
+
     @Override
     public int getEllipsizedWidth() {
         return mEllipsizedWidth;
@@ -739,11 +749,12 @@
     private static final int TAB = START;
     private static final int TOP = 1;
     private static final int DESCENT = 2;
-    private static final int COLUMNS_NORMAL = 3;
+    private static final int HYPHEN = 3;
+    private static final int COLUMNS_NORMAL = 4;
 
-    private static final int ELLIPSIS_START = 3;
-    private static final int ELLIPSIS_COUNT = 4;
-    private static final int COLUMNS_ELLIPSIZE = 5;
+    private static final int ELLIPSIS_START = 4;
+    private static final int ELLIPSIS_COUNT = 5;
+    private static final int COLUMNS_ELLIPSIZE = 6;
 
     private static final int START_MASK = 0x1FFFFFFF;
     private static final int DIR_SHIFT  = 30;
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
new file mode 100644
index 0000000..f4dff9b
--- /dev/null
+++ b/core/java/android/text/Hyphenator.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.HashMap;
+import java.util.Locale;
+
+/**
+ * Hyphenator is a wrapper class for a native implementation of automatic hyphenation,
+ * in essence finding valid hyphenation opportunities in a word.
+ *
+ * @hide
+ */
+/* package */ class Hyphenator {
+    // This class has deliberately simple lifetime management (no finalizer) because in
+    // the common case a process will use a very small number of locales.
+
+    private static String TAG = "Hyphenator";
+
+    static HashMap<Locale, Hyphenator> sMap = new HashMap<Locale, Hyphenator>();
+
+    private long mNativePtr;
+
+    private Hyphenator(long nativePtr) {
+        mNativePtr = nativePtr;
+    }
+
+    public static long get(Locale locale) {
+        synchronized (sMap) {
+            Hyphenator result = sMap.get(locale);
+            if (result == null) {
+                result = loadHyphenator(locale);
+                sMap.put(locale, result);
+            }
+            return result == null ? 0 : result.mNativePtr;
+        }
+    }
+
+    private static Hyphenator loadHyphenator(Locale locale) {
+        // TODO: find pattern dictionary (from system location) that best matches locale
+        if (Locale.US.equals(locale)) {
+            File f = new File("/data/local/tmp/hyph-en-us.pat.txt");
+            try {
+                RandomAccessFile rf = new RandomAccessFile(f, "r");
+                byte[] buf = new byte[(int)rf.length()];
+                rf.read(buf);
+                rf.close();
+                String patternData = new String(buf);
+                long nativePtr = StaticLayout.nLoadHyphenator(patternData);
+                return new Hyphenator(nativePtr);
+            } catch (IOException e) {
+                Log.e(TAG, "error loading hyphenation " + f);
+            }
+        }
+        return null;
+    }
+}
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 928bf16..22abb18 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -225,17 +225,17 @@
 
         // Draw the lines, one at a time.
         // The baseline is the top of the following line minus the current line's descent.
-        for (int i = firstLine; i <= lastLine; i++) {
+        for (int lineNum = firstLine; lineNum <= lastLine; lineNum++) {
             int start = previousLineEnd;
-            previousLineEnd = getLineStart(i + 1);
-            int end = getLineVisibleEnd(i, start, previousLineEnd);
+            previousLineEnd = getLineStart(lineNum + 1);
+            int end = getLineVisibleEnd(lineNum, start, previousLineEnd);
 
             int ltop = previousLineBottom;
-            int lbottom = getLineTop(i+1);
+            int lbottom = getLineTop(lineNum + 1);
             previousLineBottom = lbottom;
-            int lbaseline = lbottom - getLineDescent(i);
+            int lbaseline = lbottom - getLineDescent(lineNum);
 
-            int dir = getParagraphDirection(i);
+            int dir = getParagraphDirection(lineNum);
             int left = 0;
             int right = mWidth;
 
@@ -254,7 +254,7 @@
                 // just collect the ones present at the start of the paragraph.
                 // If spanEnd is before the end of the paragraph, that's not
                 // our problem.
-                if (start >= spanEnd && (i == firstLine || isFirstParaLine)) {
+                if (start >= spanEnd && (lineNum == firstLine || isFirstParaLine)) {
                     spanEnd = sp.nextSpanTransition(start, textLength,
                                                     ParagraphStyle.class);
                     spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class);
@@ -280,7 +280,7 @@
                         int startLine = getLineForOffset(sp.getSpanStart(spans[n]));
                         // if there is more than one LeadingMarginSpan2, use
                         // the count that is greatest
-                        if (i < startLine + count) {
+                        if (lineNum < startLine + count) {
                             useFirstLineMargin = true;
                             break;
                         }
@@ -304,7 +304,7 @@
                 }
             }
 
-            boolean hasTabOrEmoji = getLineContainsTab(i);
+            boolean hasTabOrEmoji = getLineContainsTab(lineNum);
             // Can't tell if we have tabs for sure, currently
             if (hasTabOrEmoji && !tabStopsIsInitialized) {
                 if (tabStops == null) {
@@ -333,7 +333,7 @@
                     x = right;
                 }
             } else {
-                int max = (int)getLineExtent(i, tabStops, false);
+                int max = (int)getLineExtent(lineNum, tabStops, false);
                 if (align == Alignment.ALIGN_OPPOSITE) {
                     if (dir == DIR_LEFT_TO_RIGHT) {
                         x = right - max;
@@ -346,7 +346,8 @@
                 }
             }
 
-            Directions directions = getLineDirections(i);
+            paint.setHyphenEdit(getHyphen(lineNum));
+            Directions directions = getLineDirections(lineNum);
             if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTabOrEmoji) {
                 // XXX: assumes there's nothing additional to be done
                 canvas.drawText(buf, start, end, x, lbaseline, paint);
@@ -677,6 +678,15 @@
      */
     public abstract int getBottomPadding();
 
+    /**
+     * Returns the hyphen edit for a line.
+     *
+     * @hide
+     */
+    public int getHyphen(int line) {
+        return 0;
+    }
+
 
     /**
      * Returns true if the character at offset and the preceding character
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index b47418f..4174df0 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -170,7 +170,8 @@
          * Measurement and break iteration is done in native code. The protocol for using
          * the native code is as follows.
          *
-         * For each paragraph, do a nSetText of the paragraph text. Also do nSetLineWidth.
+         * For each paragraph, do a nSetupParagraph, which sets paragraph text, line width, tab
+         * stops, break strategy (and possibly other parameters in the future).
          *
          * Then, for each run within the paragraph:
          *  - setLocale (this must be done at least for the first run, optional afterwards)
@@ -187,7 +188,7 @@
 
         private void setLocale(Locale locale) {
             if (!locale.equals(mLocale)) {
-                nSetLocale(mNativePtr, locale.toLanguageTag());
+                nSetLocale(mNativePtr, locale.toLanguageTag(), Hyphenator.get(locale));
                 mLocale = locale;
             }
         }
@@ -531,7 +532,7 @@
 
             int[] breaks = lineBreaks.breaks;
             float[] lineWidths = lineBreaks.widths;
-            boolean[] flags = lineBreaks.flags;
+            int[] flags = lineBreaks.flags;
 
             // here is the offset of the starting character of the line we are currently measuring
             int here = paraStart;
@@ -617,7 +618,7 @@
                     fm.top, fm.bottom,
                     v,
                     spacingmult, spacingadd, null,
-                    null, fm, false,
+                    null, fm, 0,
                     needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd,
                     includepad, trackpad, null,
                     null, bufStart, ellipsize,
@@ -629,7 +630,7 @@
                       int above, int below, int top, int bottom, int v,
                       float spacingmult, float spacingadd,
                       LineHeightSpan[] chooseHt, int[] chooseHtv,
-                      Paint.FontMetricsInt fm, boolean hasTabOrEmoji,
+                      Paint.FontMetricsInt fm, int flags,
                       boolean needMultiply, byte[] chdirs, int dir,
                       boolean easy, int bufEnd, boolean includePad,
                       boolean trackPad, char[] chs,
@@ -722,8 +723,10 @@
         lines[off + mColumns + START] = end;
         lines[off + mColumns + TOP] = v;
 
-        if (hasTabOrEmoji)
-            lines[off + TAB] |= TAB_MASK;
+        // TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining
+        // one bit for start field
+        lines[off + TAB] |= flags & TAB_MASK;
+        lines[off + HYPHEN] = flags;
 
         lines[off + DIR] |= dir << DIR_SHIFT;
         Directions linedirs = DIRS_ALL_LEFT_TO_RIGHT;
@@ -942,6 +945,14 @@
         return mBottomPadding;
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    public int getHyphen(int line) {
+        return mLines[mColumns * line + HYPHEN] & 0xff;
+    }
+
     @Override
     public int getEllipsisCount(int line) {
         if (mColumns < COLUMNS_ELLIPSIZE) {
@@ -968,7 +979,10 @@
     private static native long nNewBuilder();
     private static native void nFreeBuilder(long nativePtr);
     private static native void nFinishBuilder(long nativePtr);
-    private static native void nSetLocale(long nativePtr, String locale);
+
+    /* package */ static native long nLoadHyphenator(String patternData);
+
+    private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator);
 
     // Set up paragraph text and settings; done as one big method to minimize jni crossings
     private static native void nSetupParagraph(long nativePtr, char[] text, int length,
@@ -991,22 +1005,23 @@
     // to reduce the number of JNI calls in the common case where the
     // arrays do not have to be resized
     private static native int nComputeLineBreaks(long nativePtr, LineBreaks recycle,
-            int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength);
+            int[] recycleBreaks, float[] recycleWidths, int[] recycleFlags, int recycleLength);
 
     private int mLineCount;
     private int mTopPadding, mBottomPadding;
     private int mColumns;
     private int mEllipsizedWidth;
 
-    private static final int COLUMNS_NORMAL = 3;
-    private static final int COLUMNS_ELLIPSIZE = 5;
+    private static final int COLUMNS_NORMAL = 4;
+    private static final int COLUMNS_ELLIPSIZE = 6;
     private static final int START = 0;
     private static final int DIR = START;
     private static final int TAB = START;
     private static final int TOP = 1;
     private static final int DESCENT = 2;
-    private static final int ELLIPSIS_START = 3;
-    private static final int ELLIPSIS_COUNT = 4;
+    private static final int HYPHEN = 3;
+    private static final int ELLIPSIS_START = 4;
+    private static final int ELLIPSIS_COUNT = 5;
 
     private int[] mLines;
     private Directions[] mLineDirections;
@@ -1028,7 +1043,7 @@
         private static final int INITIAL_SIZE = 16;
         public int[] breaks = new int[INITIAL_SIZE];
         public float[] widths = new float[INITIAL_SIZE];
-        public boolean[] flags = new boolean[INITIAL_SIZE]; // hasTabOrEmoji
+        public int[] flags = new int[INITIAL_SIZE]; // hasTabOrEmoji
         // breaks, widths, and flags should all have the same length
     }
 
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 4725581..479242c 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -955,6 +955,10 @@
                     span.updateDrawState(wp);
                 }
 
+                // Only draw hyphen on last run in line
+                if (jnext < mLen) {
+                    wp.setHyphenEdit(0);
+                }
                 x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x,
                         top, y, bottom, fmi, needWidth || jnext < measureLimit);
             }
diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java
index 84d9ce8..c44f42b 100644
--- a/core/java/android/util/DebugUtils.java
+++ b/core/java/android/util/DebugUtils.java
@@ -17,8 +17,10 @@
 package android.util;
 
 import java.io.PrintWriter;
-import java.lang.reflect.Method;
+import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.util.Locale;
 
 /**
@@ -203,4 +205,57 @@
         outBuilder.append(suffix);
         return outBuilder.toString();
     }
+
+    /**
+     * Use prefixed constants (static final values) on given class to turn value
+     * into human-readable string.
+     *
+     * @hide
+     */
+    public static String valueToString(Class<?> clazz, String prefix, int value) {
+        for (Field field : clazz.getDeclaredFields()) {
+            final int modifiers = field.getModifiers();
+            if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
+                    && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
+                try {
+                    if (value == field.getInt(null)) {
+                        return field.getName().substring(prefix.length());
+                    }
+                } catch (IllegalAccessException ignored) {
+                }
+            }
+        }
+        return Integer.toString(value);
+    }
+
+    /**
+     * Use prefixed constants (static final values) on given class to turn flags
+     * into human-readable string.
+     *
+     * @hide
+     */
+    public static String flagsToString(Class<?> clazz, String prefix, int flags) {
+        final StringBuilder res = new StringBuilder();
+
+        for (Field field : clazz.getDeclaredFields()) {
+            final int modifiers = field.getModifiers();
+            if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)
+                    && field.getType().equals(int.class) && field.getName().startsWith(prefix)) {
+                try {
+                    final int value = field.getInt(null);
+                    if ((flags & value) != 0) {
+                        flags &= ~value;
+                        res.append(field.getName().substring(prefix.length())).append('|');
+                    }
+                } catch (IllegalAccessException ignored) {
+                }
+            }
+        }
+        if (flags != 0 || res.length() == 0) {
+            res.append(Integer.toHexString(flags));
+        } else {
+            res.deleteCharAt(res.length() - 1);
+        }
+        return res.toString();
+    }
 }
diff --git a/core/java/android/view/IGraphicsStats.aidl b/core/java/android/view/IGraphicsStats.aidl
new file mode 100644
index 0000000..c235eb2
--- /dev/null
+++ b/core/java/android/view/IGraphicsStats.aidl
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * @hide
+ */
+interface IGraphicsStats {
+    ParcelFileDescriptor requestBufferForProcess(String packageName, IBinder token);
+}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 031be07..87d5d9a 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -23,7 +23,9 @@
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Binder;
 import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.Trace;
@@ -124,7 +126,7 @@
         mRootNode.setClipToBounds(false);
         mNativeProxy = nCreateProxy(translucent, rootNodePtr);
 
-        AtlasInitializer.sInstance.init(context, mNativeProxy);
+        ProcessInitializer.sInstance.init(context, mNativeProxy);
 
         loadSystemProperties();
     }
@@ -410,15 +412,44 @@
         nTrimMemory(level);
     }
 
-    private static class AtlasInitializer {
-        static AtlasInitializer sInstance = new AtlasInitializer();
+    public static void dumpProfileData(byte[] data, FileDescriptor fd) {
+        nDumpProfileData(data, fd);
+    }
+
+    private static class ProcessInitializer {
+        static ProcessInitializer sInstance = new ProcessInitializer();
+        static IGraphicsStats sGraphicsStatsService;
+        private static IBinder sProcToken;
 
         private boolean mInitialized = false;
 
-        private AtlasInitializer() {}
+        private ProcessInitializer() {}
 
         synchronized void init(Context context, long renderProxy) {
             if (mInitialized) return;
+            mInitialized = true;
+            initGraphicsStats(context, renderProxy);
+            initAssetAtlas(context, renderProxy);
+        }
+
+        private static void initGraphicsStats(Context context, long renderProxy) {
+            IBinder binder = ServiceManager.getService("graphicsstats");
+            if (binder == null) return;
+
+            sGraphicsStatsService = IGraphicsStats.Stub.asInterface(binder);
+            sProcToken = new Binder();
+            try {
+                final String pkg = context.getApplicationInfo().packageName;
+                ParcelFileDescriptor pfd = sGraphicsStatsService.
+                        requestBufferForProcess(pkg, sProcToken);
+                nSetProcessStatsBuffer(renderProxy, pfd.getFd());
+                pfd.close();
+            } catch (Exception e) {
+                Log.w(LOG_TAG, "Could not acquire gfx stats buffer", e);
+            }
+        }
+
+        private static void initAssetAtlas(Context context, long renderProxy) {
             IBinder binder = ServiceManager.getService("assetatlas");
             if (binder == null) return;
 
@@ -432,7 +463,6 @@
                             // TODO Remove after fixing b/15425820
                             validateMap(context, map);
                             nSetAtlas(renderProxy, buffer, map);
-                            mInitialized = true;
                         }
                         // If IAssetAtlas is not the same class as the IBinder
                         // we are using a remote service and we can safely
@@ -477,6 +507,7 @@
     static native void setupShadersDiskCache(String cacheFile);
 
     private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map);
+    private static native void nSetProcessStatsBuffer(long nativeProxy, int fd);
 
     private static native long nCreateRootRenderNode();
     private static native long nCreateProxy(boolean translucent, long rootRenderNode);
@@ -514,4 +545,5 @@
 
     private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd,
             @DumpFlags int dumpFlags);
+    private static native void nDumpProfileData(byte[] data, FileDescriptor fd);
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index cfcc6fe..a69384a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9959,6 +9959,8 @@
      * @param oldt Previous vertical scroll origin.
      */
     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        notifySubtreeAccessibilityStateChangedIfNeeded();
+
         if (AccessibilityManager.getInstance(mContext).isEnabled()) {
             postSendViewScrolledAccessibilityEventCallback();
         }
@@ -11162,6 +11164,7 @@
             invalidateViewProperty(false, true);
 
             invalidateParentIfNeededAndWasQuickRejected();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 50e64c6..a237afd 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1005,31 +1005,23 @@
             return fields;
         }
 
-        final ArrayList<Field> declaredFields = new ArrayList();
-        klass.getDeclaredFieldsUnchecked(false, declaredFields);
-
-        final ArrayList<Field> foundFields = new ArrayList<Field>();
-        final int count = declaredFields.size();
-        for (int i = 0; i < count; i++) {
-            final Field field = declaredFields.get(i);
-
-            // Ensure the field type can be resolved.
-            try {
-                field.getType();
-            } catch (NoClassDefFoundError e) {
-                continue;
+        try {
+            final Field[] declaredFields = klass.getDeclaredFieldsUnchecked(false);
+            final ArrayList<Field> foundFields = new ArrayList<Field>();
+            for (final Field field : declaredFields) {
+              // Fields which can't be resolved have a null type.
+              if (field.getType() != null && field.isAnnotationPresent(ExportedProperty.class)) {
+                  field.setAccessible(true);
+                  foundFields.add(field);
+                  sAnnotations.put(field, field.getAnnotation(ExportedProperty.class));
+              }
             }
-
-            if (field.isAnnotationPresent(ExportedProperty.class)) {
-                field.setAccessible(true);
-                foundFields.add(field);
-                sAnnotations.put(field, field.getAnnotation(ExportedProperty.class));
-            }
+            fields = foundFields.toArray(new Field[foundFields.size()]);
+            map.put(klass, fields);
+        } catch (NoClassDefFoundError e) {
+            throw new AssertionError(e);
         }
 
-        fields = foundFields.toArray(new Field[foundFields.size()]);
-        map.put(klass, fields);
-
         return fields;
     }
 
@@ -1651,4 +1643,4 @@
             }
         });
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 294174a..4158340 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6268,41 +6268,79 @@
 
 
             case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
-                if (mAccessibilityFocusedHost != null && mAccessibilityFocusedVirtualView != null) {
-                    // We care only for changes rooted in the focused host.
-                    final long eventSourceId = event.getSourceNodeId();
-                    final int hostViewId = AccessibilityNodeInfo.getAccessibilityViewId(
-                            eventSourceId);
-                    if (hostViewId != mAccessibilityFocusedHost.getAccessibilityViewId()) {
-                        break;
-                    }
-
-                    // We only care about changes that may change the virtual focused view bounds.
-                    final int changes = event.getContentChangeTypes();
-                    if ((changes & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) != 0
-                            || changes == AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED) {
-                        AccessibilityNodeProvider provider = mAccessibilityFocusedHost
-                                .getAccessibilityNodeProvider();
-                        if (provider != null) {
-                            final int virtualChildId = AccessibilityNodeInfo.getVirtualDescendantId(
-                                    mAccessibilityFocusedVirtualView.getSourceNodeId());
-                            if (virtualChildId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
-                                mAccessibilityFocusedVirtualView = provider
-                                        .createAccessibilityNodeInfo(
-                                                AccessibilityNodeProvider.HOST_VIEW_ID);
-                            } else {
-                                mAccessibilityFocusedVirtualView = provider
-                                        .createAccessibilityNodeInfo(virtualChildId);
-                            }
-                        }
-                    }
-                }
+                handleWindowContentChangedEvent(event);
             } break;
         }
         mAccessibilityManager.sendAccessibilityEvent(event);
         return true;
     }
 
+    /**
+     * Updates the focused virtual view, when necessary, in response to a
+     * content changed event.
+     * <p>
+     * This is necessary to get updated bounds after a position change.
+     *
+     * @param event an accessibility event of type
+     *              {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}
+     */
+    private void handleWindowContentChangedEvent(AccessibilityEvent event) {
+        // No virtual view focused, nothing to do here.
+        if (mAccessibilityFocusedHost == null || mAccessibilityFocusedVirtualView == null) {
+            return;
+        }
+
+        // If we have a node but no provider, abort.
+        final AccessibilityNodeProvider provider =
+                mAccessibilityFocusedHost.getAccessibilityNodeProvider();
+        if (provider == null) {
+            // TODO: Should we clear the focused virtual view?
+            return;
+        }
+
+        // We only care about change types that may affect the bounds of the
+        // focused virtual view.
+        final int changes = event.getContentChangeTypes();
+        if ((changes & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) == 0
+                && changes != AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED) {
+            return;
+        }
+
+        final long eventSourceNodeId = event.getSourceNodeId();
+        final int changedViewId = AccessibilityNodeInfo.getAccessibilityViewId(eventSourceNodeId);
+
+        // Search up the tree for subtree containment.
+        boolean hostInSubtree = false;
+        View root = mAccessibilityFocusedHost;
+        while (root != null && !hostInSubtree) {
+            if (changedViewId == root.getAccessibilityViewId()) {
+                hostInSubtree = true;
+            } else {
+                final ViewParent parent = root.getParent();
+                if (parent instanceof View) {
+                    root = (View) parent;
+                } else {
+                    root = null;
+                }
+            }
+        }
+
+        // We care only about changes in subtrees containing the host view.
+        if (!hostInSubtree) {
+            return;
+        }
+
+        final long focusedSourceNodeId = mAccessibilityFocusedVirtualView.getSourceNodeId();
+        int focusedChildId = AccessibilityNodeInfo.getVirtualDescendantId(focusedSourceNodeId);
+        if (focusedChildId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+            // TODO: Should we clear the focused virtual view?
+            focusedChildId = AccessibilityNodeProvider.HOST_VIEW_ID;
+        }
+
+        // Refresh the node for the focused virtual view.
+        mAccessibilityFocusedVirtualView = provider.createAccessibilityNodeInfo(focusedChildId);
+    }
+
     @Override
     public void notifySubtreeAccessibilityStateChanged(View child, View source, int changeType) {
         postSendWindowContentChangedCallback(source, changeType);
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 53c7e04..8a2b3fa 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -200,8 +200,6 @@
     public static final int ERROR_FILE_NOT_FOUND = -14;
     /** Too many requests during this load */
     public static final int ERROR_TOO_MANY_REQUESTS = -15;
-    /** Request blocked by the browser */
-    public static final int ERROR_BLOCKED = -16;
 
     /**
      * Report an error to the host application. These errors are unrecoverable
diff --git a/core/java/android/widget/AnalogClock.java b/core/java/android/widget/AnalogClock.java
index 1716dbd..45eee34 100644
--- a/core/java/android/widget/AnalogClock.java
+++ b/core/java/android/widget/AnalogClock.java
@@ -40,8 +40,10 @@
  * @attr ref android.R.styleable#AnalogClock_dial
  * @attr ref android.R.styleable#AnalogClock_hand_hour
  * @attr ref android.R.styleable#AnalogClock_hand_minute
+ * @deprecated This widget is no longer supported.
  */
 @RemoteView
+@Deprecated
 public class AnalogClock extends View {
     private Time mCalendar;
 
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 7b8a979..a157087 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -49,7 +49,6 @@
  * A delegate for picking up a date (day / month / year).
  */
 class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
-
     private static final int USE_LOCALE = 0;
 
     private static final int UNINITIALIZED = -1;
@@ -61,9 +60,9 @@
 
     private static final int ANIMATION_DURATION = 300;
 
-    public static final int[] ATTRS_TEXT_COLOR = new int[]{com.android.internal.R.attr.textColor};
-
-    public static final int[] ATTRS_DISABLED_ALPHA = new int[]{
+    private static final int[] ATTRS_TEXT_COLOR = new int[] {
+            com.android.internal.R.attr.textColor};
+    private static final int[] ATTRS_DISABLED_ALPHA = new int[] {
             com.android.internal.R.attr.disabledAlpha};
 
     private SimpleDateFormat mYearFormat;
@@ -157,6 +156,8 @@
             header.setBackground(a.getDrawable(R.styleable.DatePicker_headerBackground));
         }
 
+        a.recycle();
+
         // Set up picker container.
         mAnimator = (ViewAnimator) mContainer.findViewById(R.id.animator);
 
@@ -174,32 +175,10 @@
         mYearPickerView.setDate(mCurrentDate.getTimeInMillis());
         mYearPickerView.setOnYearSelectedListener(mOnYearSelectedListener);
 
-        final int yearTextAppearanceResId = a.getResourceId(
-                R.styleable.DatePicker_yearListItemTextAppearance, 0);
-        if (yearTextAppearanceResId != 0) {
-            mYearPickerView.setYearTextAppearance(yearTextAppearanceResId);
-        }
-
-        final int yearActivatedTextAppearanceResId = a.getResourceId(
-                R.styleable.DatePicker_yearListItemActivatedTextAppearance, 0);
-        if (yearActivatedTextAppearanceResId != 0) {
-            mYearPickerView.setYearActivatedTextAppearance(yearActivatedTextAppearanceResId);
-        }
-
-        a.recycle();
-
         // Set up content descriptions.
         mSelectDay = res.getString(R.string.select_day);
         mSelectYear = res.getString(R.string.select_year);
 
-        final Animation inAnim = new AlphaAnimation(0, 1);
-        inAnim.setDuration(ANIMATION_DURATION);
-        mAnimator.setInAnimation(inAnim);
-
-        final Animation outAnim = new AlphaAnimation(1, 0);
-        outAnim.setDuration(ANIMATION_DURATION);
-        mAnimator.setOutAnimation(outAnim);
-
         // Initialize for current locale. This also initializes the date, so no
         // need to call onDateChanged.
         onLocaleChanged(mCurrentLocale);
diff --git a/core/java/android/widget/DayPickerAdapter.java b/core/java/android/widget/DayPickerAdapter.java
index 4f9f09e..9a4b6f5 100644
--- a/core/java/android/widget/DayPickerAdapter.java
+++ b/core/java/android/widget/DayPickerAdapter.java
@@ -18,10 +18,15 @@
 
 import com.android.internal.widget.PagerAdapter;
 
+import android.annotation.IdRes;
+import android.annotation.LayoutRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.util.SparseArray;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.SimpleMonthView.OnDayClickListener;
@@ -37,9 +42,13 @@
     private final Calendar mMinDate = Calendar.getInstance();
     private final Calendar mMaxDate = Calendar.getInstance();
 
-    private final SparseArray<SimpleMonthView> mItems = new SparseArray<>();
+    private final SparseArray<ViewHolder> mItems = new SparseArray<>();
 
-    private Calendar mSelectedDay = Calendar.getInstance();
+    private final LayoutInflater mInflater;
+    private final int mLayoutResId;
+    private final int mCalendarViewId;
+
+    private Calendar mSelectedDay = null;
 
     private int mMonthTextAppearance;
     private int mDayOfWeekTextAppearance;
@@ -51,19 +60,29 @@
 
     private OnDaySelectedListener mOnDaySelectedListener;
 
+    private int mCount;
     private int mFirstDayOfWeek;
 
-    public DayPickerAdapter(Context context) {
+    public DayPickerAdapter(@NonNull Context context, @LayoutRes int layoutResId,
+            @IdRes int calendarViewId) {
+        mInflater = LayoutInflater.from(context);
+        mLayoutResId = layoutResId;
+        mCalendarViewId = calendarViewId;
+
         final TypedArray ta = context.obtainStyledAttributes(new int[] {
                 com.android.internal.R.attr.colorControlHighlight});
         mDayHighlightColor = ta.getColorStateList(0);
         ta.recycle();
     }
 
-    public void setRange(Calendar min, Calendar max) {
+    public void setRange(@NonNull Calendar min, @NonNull Calendar max) {
         mMinDate.setTimeInMillis(min.getTimeInMillis());
         mMaxDate.setTimeInMillis(max.getTimeInMillis());
 
+        final int diffYear = mMaxDate.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR);
+        final int diffMonth = mMaxDate.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH);
+        mCount = diffMonth + MONTHS_IN_YEAR * diffYear + 1;
+
         // Positions are now invalid, clear everything and start over.
         notifyDataSetChanged();
     }
@@ -80,7 +99,7 @@
         // Update displayed views.
         final int count = mItems.size();
         for (int i = 0; i < count; i++) {
-            final SimpleMonthView monthView = mItems.valueAt(i);
+            final SimpleMonthView monthView = mItems.valueAt(i).calendar;
             monthView.setFirstDayOfWeek(weekStart);
         }
     }
@@ -94,23 +113,25 @@
      *
      * @param day the selected day
      */
-    public void setSelectedDay(Calendar day) {
+    public void setSelectedDay(@Nullable Calendar day) {
         final int oldPosition = getPositionForDay(mSelectedDay);
         final int newPosition = getPositionForDay(day);
 
         // Clear the old position if necessary.
-        if (oldPosition != newPosition) {
-            final SimpleMonthView oldMonthView = mItems.get(oldPosition, null);
+        if (oldPosition != newPosition && oldPosition >= 0) {
+            final ViewHolder oldMonthView = mItems.get(oldPosition, null);
             if (oldMonthView != null) {
-                oldMonthView.setSelectedDay(-1);
+                oldMonthView.calendar.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);
+        if (newPosition >= 0) {
+            final ViewHolder newMonthView = mItems.get(newPosition, null);
+            if (newMonthView != null) {
+                final int dayOfMonth = day.get(Calendar.DAY_OF_MONTH);
+                newMonthView.calendar.setSelectedDay(dayOfMonth);
+            }
         }
 
         mSelectedDay = day;
@@ -155,14 +176,13 @@
 
     @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;
+        return mCount;
     }
 
     @Override
     public boolean isViewFromObject(View view, Object object) {
-        return view == object;
+        final ViewHolder holder = (ViewHolder) object;
+        return view == holder.container;
     }
 
     private int getMonthForPosition(int position) {
@@ -173,7 +193,11 @@
         return position / MONTHS_IN_YEAR + mMinDate.get(Calendar.YEAR);
     }
 
-    private int getPositionForDay(Calendar day) {
+    private int getPositionForDay(@Nullable Calendar day) {
+        if (day == null) {
+            return -1;
+        }
+
         final int yearOffset = (day.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR));
         final int monthOffset = (day.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH));
         return yearOffset * MONTHS_IN_YEAR + monthOffset;
@@ -181,7 +205,9 @@
 
     @Override
     public Object instantiateItem(ViewGroup container, int position) {
-        final SimpleMonthView v = new SimpleMonthView(container.getContext());
+        final View itemView = mInflater.inflate(mLayoutResId, container, false);
+
+        final SimpleMonthView v = (SimpleMonthView) itemView.findViewById(mCalendarViewId);
         v.setOnDayClickListener(mOnDayClickListener);
         v.setMonthTextAppearance(mMonthTextAppearance);
         v.setDayOfWeekTextAppearance(mDayOfWeekTextAppearance);
@@ -205,7 +231,7 @@
         final int year = getYearForPosition(position);
 
         final int selectedDay;
-        if (mSelectedDay.get(Calendar.MONTH) == month) {
+        if (mSelectedDay != null && mSelectedDay.get(Calendar.MONTH) == month) {
             selectedDay = mSelectedDay.get(Calendar.DAY_OF_MONTH);
         } else {
             selectedDay = -1;
@@ -227,33 +253,34 @@
 
         v.setMonthParams(selectedDay, month, year, mFirstDayOfWeek,
                 enabledDayRangeStart, enabledDayRangeEnd);
+        v.setPrevEnabled(position > 0);
+        v.setNextEnabled(position < mCount - 1);
 
-        mItems.put(position, v);
+        final ViewHolder holder = new ViewHolder(position, itemView, v);
+        mItems.put(position, holder);
 
-        container.addView(v);
+        container.addView(itemView);
 
-        return v;
+        return holder;
     }
 
     @Override
     public void destroyItem(ViewGroup container, int position, Object object) {
-        container.removeView(mItems.get(position));
+        final ViewHolder holder = (ViewHolder) object;
+        container.removeView(holder.container);
 
         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;
+        final ViewHolder holder = (ViewHolder) object;
+        return holder.position;
     }
 
     @Override
     public CharSequence getPageTitle(int position) {
-        final SimpleMonthView v = mItems.get(position);
+        final SimpleMonthView v = mItems.get(position).calendar;
         if (v != null) {
             return v.getTitle();
         }
@@ -275,9 +302,29 @@
                 }
             }
         }
+
+        @Override
+        public void onNavigationClick(SimpleMonthView view, int direction, boolean animate) {
+            if (mOnDaySelectedListener != null) {
+                mOnDaySelectedListener.onNavigationClick(DayPickerAdapter.this, direction, animate);
+            }
+        }
     };
 
+    private static class ViewHolder {
+        public final int position;
+        public final View container;
+        public final SimpleMonthView calendar;
+
+        public ViewHolder(int position, View container, SimpleMonthView calendar) {
+            this.position = position;
+            this.container = container;
+            this.calendar = calendar;
+        }
+    }
+
     public interface OnDaySelectedListener {
         public void onDaySelected(DayPickerAdapter view, Calendar day);
+        public void onNavigationClick(DayPickerAdapter view, int direction, boolean animate);
     }
 }
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index a7ae926..e2f8efc 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -88,7 +88,8 @@
         a.recycle();
 
         // Set up adapter.
-        mAdapter = new DayPickerAdapter(context);
+        mAdapter = new DayPickerAdapter(context,
+                R.layout.date_picker_month_item_material, R.id.month_view);
         mAdapter.setMonthTextAppearance(monthTextAppearanceResId);
         mAdapter.setDayOfWeekTextAppearance(dayOfWeekTextAppearanceResId);
         mAdapter.setDayTextAppearance(dayTextAppearanceResId);
@@ -128,6 +129,14 @@
                     mOnDaySelectedListener.onDaySelected(DayPickerView.this, day);
                 }
             }
+
+            @Override
+            public void onNavigationClick(DayPickerAdapter view, int direction, boolean animate) {
+                // ViewPager clamps input values, so we don't need to worry
+                // about passing invalid indices.
+                final int nextItem = getCurrentItem() + direction;
+                setCurrentItem(nextItem, animate);
+            }
         });
     }
 
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 6e24837..f9729a2 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -16,18 +16,6 @@
 
 package android.widget;
 
-import android.content.UndoManager;
-import android.content.UndoOperation;
-import android.content.UndoOwner;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.InputFilter;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.GrowingArrayUtils;
-import com.android.internal.view.menu.MenuBuilder;
-import com.android.internal.widget.EditableInputConnection;
-
 import android.R;
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
@@ -35,6 +23,9 @@
 import android.content.ClipData.Item;
 import android.content.Context;
 import android.content.Intent;
+import android.content.UndoManager;
+import android.content.UndoOperation;
+import android.content.UndoOwner;
 import android.content.pm.PackageManager;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
@@ -46,13 +37,17 @@
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 import android.inputmethodservice.ExtractEditText;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.ParcelableParcel;
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.text.DynamicLayout;
 import android.text.Editable;
+import android.text.InputFilter;
 import android.text.InputType;
 import android.text.Layout;
 import android.text.ParcelableSpan;
@@ -105,6 +100,11 @@
 import android.widget.TextView.Drawables;
 import android.widget.TextView.OnEditorActionListener;
 
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.GrowingArrayUtils;
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.widget.EditableInputConnection;
+
 import java.text.BreakIterator;
 import java.util.Arrays;
 import java.util.Comparator;
@@ -3066,97 +3066,6 @@
         showSuggestions();
     }
 
-    private class ActionPopupWindow extends PinnedPopupWindow implements OnClickListener {
-        private static final int POPUP_TEXT_LAYOUT =
-                com.android.internal.R.layout.text_edit_action_popup_text;
-        private TextView mPasteTextView;
-        private TextView mReplaceTextView;
-
-        @Override
-        protected void createPopupWindow() {
-            mPopupWindow = new PopupWindow(mTextView.getContext(), null,
-                    com.android.internal.R.attr.textSelectHandleWindowStyle);
-            mPopupWindow.setClippingEnabled(true);
-        }
-
-        @Override
-        protected void initContentView() {
-            LinearLayout linearLayout = new LinearLayout(mTextView.getContext());
-            linearLayout.setOrientation(LinearLayout.HORIZONTAL);
-            mContentView = linearLayout;
-            mContentView.setBackgroundResource(
-                    com.android.internal.R.drawable.text_edit_paste_window);
-
-            LayoutInflater inflater = (LayoutInflater) mTextView.getContext().
-                    getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
-            LayoutParams wrapContent = new LayoutParams(
-                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
-            mPasteTextView = (TextView) inflater.inflate(POPUP_TEXT_LAYOUT, null);
-            mPasteTextView.setLayoutParams(wrapContent);
-            mContentView.addView(mPasteTextView);
-            mPasteTextView.setText(com.android.internal.R.string.paste);
-            mPasteTextView.setOnClickListener(this);
-
-            mReplaceTextView = (TextView) inflater.inflate(POPUP_TEXT_LAYOUT, null);
-            mReplaceTextView.setLayoutParams(wrapContent);
-            mContentView.addView(mReplaceTextView);
-            mReplaceTextView.setText(com.android.internal.R.string.replace);
-            mReplaceTextView.setOnClickListener(this);
-        }
-
-        @Override
-        public void show() {
-            boolean canPaste = mTextView.canPaste();
-            boolean canSuggest = mTextView.isSuggestionsEnabled() && isCursorInsideSuggestionSpan();
-            mPasteTextView.setVisibility(canPaste ? View.VISIBLE : View.GONE);
-            mReplaceTextView.setVisibility(canSuggest ? View.VISIBLE : View.GONE);
-
-            if (!canPaste && !canSuggest) return;
-
-            super.show();
-        }
-
-        @Override
-        public void onClick(View view) {
-            if (view == mPasteTextView && mTextView.canPaste()) {
-                mTextView.onTextContextMenuItem(TextView.ID_PASTE);
-                hide();
-            } else if (view == mReplaceTextView) {
-                onReplace();
-            }
-        }
-
-        @Override
-        protected int getTextOffset() {
-            return (mTextView.getSelectionStart() + mTextView.getSelectionEnd()) / 2;
-        }
-
-        @Override
-        protected int getVerticalLocalPosition(int line) {
-            return mTextView.getLayout().getLineTop(line) - mContentView.getMeasuredHeight();
-        }
-
-        @Override
-        protected int clipVertically(int positionY) {
-            if (positionY < 0) {
-                final int offset = getTextOffset();
-                final Layout layout = mTextView.getLayout();
-                final int line = layout.getLineForOffset(offset);
-                positionY += layout.getLineBottom(line) - layout.getLineTop(line);
-                positionY += mContentView.getMeasuredHeight();
-
-                // Assumes insertion and selection handles share the same height
-                final Drawable handle = mTextView.getContext().getDrawable(
-                        mTextView.mTextSelectHandleRes);
-                positionY += handle.getIntrinsicHeight();
-            }
-
-            return positionY;
-        }
-    }
-
     /**
      * A listener to call {@link InputMethodManager#updateCursorAnchorInfo(View, CursorAnchorInfo)}
      * while the input method is requesting the cursor/anchor position. Does nothing as long as
@@ -4102,7 +4011,6 @@
     }
 
     class SelectionModifierCursorController implements CursorController {
-        private static final int DELAY_BEFORE_REPLACE_ACTION = 200; // milliseconds
         // The cursor controller handles, lazily created when shown.
         private SelectionStartHandleView mStartHandle;
         private SelectionEndHandleView mEndHandle;
diff --git a/core/java/android/widget/RadialTimePickerView.java b/core/java/android/widget/RadialTimePickerView.java
index 20aa972..143dea4 100644
--- a/core/java/android/widget/RadialTimePickerView.java
+++ b/core/java/android/widget/RadialTimePickerView.java
@@ -122,8 +122,9 @@
     private final Paint mPaintCenter = new Paint();
 
     private final Paint[][] mPaintSelector = new Paint[2][3];
-    private final int[][] mColorSelector = new int[2][3];
-    private final IntHolder[][] mAlphaSelector = new IntHolder[2][3];
+
+    private final int mSelectorColor;
+    private final int mSelectorDotColor;
 
     private final Paint mPaintBackground = new Paint();
 
@@ -147,6 +148,8 @@
 
     private final RadialPickerTouchHelper mTouchHelper;
 
+    private final Path mSelectorPath = new Path();
+
     private boolean mIs24HourMode;
     private boolean mShowHours;
 
@@ -316,11 +319,6 @@
         for (int i = 0; i < mAlpha.length; i++) {
             mAlpha[i] = new IntHolder(ALPHA_OPAQUE);
         }
-        for (int i = 0; i < mAlphaSelector.length; i++) {
-            for (int j = 0; j < mAlphaSelector[i].length; j++) {
-                mAlphaSelector[i][j] = new IntHolder(ALPHA_OPAQUE);
-            }
-        }
 
         mTextColor[HOURS] = a.getColorStateList(R.styleable.TimePicker_numbersTextColor);
         mTextColor[HOURS_INNER] = a.getColorStateList(R.styleable.TimePicker_numbersInnerTextColor);
@@ -345,33 +343,28 @@
         final int[] activatedStateSet = StateSet.get(
                 StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED);
 
+        mSelectorColor = selectorActivatedColor;
+        mSelectorDotColor = mTextColor[HOURS].getColorForState(activatedStateSet, 0);
+
         mPaintSelector[HOURS][SELECTOR_CIRCLE] = new Paint();
         mPaintSelector[HOURS][SELECTOR_CIRCLE].setAntiAlias(true);
-        mColorSelector[HOURS][SELECTOR_CIRCLE] = selectorActivatedColor;
 
         mPaintSelector[HOURS][SELECTOR_DOT] = new Paint();
         mPaintSelector[HOURS][SELECTOR_DOT].setAntiAlias(true);
-        mColorSelector[HOURS][SELECTOR_DOT] =
-                mTextColor[HOURS].getColorForState(activatedStateSet, 0);
 
         mPaintSelector[HOURS][SELECTOR_LINE] = new Paint();
         mPaintSelector[HOURS][SELECTOR_LINE].setAntiAlias(true);
         mPaintSelector[HOURS][SELECTOR_LINE].setStrokeWidth(2);
-        mColorSelector[HOURS][SELECTOR_LINE] = selectorActivatedColor;
 
         mPaintSelector[MINUTES][SELECTOR_CIRCLE] = new Paint();
         mPaintSelector[MINUTES][SELECTOR_CIRCLE].setAntiAlias(true);
-        mColorSelector[MINUTES][SELECTOR_CIRCLE] = selectorActivatedColor;
 
         mPaintSelector[MINUTES][SELECTOR_DOT] = new Paint();
         mPaintSelector[MINUTES][SELECTOR_DOT].setAntiAlias(true);
-        mColorSelector[MINUTES][SELECTOR_DOT] =
-                mTextColor[MINUTES].getColorForState(activatedStateSet, 0);
 
         mPaintSelector[MINUTES][SELECTOR_LINE] = new Paint();
         mPaintSelector[MINUTES][SELECTOR_LINE].setAntiAlias(true);
         mPaintSelector[MINUTES][SELECTOR_LINE].setStrokeWidth(2);
-        mColorSelector[MINUTES][SELECTOR_LINE] = selectorActivatedColor;
 
         mPaintBackground.setColor(a.getColor(R.styleable.TimePicker_numbersBackgroundColor,
                 context.getColor(R.color.timepicker_default_numbers_background_color_material)));
@@ -600,8 +593,8 @@
         // Initialize the hours and minutes numbers.
         for (int i = 0; i < 12; i++) {
             mHours12Texts[i] = String.format("%d", HOURS_NUMBERS[i]);
-            mOuterHours24Texts[i] = String.format("%02d", HOURS_NUMBERS_24[i]);
-            mInnerHours24Texts[i] = String.format("%d", HOURS_NUMBERS[i]);
+            mInnerHours24Texts[i] = String.format("%02d", HOURS_NUMBERS_24[i]);
+            mOuterHours24Texts[i] = String.format("%d", HOURS_NUMBERS[i]);
             mMinutesTexts[i] = String.format("%02d", MINUTES_NUMBERS[i]);
         }
     }
@@ -612,22 +605,16 @@
             mInnerTextHours = mInnerHours24Texts;
         } else {
             mOuterTextHours = mHours12Texts;
-            mInnerTextHours = null;
+            mInnerTextHours = mHours12Texts;
         }
 
         mOuterTextMinutes = mMinutesTexts;
 
         final int hoursAlpha = mShowHours ? ALPHA_OPAQUE : ALPHA_TRANSPARENT;
         mAlpha[HOURS].setValue(hoursAlpha);
-        mAlphaSelector[HOURS][SELECTOR_CIRCLE].setValue(hoursAlpha);
-        mAlphaSelector[HOURS][SELECTOR_DOT].setValue(hoursAlpha);
-        mAlphaSelector[HOURS][SELECTOR_LINE].setValue(hoursAlpha);
 
         final int minutesAlpha = mShowHours ? ALPHA_TRANSPARENT : ALPHA_OPAQUE;
         mAlpha[MINUTES].setValue(minutesAlpha);
-        mAlphaSelector[MINUTES][SELECTOR_CIRCLE].setValue(minutesAlpha);
-        mAlphaSelector[MINUTES][SELECTOR_DOT].setValue(minutesAlpha);
-        mAlphaSelector[MINUTES][SELECTOR_LINE].setValue(minutesAlpha);
     }
 
     @Override
@@ -675,7 +662,7 @@
                     mOuterTextHours, mOuterTextX[HOURS], mOuterTextY[HOURS], mPaint[HOURS],
                     hoursAlpha, !mIsOnInnerCircle, mSelectionDegrees[HOURS], false);
 
-            // Draw inner hours (12-23) for 24-hour time.
+            // Draw inner hours (13-00) for 24-hour time.
             if (mIs24HourMode && mInnerTextHours != null) {
                 drawTextElements(canvas, mTextSize[HOURS_INNER], mTypeface, mTextColor[HOURS_INNER],
                         mInnerTextHours, mInnerTextX, mInnerTextY, mPaint[HOURS], hoursAlpha,
@@ -714,69 +701,61 @@
         canvas.drawCircle(mXCenter, mYCenter, mCenterDotRadius, mPaintCenter);
     }
 
+    private int applyAlpha(int argb, int alpha) {
+        final int srcAlpha = (argb >> 24) & 0xFF;
+        final int dstAlpha = (int) (srcAlpha * (alpha / 255.0) + 0.5f);
+        return (0xFFFFFF & argb) | (dstAlpha << 24);
+    }
+
     private int getMultipliedAlpha(int argb, int alpha) {
         return (int) (Color.alpha(argb) * (alpha / 255.0) + 0.5);
     }
 
-    private final Path mSelectorPath = new Path();
-
     private void drawSelector(Canvas canvas, int index, Path selectorPath, float alphaMod) {
+        final int alpha = (int) (mAlpha[index % 2].getValue() * alphaMod + 0.5f);
+        final int color = applyAlpha(mSelectorColor, alpha);
+
         // Calculate the current radius at which to place the selection circle.
-        mLineLength[index] = mCircleRadius - mTextInset[index];
+        final int selRadius = mSelectorRadius;
+        final int selLength = mCircleRadius - mTextInset[index];
+        final double selAngleRad = Math.toRadians(mSelectionDegrees[index]);
+        final float selCenterX = mXCenter + selLength * (float) Math.sin(selAngleRad);
+        final float selCenterY = mYCenter - selLength * (float) Math.cos(selAngleRad);
 
-        final double selectionRadians = Math.toRadians(mSelectionDegrees[index]);
-
-        float pointX = mXCenter + (int) (mLineLength[index] * Math.sin(selectionRadians));
-        float pointY = mYCenter - (int) (mLineLength[index] * Math.cos(selectionRadians));
-
-        int color;
-        int alpha;
-        Paint paint;
-
-        // Draw the selection circle
-        color = mColorSelector[index % 2][SELECTOR_CIRCLE];
-        alpha = (int) (mAlphaSelector[index % 2][SELECTOR_CIRCLE].getValue() * alphaMod + 0.5f);
-        paint = mPaintSelector[index % 2][SELECTOR_CIRCLE];
+        // Draw the selection circle.
+        final Paint paint = mPaintSelector[index % 2][SELECTOR_CIRCLE];
         paint.setColor(color);
-        paint.setAlpha(getMultipliedAlpha(color, alpha));
-        canvas.drawCircle(pointX, pointY, mSelectorRadius, paint);
+        canvas.drawCircle(selCenterX, selCenterY, selRadius, paint);
 
         // If needed, set up the clip path for later.
         if (selectorPath != null) {
-            mSelectorPath.reset();
-            mSelectorPath.addCircle(pointX, pointY, mSelectorRadius, Path.Direction.CCW);
+            selectorPath.reset();
+            selectorPath.addCircle(selCenterX, selCenterY, selRadius, Path.Direction.CCW);
         }
 
-        // Draw the dot if needed.
+        // Draw the dot if we're between two items.
         final boolean shouldDrawDot = mSelectionDegrees[index] % 30 != 0;
         if (shouldDrawDot) {
-            // We're not on a direct tick
-            color = mColorSelector[index % 2][SELECTOR_DOT];
-            alpha = (int) (mAlphaSelector[index % 2][SELECTOR_DOT].getValue() * alphaMod + 0.5f);
-            paint = mPaintSelector[index % 2][SELECTOR_DOT];
-            paint.setColor(color);
-            paint.setAlpha(getMultipliedAlpha(color, alpha));
-            canvas.drawCircle(pointX, pointY, mSelectorDotRadius, paint);
+            final Paint dotPaint = mPaintSelector[index % 2][SELECTOR_DOT];
+            dotPaint.setColor(color);
+            canvas.drawCircle(selCenterX, selCenterY, mSelectorDotRadius, dotPaint);
         }
 
         // Shorten the line to only go from the edge of the center dot to the
         // edge of the selection circle.
-        final double sin = Math.sin(selectionRadians);
-        final double cos = Math.cos(selectionRadians);
-        final int lineLength = mLineLength[index] - mSelectorRadius;
+        final double sin = Math.sin(selAngleRad);
+        final double cos = Math.cos(selAngleRad);
+        final int lineLength = selLength - selRadius;
         final int centerX = mXCenter + (int) (mCenterDotRadius * sin);
         final int centerY = mYCenter - (int) (mCenterDotRadius * cos);
-        pointX = centerX + (int) (lineLength * sin);
-        pointY = centerY - (int) (lineLength * cos);
+        final float linePointX = centerX + (int) (lineLength * sin);
+        final float linePointY = centerY - (int) (lineLength * cos);
 
-        // Draw the line
-        color = mColorSelector[index % 2][SELECTOR_LINE];
-        alpha = (int) (mAlphaSelector[index % 2][SELECTOR_LINE].getValue() * alphaMod + 0.5f);
-        paint = mPaintSelector[index % 2][SELECTOR_LINE];
-        paint.setColor(color);
-        paint.setStrokeWidth(mSelectorStroke);
-        paint.setAlpha(getMultipliedAlpha(color, alpha));
-        canvas.drawLine(mXCenter, mYCenter, pointX, pointY, paint);
+        // Draw the line.
+        final Paint linePaint = mPaintSelector[index % 2][SELECTOR_LINE];
+        linePaint.setColor(color);
+        linePaint.setStrokeWidth(mSelectorStroke);
+        canvas.drawLine(mXCenter, mYCenter, linePointX, linePointY, linePaint);
     }
 
     private void calculatePositionsHours() {
@@ -890,21 +869,8 @@
         if (mHoursToMinutesAnims.size() == 0) {
             mHoursToMinutesAnims.add(getFadeOutAnimator(mAlpha[HOURS],
                     ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-            mHoursToMinutesAnims.add(getFadeOutAnimator(mAlphaSelector[HOURS][SELECTOR_CIRCLE],
-                    ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-            mHoursToMinutesAnims.add(getFadeOutAnimator(mAlphaSelector[HOURS][SELECTOR_DOT],
-                    ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-            mHoursToMinutesAnims.add(getFadeOutAnimator(mAlphaSelector[HOURS][SELECTOR_LINE],
-                    ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-
             mHoursToMinutesAnims.add(getFadeInAnimator(mAlpha[MINUTES],
                     ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
-            mHoursToMinutesAnims.add(getFadeInAnimator(mAlphaSelector[MINUTES][SELECTOR_CIRCLE],
-                    ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
-            mHoursToMinutesAnims.add(getFadeInAnimator(mAlphaSelector[MINUTES][SELECTOR_DOT],
-                    ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
-            mHoursToMinutesAnims.add(getFadeInAnimator(mAlphaSelector[MINUTES][SELECTOR_LINE],
-                    ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
         }
 
         if (mTransition != null && mTransition.isRunning()) {
@@ -919,21 +885,8 @@
         if (mMinuteToHoursAnims.size() == 0) {
             mMinuteToHoursAnims.add(getFadeOutAnimator(mAlpha[MINUTES],
                     ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-            mMinuteToHoursAnims.add(getFadeOutAnimator(mAlphaSelector[MINUTES][SELECTOR_CIRCLE],
-                    ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-            mMinuteToHoursAnims.add(getFadeOutAnimator(mAlphaSelector[MINUTES][SELECTOR_DOT],
-                    ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-            mMinuteToHoursAnims.add(getFadeOutAnimator(mAlphaSelector[MINUTES][SELECTOR_LINE],
-                    ALPHA_OPAQUE, ALPHA_TRANSPARENT, mInvalidateUpdateListener));
-
             mMinuteToHoursAnims.add(getFadeInAnimator(mAlpha[HOURS],
                     ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
-            mMinuteToHoursAnims.add(getFadeInAnimator(mAlphaSelector[HOURS][SELECTOR_CIRCLE],
-                    ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
-            mMinuteToHoursAnims.add(getFadeInAnimator(mAlphaSelector[HOURS][SELECTOR_DOT],
-                    ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
-            mMinuteToHoursAnims.add(getFadeInAnimator(mAlphaSelector[HOURS][SELECTOR_LINE],
-                    ALPHA_TRANSPARENT, ALPHA_OPAQUE, mInvalidateUpdateListener));
         }
 
         if (mTransition != null && mTransition.isRunning()) {
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 4e5a39a..3fb096c6 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -26,6 +26,7 @@
 import android.graphics.Paint.Style;
 import android.graphics.Rect;
 import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.text.TextPaint;
 import android.text.format.DateFormat;
@@ -59,6 +60,12 @@
     private static final String DEFAULT_TITLE_FORMAT = "MMMMy";
     private static final String DAY_OF_WEEK_FORMAT = "EEEEE";
 
+    /** Virtual view ID for previous button. */
+    private static final int ITEM_ID_PREV = 0x101;
+
+    /** Virtual view ID for next button. */
+    private static final int ITEM_ID_NEXT = 0x100;
+
     private final TextPaint mMonthPaint = new TextPaint();
     private final TextPaint mDayOfWeekPaint = new TextPaint();
     private final TextPaint mDayPaint = new TextPaint();
@@ -66,13 +73,27 @@
     private final Paint mDayHighlightPaint = new Paint();
 
     private final Calendar mCalendar = Calendar.getInstance();
-    private final Calendar mDayLabelCalendar = Calendar.getInstance();
+    private final Calendar mDayOfWeekLabelCalendar = Calendar.getInstance();
 
     private final MonthViewTouchHelper mTouchHelper;
 
     private final SimpleDateFormat mTitleFormatter;
     private final SimpleDateFormat mDayOfWeekFormatter;
 
+    private final int mMonthHeight;
+    private final int mDayOfWeekHeight;
+    private final int mDayHeight;
+    private final int mCellWidth;
+    private final int mDaySelectorRadius;
+
+    // Next/previous drawables.
+    private final Drawable mPrevDrawable;
+    private final Drawable mNextDrawable;
+    private final Rect mPrevHitArea;
+    private final Rect mNextHitArea;
+    private final CharSequence mPrevContentDesc;
+    private final CharSequence mNextContentDesc;
+
     private CharSequence mTitle;
 
     private int mMonth;
@@ -81,12 +102,6 @@
     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;
 
@@ -122,7 +137,10 @@
 
     private ColorStateList mDayTextColor;
 
-    private int mTouchedDay = -1;
+    private int mTouchedItem = -1;
+
+    private boolean mPrevEnabled;
+    private boolean mNextEnabled;
 
     public SimpleMonthView(Context context) {
         this(context, null);
@@ -146,6 +164,13 @@
         mCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width);
         mDaySelectorRadius = res.getDimensionPixelSize(R.dimen.date_picker_day_selector_radius);
 
+        mPrevDrawable = context.getDrawable(R.drawable.ic_chevron_left);
+        mNextDrawable = context.getDrawable(R.drawable.ic_chevron_right);
+        mPrevHitArea = mPrevDrawable != null ? new Rect() : null;
+        mNextHitArea = mNextDrawable != null ? new Rect() : null;
+        mPrevContentDesc = res.getText(R.string.date_picker_prev_month_button);
+        mNextContentDesc = res.getText(R.string.date_picker_next_month_button);
+
         // Set up accessibility components.
         mTouchHelper = new MonthViewTouchHelper(this);
         setAccessibilityDelegate(mTouchHelper);
@@ -160,6 +185,18 @@
         initPaints(res);
     }
 
+    public void setNextEnabled(boolean enabled) {
+        mNextEnabled = enabled;
+        mTouchHelper.invalidateRoot();
+        invalidate();
+    }
+
+    public void setPrevEnabled(boolean enabled) {
+        mPrevEnabled = enabled;
+        mTouchHelper.invalidateRoot();
+        invalidate();
+    }
+
     /**
      * Applies the specified text appearance resource to a paint, returning the
      * text color if one is set in the text appearance.
@@ -192,7 +229,16 @@
     }
 
     public void setMonthTextAppearance(int resId) {
-        applyTextAppearance(mMonthPaint, resId);
+        final ColorStateList monthColor = applyTextAppearance(mMonthPaint, resId);
+        if (monthColor != null) {
+            if (mPrevDrawable != null) {
+                mPrevDrawable.setTintList(monthColor);
+            }
+            if (mNextDrawable != null) {
+                mNextDrawable.setTintList(monthColor);
+            }
+        }
+
         invalidate();
     }
 
@@ -300,23 +346,26 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
+        final int x = (int) (event.getX() + 0.5f);
+        final int y = (int) (event.getY() + 0.5f);
+
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
             case MotionEvent.ACTION_MOVE:
-                final int touchedDay = getDayAtLocation(event.getX(), event.getY());
-                if (mTouchedDay != touchedDay) {
-                    mTouchedDay = touchedDay;
+                final int touchedItem = getItemAtLocation(x, y);
+                if (mTouchedItem != touchedItem) {
+                    mTouchedItem = touchedItem;
                     invalidate();
                 }
                 break;
 
             case MotionEvent.ACTION_UP:
-                final int clickedDay = getDayAtLocation(event.getX(), event.getY());
-                onDayClicked(clickedDay);
+                final int clickedItem = getItemAtLocation(x, y);
+                onItemClicked(clickedItem, true);
                 // Fall through.
             case MotionEvent.ACTION_CANCEL:
                 // Reset touched day on stream end.
-                mTouchedDay = -1;
+                mTouchedItem = -1;
                 invalidate();
                 break;
         }
@@ -332,6 +381,7 @@
         drawMonth(canvas);
         drawDaysOfWeek(canvas);
         drawDays(canvas);
+        drawButtons(canvas);
 
         canvas.translate(-paddingLeft, -paddingTop);
     }
@@ -347,34 +397,43 @@
     }
 
     private void drawDaysOfWeek(Canvas canvas) {
-        final float cellWidthHalf = mPaddedWidth / (DAYS_IN_WEEK * 2);
+        final TextPaint p = mDayOfWeekPaint;
+        final int headerHeight = mMonthHeight;
+        final int rowHeight = mDayOfWeekHeight;
+        final int colWidth = mPaddedWidth / DAYS_IN_WEEK;
 
-        // Vertically centered within the cell height.
-        final float lineHeight = mDayOfWeekPaint.ascent() + mDayOfWeekPaint.descent();
-        final float y = mMonthHeight + (mDayOfWeekHeight - lineHeight) / 2f;
+        // Text is vertically centered within the day of week height.
+        final float halfLineHeight = (p.ascent() + p.descent()) / 2f;
+        final int rowCenter = headerHeight + rowHeight / 2;
 
-        for (int 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);
+        for (int col = 0; col < DAYS_IN_WEEK; col++) {
+            final int colCenter = colWidth * col + colWidth / 2;
+            final int dayOfWeek = (col + mWeekStart) % DAYS_IN_WEEK;
+            final String label = getDayOfWeekLabel(dayOfWeek);
+            canvas.drawText(label, colCenter, rowCenter - halfLineHeight, p);
         }
     }
 
+    private String getDayOfWeekLabel(int dayOfWeek) {
+        mDayOfWeekLabelCalendar.set(Calendar.DAY_OF_WEEK, dayOfWeek);
+        return mDayOfWeekFormatter.format(mDayOfWeekLabelCalendar.getTime());
+    }
+
     /**
      * Draws the month days.
      */
     private void drawDays(Canvas canvas) {
-        final int cellWidthHalf = mPaddedWidth / (DAYS_IN_WEEK * 2);
+        final TextPaint p = mDayPaint;
+        final int headerHeight = mMonthHeight + mDayOfWeekHeight;
+        final int rowHeight = mDayHeight;
+        final int colWidth = mPaddedWidth / DAYS_IN_WEEK;
 
-        // Vertically centered within the cell height.
-        final float halfLineHeight = (mDayPaint.ascent() + mDayPaint.descent()) / 2;
-        float centerY = mMonthHeight + mDayOfWeekHeight + mDayHeight / 2f;
+        // Text is vertically centered within the row height.
+        final float halfLineHeight = (p.ascent() + p.descent()) / 2f;
+        int rowCenter = headerHeight + rowHeight / 2;
 
-        for (int day = 1, j = findDayOffset(); day <= mDaysInMonth; day++) {
-            final int x = (2 * j + 1) * cellWidthHalf;
+        for (int day = 1, col = findDayOffset(); day <= mDaysInMonth; day++) {
+            final int colCenter = colWidth * col + colWidth / 2;
             int stateMask = 0;
 
             if (day >= mEnabledDayStart && day <= mEnabledDayEnd) {
@@ -386,12 +445,12 @@
                 stateMask |= StateSet.VIEW_STATE_ACTIVATED;
 
                 // Adjust the circle to be centered on the row.
-                canvas.drawCircle(x, centerY, mDaySelectorRadius, mDaySelectorPaint);
-            } else if (mTouchedDay == day) {
+                canvas.drawCircle(colCenter, rowCenter, mDaySelectorRadius, mDaySelectorPaint);
+            } else if (mTouchedItem == day) {
                 stateMask |= StateSet.VIEW_STATE_PRESSED;
 
                 // Adjust the circle to be centered on the row.
-                canvas.drawCircle(x, centerY, mDaySelectorRadius, mDayHighlightPaint);
+                canvas.drawCircle(colCenter, rowCenter, mDaySelectorRadius, mDayHighlightPaint);
             }
 
             final boolean isDayToday = mToday == day;
@@ -402,19 +461,29 @@
                 final int[] stateSet = StateSet.get(stateMask);
                 dayTextColor = mDayTextColor.getColorForState(stateSet, 0);
             }
-            mDayPaint.setColor(dayTextColor);
+            p.setColor(dayTextColor);
 
-            canvas.drawText("" + day, x, centerY - halfLineHeight, mDayPaint);
+            canvas.drawText(Integer.toString(day), colCenter, rowCenter - halfLineHeight, p);
 
-            j++;
+            col++;
 
-            if (j == DAYS_IN_WEEK) {
-                j = 0;
-                centerY += mDayHeight;
+            if (col == DAYS_IN_WEEK) {
+                col = 0;
+                rowCenter += rowHeight;
             }
         }
     }
 
+    private void drawButtons(Canvas canvas) {
+        if (mPrevEnabled && mPrevDrawable != null) {
+            mPrevDrawable.draw(canvas);
+        }
+
+        if (mNextEnabled && mNextDrawable != null) {
+            mNextDrawable.draw(canvas);
+        }
+    }
+
     private static boolean isValidDayOfWeek(int day) {
         return day >= Calendar.SUNDAY && day <= Calendar.SATURDAY;
     }
@@ -569,8 +638,42 @@
 
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        mPaddedWidth = w - getPaddingLeft() - getPaddingRight();
-        mPaddedHeight = w - getPaddingTop() - getPaddingBottom();
+        final int paddedLeft = getPaddingLeft();
+        final int paddedTop = getPaddingTop();
+        final int paddedRight = w - getPaddingRight();
+        final int paddedBottom = h - getPaddingBottom();
+        mPaddedWidth = paddedRight - paddedLeft;
+        mPaddedHeight = paddedBottom - paddedTop;
+
+        final int monthHeight = mMonthHeight;
+        final int cellWidth = mPaddedWidth / DAYS_IN_WEEK;
+
+        // Vertically center the previous/next drawables within the month
+        // header, horizontally center within the day cell, then expand the
+        // hit area to ensure it's at least 48x48dp.
+        final Drawable prevDrawable = mPrevDrawable;
+        if (prevDrawable != null) {
+            final int dW = prevDrawable.getIntrinsicWidth();
+            final int dH = prevDrawable.getIntrinsicHeight();
+            final int iconTop = (monthHeight - dH) / 2;
+            final int iconLeft = (cellWidth - dW) / 2;
+
+            // Button bounds don't include padding, but hit area does.
+            prevDrawable.setBounds(iconLeft, iconTop, iconLeft + dW, iconTop + dH);
+            mPrevHitArea.set(0, 0, paddedLeft + cellWidth, paddedTop + monthHeight);
+        }
+
+        final Drawable nextDrawable = mNextDrawable;
+        if (nextDrawable != null) {
+            final int dW = nextDrawable.getIntrinsicWidth();
+            final int dH = nextDrawable.getIntrinsicHeight();
+            final int iconTop = (monthHeight - dH) / 2;
+            final int iconRight = mPaddedWidth - (cellWidth - dW) / 2;
+
+            // Button bounds don't include padding, but hit area does.
+            nextDrawable.setBounds(iconRight - dW, iconTop, iconRight, iconTop + dH);
+            mNextHitArea.set(paddedRight - cellWidth, 0, w, paddedTop + monthHeight);
+        }
 
         // Invalidate cached accessibility information.
         mTouchHelper.invalidateRoot();
@@ -585,22 +688,29 @@
     }
 
     /**
-     * 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.
+     * Calculates the day of the month or item identifier at the specified
+     * touch position. Returns the day of the month or -1 if the position
+     * wasn't in a valid day.
      *
      * @param x the x position of the touch event
      * @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
+     * @return the day of the month at (x, y), an item identifier, or -1 if the
+     *         position wasn't in a valid day or item
      */
-    private int getDayAtLocation(float x, float y) {
-        final int paddedX = (int) (x - getPaddingLeft() + 0.5f);
+    private int getItemAtLocation(int x, int y) {
+        if (mNextEnabled && mNextDrawable != null && mNextHitArea.contains(x, y)) {
+            return ITEM_ID_NEXT;
+        } else if (mPrevEnabled && mPrevDrawable != null && mPrevHitArea.contains(x, y)) {
+            return ITEM_ID_PREV;
+        }
+
+        final int paddedX = x - getPaddingLeft();
         if (paddedX < 0 || paddedX >= mPaddedWidth) {
             return -1;
         }
 
         final int headerHeight = mMonthHeight + mDayOfWeekHeight;
-        final int paddedY = (int) (y - getPaddingTop() + 0.5f);
+        final int paddedY = y - getPaddingTop();
         if (paddedY < headerHeight || paddedY >= mPaddedHeight) {
             return -1;
         }
@@ -619,47 +729,97 @@
     /**
      * Calculates the bounds of the specified day.
      *
-     * @param day the day of the month
+     * @param id the day of the month, or an item identifier
      * @param outBounds the rect to populate with bounds
      */
-    private boolean getBoundsForDay(int day, Rect outBounds) {
-        if (day < 1 || day > mDaysInMonth) {
+    private boolean getBoundsForItem(int id, Rect outBounds) {
+        if (mNextEnabled && id == ITEM_ID_NEXT) {
+            if (mNextDrawable != null) {
+                outBounds.set(mNextHitArea);
+                return true;
+            }
+        } else if (mPrevEnabled && id == ITEM_ID_PREV) {
+            if (mPrevDrawable != null) {
+                outBounds.set(mPrevHitArea);
+                return true;
+            }
+        }
+
+        if (id < 1 || id > mDaysInMonth) {
             return false;
         }
 
-        final int index = day - 1 + findDayOffset();
-        final int row = index / DAYS_IN_WEEK;
+        final int index = id - 1 + findDayOffset();
+
+        // Compute left edge.
         final int col = index % DAYS_IN_WEEK;
+        final int colWidth = mPaddedWidth / DAYS_IN_WEEK;
+        final int left = getPaddingLeft() + col * colWidth;
 
+        // Compute top edge.
+        final int row = index / DAYS_IN_WEEK;
+        final int rowHeight = mDayHeight;
         final int headerHeight = mMonthHeight + mDayOfWeekHeight;
-        final int paddedY = row * mDayHeight + headerHeight;
-        final int paddedX = col * mPaddedWidth;
+        final int top = getPaddingTop() + headerHeight + row * rowHeight;
 
-        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));
-
+        outBounds.set(left, top, left + colWidth, top + rowHeight);
         return true;
     }
 
     /**
+     * Called when an item is clicked.
+     *
+     * @param id the day number or item identifier
+     */
+    private boolean onItemClicked(int id, boolean animate) {
+        return onNavigationClicked(id, animate) || onDayClicked(id);
+    }
+
+    /**
      * Called when the user clicks on a day. Handles callbacks to the
      * {@link OnDayClickListener} if one is set.
      *
      * @param day the day that was clicked
      */
-    private void onDayClicked(int day) {
+    private boolean onDayClicked(int day) {
+        if (day < 0 || day > mDaysInMonth) {
+            return false;
+        }
+
         if (mOnDayClickListener != null) {
-            Calendar date = Calendar.getInstance();
+            final Calendar date = Calendar.getInstance();
             date.set(mYear, mMonth, day);
             mOnDayClickListener.onDayClick(this, date);
         }
 
         // This is a no-op if accessibility is turned off.
         mTouchHelper.sendEventForVirtualView(day, AccessibilityEvent.TYPE_VIEW_CLICKED);
+        return true;
+    }
+
+    /**
+     * Called when the user clicks on a navigation button. Handles callbacks to
+     * the {@link OnDayClickListener} if one is set.
+     *
+     * @param id the item identifier
+     */
+    private boolean onNavigationClicked(int id, boolean animate) {
+        final int direction;
+        if (id == ITEM_ID_NEXT) {
+            direction = 1;
+        } else if (id == ITEM_ID_PREV) {
+            direction = -1;
+        } else {
+            return false;
+        }
+
+        if (mOnDayClickListener != null) {
+            mOnDayClickListener.onNavigationClick(this, direction, animate);
+        }
+
+        // This is a no-op if accessibility is turned off.
+        mTouchHelper.sendEventForVirtualView(id, AccessibilityEvent.TYPE_VIEW_CLICKED);
+        return true;
     }
 
     /**
@@ -678,7 +838,7 @@
 
         @Override
         protected int getVirtualViewAt(float x, float y) {
-            final int day = getDayAtLocation(x, y);
+            final int day = getItemAtLocation((int) (x + 0.5f), (int) (y + 0.5f));
             if (day >= 0) {
                 return day;
             }
@@ -687,6 +847,14 @@
 
         @Override
         protected void getVisibleVirtualViews(IntArray virtualViewIds) {
+            if (mNextEnabled && mNextDrawable != null) {
+                virtualViewIds.add(ITEM_ID_PREV);
+            }
+
+            if (mPrevEnabled && mPrevDrawable != null) {
+                virtualViewIds.add(ITEM_ID_NEXT);
+            }
+
             for (int day = 1; day <= mDaysInMonth; day++) {
                 virtualViewIds.add(day);
             }
@@ -699,7 +867,7 @@
 
         @Override
         protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) {
-            final boolean hasBounds = getBoundsForDay(virtualViewId, mTempRect);
+            final boolean hasBounds = getBoundsForItem(virtualViewId, mTempRect);
 
             if (!hasBounds) {
                 // The day is invalid, kill the node.
@@ -710,12 +878,14 @@
                 return;
             }
 
+            node.setText(getItemText(virtualViewId));
             node.setContentDescription(getItemDescription(virtualViewId));
             node.setBoundsInParent(mTempRect);
             node.addAction(AccessibilityAction.ACTION_CLICK);
 
             if (virtualViewId == mActivatedDay) {
-                node.setSelected(true);
+                // TODO: This should use activated once that's supported.
+                node.setChecked(true);
             }
 
         }
@@ -725,31 +895,45 @@
                 Bundle arguments) {
             switch (action) {
                 case AccessibilityNodeInfo.ACTION_CLICK:
-                    onDayClicked(virtualViewId);
-                    return true;
+                    return onItemClicked(virtualViewId, false);
             }
 
             return false;
         }
 
         /**
-         * Generates a description for a given time object. Since this
-         * description will be spoken, the components are ordered by descending
-         * specificity as DAY MONTH YEAR.
+         * Generates a description for a given virtual view.
          *
-         * @param day The day to generate a description for
-         * @return A description of the time object
+         * @param id the day or item identifier to generate a description for
+         * @return a description of the virtual view
          */
-        private CharSequence getItemDescription(int day) {
-            mTempCalendar.set(mYear, mMonth, day);
-            final CharSequence date = DateFormat.format(DATE_FORMAT,
-                    mTempCalendar.getTimeInMillis());
-
-            if (day == mActivatedDay) {
-                return getContext().getString(R.string.item_is_selected, date);
+        private CharSequence getItemDescription(int id) {
+            if (id == ITEM_ID_NEXT) {
+                return mNextContentDesc;
+            } else if (id == ITEM_ID_PREV) {
+                return mPrevContentDesc;
+            } else if (id >= 1 && id <= mDaysInMonth) {
+                mTempCalendar.set(mYear, mMonth, id);
+                return DateFormat.format(DATE_FORMAT, mTempCalendar.getTimeInMillis());
             }
 
-            return date;
+            return "";
+        }
+
+        /**
+         * Generates displayed text for a given virtual view.
+         *
+         * @param id the day or item identifier to generate text for
+         * @return the visible text of the virtual view
+         */
+        private CharSequence getItemText(int id) {
+            if (id == ITEM_ID_NEXT || id == ITEM_ID_PREV) {
+                return null;
+            } else if (id >= 1 && id <= mDaysInMonth) {
+                return Integer.toString(id);
+            }
+
+            return null;
         }
     }
 
@@ -758,5 +942,6 @@
      */
     public interface OnDayClickListener {
         public void onDayClick(SimpleMonthView view, Calendar day);
+        public void onNavigationClick(SimpleMonthView view, int direction, boolean animate);
     }
 }
diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java
index 944b491..986c0f8 100644
--- a/core/java/android/widget/TimePicker.java
+++ b/core/java/android/widget/TimePicker.java
@@ -16,6 +16,7 @@
 
 package android.widget;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.Widget;
 import android.content.Context;
@@ -29,18 +30,13 @@
 import java.util.Locale;
 
 /**
- * A view for selecting the time of day, in either 24 hour or AM/PM mode. The
- * hour, each minute digit, and AM/PM (if applicable) can be conrolled by
- * vertical spinners. The hour can be entered by keyboard input. Entering in two
- * digit hours can be accomplished by hitting two digits within a timeout of
- * about a second (e.g. '1' then '2' to select 12). The minutes can be entered
- * by entering single digits. Under AM/PM mode, the user can hit 'a', 'A", 'p'
- * or 'P' to pick. For a dialog using this view, see
- * {@link android.app.TimePickerDialog}.
+ * A widget for selecting the time of day, in either 24-hour or AM/PM mode.
  * <p>
- * See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
- * guide.
- * </p>
+ * For a dialog using this view, see {@link android.app.TimePickerDialog}. See
+ * the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
+ * guide for more information.
+ *
+ * @attr ref android.R.styleable#TimePicker_timePickerMode
  */
 @Widget
 public class TimePicker extends FrameLayout {
@@ -96,44 +92,105 @@
     }
 
     /**
-     * Set the current hour.
+     * Sets the currently selected hour using 24-hour time.
+     *
+     * @param hour the hour to set, in the range (0-23)
+     * @see #getHour()
      */
-    public void setCurrentHour(Integer currentHour) {
-        mDelegate.setCurrentHour(currentHour);
+    public void setHour(int hour) {
+        mDelegate.setCurrentHour(hour);
     }
 
     /**
-     * @return The current hour in the range (0-23).
+     * Returns the currently selected hour using 24-hour time.
+     *
+     * @return the currently selected hour, in the range (0-23)
+     * @see #setHour(int)
      */
+    public int getHour() {
+        return mDelegate.getCurrentHour();
+    }
+
+    /**
+     * Sets the currently selected minute..
+     *
+     * @param minute the minute to set, in the range (0-59)
+     * @see #getMinute()
+     */
+    public void setMinute(int minute) {
+        mDelegate.setCurrentMinute(minute);
+    }
+
+    /**
+     * Returns the currently selected minute.
+     *
+     * @return the currently selected minute, in the range (0-59)
+     * @see #setMinute(int)
+     */
+    public int getMinute() {
+        return mDelegate.getCurrentMinute();
+    }
+
+    /**
+     * Sets the current hour.
+     *
+     * @deprecated Use {@link #setHour(int)}
+     */
+    @Deprecated
+    public void setCurrentHour(@NonNull Integer currentHour) {
+        setHour(currentHour);
+    }
+
+    /**
+     * @return the current hour in the range (0-23)
+     * @deprecated Use {@link #getHour()}
+     */
+    @NonNull
+    @Deprecated
     public Integer getCurrentHour() {
         return mDelegate.getCurrentHour();
     }
 
     /**
      * Set the current minute (0-59).
+     *
+     * @deprecated Use {@link #setMinute(int)}
      */
-    public void setCurrentMinute(Integer currentMinute) {
+    @Deprecated
+    public void setCurrentMinute(@NonNull Integer currentMinute) {
         mDelegate.setCurrentMinute(currentMinute);
     }
 
     /**
-     * @return The current minute.
+     * @return the current minute
+     * @deprecated Use {@link #getMinute()}
      */
+    @NonNull
+    @Deprecated
     public Integer getCurrentMinute() {
         return mDelegate.getCurrentMinute();
     }
 
     /**
-     * Set whether in 24 hour or AM/PM mode.
+     * Sets whether this widget displays time in 24-hour mode or 12-hour mode
+     * with an AM/PM picker.
      *
-     * @param is24HourView True = 24 hour mode. False = AM/PM.
+     * @param is24HourView {@code true} to display in 24-hour mode,
+     *                     {@code false} for 12-hour mode with AM/PM
+     * @see #is24HourView()
      */
-    public void setIs24HourView(Boolean is24HourView) {
+    public void setIs24HourView(@NonNull Boolean is24HourView) {
+        if (is24HourView == null) {
+            return;
+        }
+
         mDelegate.setIs24HourView(is24HourView);
     }
 
     /**
-     * @return true if this is in 24 hour view else false.
+     * @return {@code true} if this widget displays time in 24-hour mode,
+     *         {@code false} otherwise}
+     * @see #setIs24HourView(Boolean)
      */
     public boolean is24HourView() {
         return mDelegate.is24HourView();
@@ -210,13 +267,13 @@
      * for the real behavior.
      */
     interface TimePickerDelegate {
-        void setCurrentHour(Integer currentHour);
-        Integer getCurrentHour();
+        void setCurrentHour(int currentHour);
+        int getCurrentHour();
 
-        void setCurrentMinute(Integer currentMinute);
-        Integer getCurrentMinute();
+        void setCurrentMinute(int currentMinute);
+        int getCurrentMinute();
 
-        void setIs24HourView(Boolean is24HourView);
+        void setIs24HourView(boolean is24HourView);
         boolean is24HourView();
 
         void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener);
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index 9fdd718..c58d5cb 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -16,16 +16,20 @@
 
 package android.widget;
 
+import android.annotation.Nullable;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.format.DateFormat;
 import android.text.format.DateUtils;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.StateSet;
 import android.util.TypedValue;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyCharacterMap;
@@ -48,7 +52,6 @@
  */
 class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate implements
         RadialTimePickerView.OnValueSelectedListener {
-
     private static final String TAG = "TimePickerClockDelegate";
 
     // Index used by RadialPickerLayout
@@ -61,14 +64,16 @@
     // Also NOT a real index, just used for keyboard mode.
     private static final int ENABLE_PICKER_INDEX = 3;
 
+    private static final int[] ATTRS_TEXT_COLOR = new int[] {
+            com.android.internal.R.attr.textColor};
+    private static final int[] ATTRS_DISABLED_ALPHA = new int[] {
+            com.android.internal.R.attr.disabledAlpha};
+
     // LayoutLib relies on these constants. Change TimePickerClockDelegate_Delegate if
     // modifying these.
     static final int AM = 0;
     static final int PM = 1;
 
-    private static final boolean DEFAULT_ENABLED_STATE = true;
-    private boolean mIsEnabled = DEFAULT_ENABLED_STATE;
-
     private static final int HOURS_IN_HALF_DAY = 12;
 
     private final View mHeaderView;
@@ -83,8 +88,7 @@
     private final String mAmText;
     private final String mPmText;
 
-    private final float mDisabledAlpha;
-
+    private boolean mIsEnabled = true;
     private boolean mAllowAutoAdvance;
     private int mInitialHourOfDay;
     private int mInitialMinute;
@@ -134,7 +138,6 @@
         final View mainView = inflater.inflate(layoutResourceId, delegator);
 
         mHeaderView = mainView.findViewById(R.id.time_header);
-        mHeaderView.setBackground(a.getDrawable(R.styleable.TimePicker_headerBackground));
 
         // Set up hour/minute labels.
         mHourView = (TextView) mainView.findViewById(R.id.hours);
@@ -147,14 +150,6 @@
         mMinuteView.setAccessibilityDelegate(
                 new ClickActionDelegate(context, R.string.select_minutes));
 
-        final int headerTimeTextAppearance = a.getResourceId(
-                R.styleable.TimePicker_headerTimeTextAppearance, 0);
-        if (headerTimeTextAppearance != 0) {
-            mHourView.setTextAppearance(context, headerTimeTextAppearance);
-            mSeparatorView.setTextAppearance(context, headerTimeTextAppearance);
-            mMinuteView.setTextAppearance(context, headerTimeTextAppearance);
-        }
-
         // Now that we have text appearances out of the way, make sure the hour
         // and minute views are correctly sized.
         mHourView.setMinWidth(computeStableWidth(mHourView, 24));
@@ -169,20 +164,41 @@
         mPmLabel.setText(amPmStrings[1]);
         mPmLabel.setOnClickListener(mClickListener);
 
-        final int headerAmPmTextAppearance = a.getResourceId(
-                R.styleable.TimePicker_headerAmPmTextAppearance, 0);
-        if (headerAmPmTextAppearance != 0) {
-            mAmLabel.setTextAppearance(context, headerAmPmTextAppearance);
-            mPmLabel.setTextAppearance(context, headerAmPmTextAppearance);
+        // For the sake of backwards compatibility, attempt to extract the text
+        // color from the header time text appearance. If it's set, we'll let
+        // that override the "real" header text color.
+        ColorStateList headerTextColor = null;
+
+        @SuppressWarnings("deprecation")
+        final int timeHeaderTextAppearance = a.getResourceId(
+                R.styleable.TimePicker_headerTimeTextAppearance, 0);
+        if (timeHeaderTextAppearance != 0) {
+            final TypedArray textAppearance = mContext.obtainStyledAttributes(null,
+                    ATTRS_TEXT_COLOR, 0, timeHeaderTextAppearance);
+            final ColorStateList legacyHeaderTextColor = textAppearance.getColorStateList(0);
+            headerTextColor = applyLegacyColorFixes(legacyHeaderTextColor);
+            textAppearance.recycle();
+        }
+
+        if (headerTextColor == null) {
+            headerTextColor = a.getColorStateList(R.styleable.TimePicker_headerTextColor);
+        }
+
+        if (headerTextColor != null) {
+            mHourView.setTextColor(headerTextColor);
+            mSeparatorView.setTextColor(headerTextColor);
+            mMinuteView.setTextColor(headerTextColor);
+            mAmLabel.setTextColor(headerTextColor);
+            mPmLabel.setTextColor(headerTextColor);
+        }
+
+        // Set up header background, if available.
+        if (a.hasValueOrEmpty(R.styleable.TimePicker_headerBackground)) {
+            mHeaderView.setBackground(a.getDrawable(R.styleable.TimePicker_headerBackground));
         }
 
         a.recycle();
 
-        // Pull disabled alpha from theme.
-        final TypedValue outValue = new TypedValue();
-        context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true);
-        mDisabledAlpha = outValue.getFloat();
-
         mRadialTimePickerView = (RadialTimePickerView) mainView.findViewById(
                 R.id.radial_picker);
 
@@ -204,6 +220,54 @@
         initialize(currentHour, currentMinute, false /* 12h */, HOUR_INDEX);
     }
 
+    /**
+     * The legacy text color might have been poorly defined. Ensures that it
+     * has an appropriate activated state, using the selected state if one
+     * exists or modifying the default text color otherwise.
+     *
+     * @param color a legacy text color, or {@code null}
+     * @return a color state list with an appropriate activated state, or
+     *         {@code null} if a valid activated state could not be generated
+     */
+    @Nullable
+    private ColorStateList applyLegacyColorFixes(@Nullable ColorStateList color) {
+        if (color == null || color.hasState(R.attr.state_activated)) {
+            return color;
+        }
+
+        final int activatedColor;
+        final int defaultColor;
+        if (color.hasState(R.attr.state_selected)) {
+            activatedColor = color.getColorForState(StateSet.get(
+                    StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_SELECTED), 0);
+            defaultColor = color.getColorForState(StateSet.get(
+                    StateSet.VIEW_STATE_ENABLED), 0);
+        } else {
+            activatedColor = color.getDefaultColor();
+
+            // Generate a non-activated color using the disabled alpha.
+            final TypedArray ta = mContext.obtainStyledAttributes(ATTRS_DISABLED_ALPHA);
+            final float disabledAlpha = ta.getFloat(0, 0.30f);
+            defaultColor = multiplyAlphaComponent(activatedColor, disabledAlpha);
+        }
+
+        if (activatedColor == 0 || defaultColor == 0) {
+            // We somehow failed to obtain the colors.
+            return null;
+        }
+
+        final int[][] stateSet = new int[][] {{ R.attr.state_activated }, {}};
+        final int[] colors = new int[] { activatedColor, defaultColor };
+        return new ColorStateList(stateSet, colors);
+    }
+
+    private int multiplyAlphaComponent(int color, float alphaMod) {
+        final int srcRgb = color & 0xFFFFFF;
+        final int srcAlpha = (color >> 24) & 0xFF;
+        final int dstAlpha = (int) (srcAlpha * alphaMod + 0.5f);
+        return srcRgb | (dstAlpha << 24);
+    }
+
     private static class ClickActionDelegate extends AccessibilityDelegate {
         private final AccessibilityAction mClickAction;
 
@@ -312,7 +376,7 @@
      * Set the current hour.
      */
     @Override
-    public void setCurrentHour(Integer currentHour) {
+    public void setCurrentHour(int currentHour) {
         if (mInitialHourOfDay == currentHour) {
             return;
         }
@@ -329,7 +393,7 @@
      * @return The current hour in the range (0-23).
      */
     @Override
-    public Integer getCurrentHour() {
+    public int getCurrentHour() {
         int currentHour = mRadialTimePickerView.getCurrentHour();
         if (mIs24HourView) {
             return currentHour;
@@ -348,7 +412,7 @@
      * Set the current minute (0-59).
      */
     @Override
-    public void setCurrentMinute(Integer currentMinute) {
+    public void setCurrentMinute(int currentMinute) {
         if (mInitialMinute == currentMinute) {
             return;
         }
@@ -363,7 +427,7 @@
      * @return The current minute.
      */
     @Override
-    public Integer getCurrentMinute() {
+    public int getCurrentMinute() {
         return mRadialTimePickerView.getCurrentMinute();
     }
 
@@ -373,7 +437,7 @@
      * @param is24HourView True = 24 hour mode. False = AM/PM.
      */
     @Override
-    public void setIs24HourView(Boolean is24HourView) {
+    public void setIs24HourView(boolean is24HourView) {
         if (is24HourView == mIs24HourView) {
             return;
         }
@@ -604,12 +668,12 @@
 
     private void updateAmPmLabelStates(int amOrPm) {
         final boolean isAm = amOrPm == AM;
+        mAmLabel.setActivated(isAm);
         mAmLabel.setChecked(isAm);
-        mAmLabel.setSelected(isAm);
 
         final boolean isPm = amOrPm == PM;
+        mPmLabel.setActivated(isPm);
         mPmLabel.setChecked(isPm);
-        mPmLabel.setSelected(isPm);
     }
 
     /**
@@ -769,8 +833,8 @@
             }
         }
 
-        mHourView.setSelected(index == HOUR_INDEX);
-        mMinuteView.setSelected(index == MINUTE_INDEX);
+        mHourView.setActivated(index == HOUR_INDEX);
+        mMinuteView.setActivated(index == MINUTE_INDEX);
     }
 
     private void setAmOrPm(int amOrPm) {
@@ -960,9 +1024,9 @@
             String minuteStr = (values[1] == -1) ? mDoublePlaceholderText :
                     String.format(minuteFormat, values[1]).replace(' ', mPlaceholderText);
             mHourView.setText(hourStr);
-            mHourView.setSelected(false);
+            mHourView.setActivated(false);
             mMinuteView.setText(minuteStr);
-            mMinuteView.setSelected(false);
+            mMinuteView.setActivated(false);
             if (!mIs24HourView) {
                 updateAmPmLabelStates(values[2]);
             }
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index 513c55b..df6b0a9 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -279,13 +279,13 @@
     }
 
     @Override
-    public void setCurrentHour(Integer currentHour) {
+    public void setCurrentHour(int currentHour) {
         setCurrentHour(currentHour, true);
     }
 
-    private void setCurrentHour(Integer currentHour, boolean notifyTimeChanged) {
+    private void setCurrentHour(int currentHour, boolean notifyTimeChanged) {
         // why was Integer used in the first place?
-        if (currentHour == null || currentHour == getCurrentHour()) {
+        if (currentHour == getCurrentHour()) {
             return;
         }
         if (!is24HourView()) {
@@ -310,7 +310,7 @@
     }
 
     @Override
-    public Integer getCurrentHour() {
+    public int getCurrentHour() {
         int currentHour = mHourSpinner.getValue();
         if (is24HourView()) {
             return currentHour;
@@ -322,7 +322,7 @@
     }
 
     @Override
-    public void setCurrentMinute(Integer currentMinute) {
+    public void setCurrentMinute(int currentMinute) {
         if (currentMinute == getCurrentMinute()) {
             return;
         }
@@ -331,12 +331,12 @@
     }
 
     @Override
-    public Integer getCurrentMinute() {
+    public int getCurrentMinute() {
         return mMinuteSpinner.getValue();
     }
 
     @Override
-    public void setIs24HourView(Boolean is24HourView) {
+    public void setIs24HourView(boolean is24HourView) {
         if (mIs24HourView == is24HourView) {
             return;
         }
diff --git a/core/java/android/widget/YearPickerView.java b/core/java/android/widget/YearPickerView.java
index 7bd502e..7182414 100644
--- a/core/java/android/widget/YearPickerView.java
+++ b/core/java/android/widget/YearPickerView.java
@@ -111,16 +111,12 @@
         mAdapter.setRange(min, max);
     }
 
-    public void setYearTextAppearance(int resId) {
-        mAdapter.setItemTextAppearance(resId);
-    }
-
-    public void setYearActivatedTextAppearance(int resId) {
-        mAdapter.setItemActivatedTextAppearance(resId);
-    }
-
     private static class YearAdapter extends BaseAdapter {
         private static final int ITEM_LAYOUT = R.layout.year_label_text_view;
+        private static final int ITEM_TEXT_APPEARANCE =
+                R.style.TextAppearance_Material_DatePicker_List_YearLabel;
+        private static final int ITEM_TEXT_ACTIVATED_APPEARANCE =
+                R.style.TextAppearance_Material_DatePicker_List_YearLabel_Activated;
 
         private final LayoutInflater mInflater;
 
@@ -128,9 +124,6 @@
         private int mMinYear;
         private int mCount;
 
-        private int mItemTextAppearanceResId;
-        private int mItemActivatedTextAppearanceResId;
-
         public YearAdapter(Context context) {
             mInflater = LayoutInflater.from(context);
         }
@@ -155,16 +148,6 @@
             return false;
         }
 
-        public void setItemTextAppearance(int resId) {
-            mItemTextAppearanceResId = resId;
-            notifyDataSetChanged();
-        }
-
-        public void setItemActivatedTextAppearance(int resId) {
-            mItemActivatedTextAppearanceResId = resId;
-            notifyDataSetChanged();
-        }
-
         @Override
         public int getCount() {
             return mCount;
@@ -203,10 +186,10 @@
             final boolean activated = mActivatedYear == year;
 
             final int textAppearanceResId;
-            if (activated && mItemActivatedTextAppearanceResId != 0) {
-                textAppearanceResId = mItemActivatedTextAppearanceResId;
+            if (activated && ITEM_TEXT_ACTIVATED_APPEARANCE != 0) {
+                textAppearanceResId = ITEM_TEXT_ACTIVATED_APPEARANCE;
             } else {
-                textAppearanceResId = mItemTextAppearanceResId;
+                textAppearanceResId = ITEM_TEXT_APPEARANCE;
             }
 
             final TextView v = (TextView) convertView;
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 3ceea9d..6b35f3f 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -604,9 +604,10 @@
         if ((mAlwaysUseOption || mAdapter.hasFilteredItem()) && mAdapter.mOrigResolveList != null) {
             // Build a reasonable intent filter, based on what matched.
             IntentFilter filter = new IntentFilter();
+            String action = intent.getAction();
 
-            if (intent.getAction() != null) {
-                filter.addAction(intent.getAction());
+            if (action != null) {
+                filter.addAction(action);
             }
             Set<String> categories = intent.getCategories();
             if (categories != null) {
@@ -688,8 +689,30 @@
                     if (r.match > bestMatch) bestMatch = r.match;
                 }
                 if (alwaysCheck) {
-                    getPackageManager().addPreferredActivity(filter, bestMatch, set,
-                            intent.getComponent());
+                    PackageManager pm = getPackageManager();
+
+                    // Set the preferred Activity
+                    pm.addPreferredActivity(filter, bestMatch, set, intent.getComponent());
+
+                    // Update Domain Verification status
+                    int userId = getUserId();
+                    ComponentName cn = intent.getComponent();
+                    String packageName = cn.getPackageName();
+                    String dataScheme = (data != null) ? data.getScheme() : null;
+
+                    boolean isHttpOrHttps = (dataScheme != null) &&
+                            (dataScheme.equals(IntentFilter.SCHEME_HTTP) ||
+                            dataScheme.equals(IntentFilter.SCHEME_HTTPS));
+
+                    boolean isViewAction = (action != null) && action.equals(Intent.ACTION_VIEW);
+                    boolean hasCategoryBrowsable = (categories != null) &&
+                            categories.contains(Intent.CATEGORY_BROWSABLE);
+
+                    if (isHttpOrHttps && isViewAction && hasCategoryBrowsable) {
+                        pm.updateIntentVerificationStatus(packageName,
+                                PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
+                                userId);
+                    }
                 } else {
                     try {
                         AppGlobals.getPackageManager().setLastChosenActivity(intent,
diff --git a/core/java/com/android/internal/logging/EventLogTags.logtags b/core/java/com/android/internal/logging/EventLogTags.logtags
index 9e178df..870d20d 100644
--- a/core/java/com/android/internal/logging/EventLogTags.logtags
+++ b/core/java/com/android/internal/logging/EventLogTags.logtags
@@ -4,4 +4,4 @@
 
 # interaction logs
 524287 sysui_view_visibility (category|1|5),(visible|1|6)
-524288 sysui_action (category|1|5),(type|1|6)
+524288 sysui_action (category|1|5)
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index 2de7394..9b45e34 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -31,6 +31,15 @@
     public static final int MANAGE_APPLICATIONS_ALL = 134;
     public static final int MANAGE_APPLICATIONS_NOTIFICATIONS = 135;
 
+    public static final int ACTION_WIFI_ADD_NETWORK = 136;
+    public static final int ACTION_WIFI_CONNECT = 137;
+    public static final int ACTION_WIFI_FORCE_SCAN = 138;
+    public static final int ACTION_WIFI_FORGET = 139;
+    public static final int ACTION_WIFI_OFF = 140;
+    public static final int ACTION_WIFI_ON = 141;
+
+    public static final int MANAGE_PERMISSIONS = 142;
+
     public static void visible(Context context, int category) throws IllegalArgumentException {
         if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
             throw new IllegalArgumentException("Must define metric category");
@@ -44,4 +53,11 @@
         }
         EventLogTags.writeSysuiViewVisibility(category, 0);
     }
+
+    public static void action(Context context, int category) {
+        if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
+            throw new IllegalArgumentException("Must define metric category");
+        }
+        EventLogTags.writeSysuiAction(category);
+    }
 }
diff --git a/core/java/com/android/internal/midi/EventScheduler.java b/core/java/com/android/internal/midi/EventScheduler.java
new file mode 100644
index 0000000..7526609
--- /dev/null
+++ b/core/java/com/android/internal/midi/EventScheduler.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.midi;
+
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * Store arbitrary timestamped events using a Long timestamp.
+ * Only one Thread can write into the buffer.
+ * And only one Thread can read from the buffer.
+ */
+public class EventScheduler {
+    private static final long NANOS_PER_MILLI = 1000000;
+
+    private final Object lock = new Object();
+    private SortedMap<Long, FastEventQueue> mEventBuffer;
+    private FastEventQueue mEventPool = null;
+    private int mMaxPoolSize = 200;
+
+    public EventScheduler() {
+        mEventBuffer = new TreeMap<Long, FastEventQueue>();
+    }
+
+    // If we keep at least one node in the list then it can be atomic
+    // and non-blocking.
+    private class FastEventQueue {
+        // One thread takes from the beginning of the list.
+        volatile SchedulableEvent mFirst;
+        // A second thread returns events to the end of the list.
+        volatile SchedulableEvent mLast;
+        volatile long mEventsAdded;
+        volatile long mEventsRemoved;
+
+        FastEventQueue(SchedulableEvent event) {
+            mFirst = event;
+            mLast = mFirst;
+            mEventsAdded = 1;
+            mEventsRemoved = 0;
+        }
+
+        int size() {
+            return (int)(mEventsAdded - mEventsRemoved);
+        }
+
+        /**
+         * Do not call this unless there is more than one event
+         * in the list.
+         * @return first event in the list
+         */
+        public SchedulableEvent remove() {
+            // Take first event.
+            mEventsRemoved++;
+            SchedulableEvent event = mFirst;
+            mFirst = event.mNext;
+            return event;
+        }
+
+        /**
+         * @param event
+         */
+        public void add(SchedulableEvent event) {
+            event.mNext = null;
+            mLast.mNext = event;
+            mLast = event;
+            mEventsAdded++;
+        }
+    }
+
+    /**
+     * Base class for events that can be stored in the EventScheduler.
+     */
+    public static class SchedulableEvent {
+        private long mTimestamp;
+        private SchedulableEvent mNext = null;
+
+        /**
+         * @param timestamp
+         */
+        public SchedulableEvent(long timestamp) {
+            mTimestamp = timestamp;
+        }
+
+        /**
+         * @return timestamp
+         */
+        public long getTimestamp() {
+            return mTimestamp;
+        }
+
+        /**
+         * The timestamp should not be modified when the event is in the
+         * scheduling buffer.
+         */
+        public void setTimestamp(long timestamp) {
+            mTimestamp = timestamp;
+        }
+    }
+
+    /**
+     * Get an event from the pool.
+     * Always leave at least one event in the pool.
+     * @return event or null
+     */
+    public SchedulableEvent removeEventfromPool() {
+        SchedulableEvent event = null;
+        if (mEventPool != null && (mEventPool.size() > 1)) {
+            event = mEventPool.remove();
+        }
+        return event;
+    }
+
+    /**
+     * Return events to a pool so they can be reused.
+     *
+     * @param event
+     */
+    public void addEventToPool(SchedulableEvent event) {
+        if (mEventPool == null) {
+            mEventPool = new FastEventQueue(event);
+        // If we already have enough items in the pool then just
+        // drop the event. This prevents unbounded memory leaks.
+        } else if (mEventPool.size() < mMaxPoolSize) {
+            mEventPool.add(event);
+        }
+    }
+
+    /**
+     * Add an event to the scheduler. Events with the same time will be
+     * processed in order.
+     *
+     * @param event
+     */
+    public void add(SchedulableEvent event) {
+        synchronized (lock) {
+            FastEventQueue list = mEventBuffer.get(event.getTimestamp());
+            if (list == null) {
+                long lowestTime = mEventBuffer.isEmpty() ? Long.MAX_VALUE
+                        : mEventBuffer.firstKey();
+                list = new FastEventQueue(event);
+                mEventBuffer.put(event.getTimestamp(), list);
+                // If the event we added is earlier than the previous earliest
+                // event then notify any threads waiting for the next event.
+                if (event.getTimestamp() < lowestTime) {
+                    lock.notify();
+                }
+            } else {
+                list.add(event);
+            }
+        }
+    }
+
+    private SchedulableEvent removeNextEventLocked(long lowestTime) {
+        SchedulableEvent event;
+        FastEventQueue list = mEventBuffer.get(lowestTime);
+        // Remove list from tree if this is the last node.
+        if ((list.size() == 1)) {
+            mEventBuffer.remove(lowestTime);
+        }
+        event = list.remove();
+        return event;
+    }
+
+    /**
+     * Check to see if any scheduled events are ready to be processed.
+     *
+     * @param timestamp
+     * @return next event or null if none ready
+     */
+    public SchedulableEvent getNextEvent(long time) {
+        SchedulableEvent event = null;
+        synchronized (lock) {
+            if (!mEventBuffer.isEmpty()) {
+                long lowestTime = mEventBuffer.firstKey();
+                // Is it time for this list to be processed?
+                if (lowestTime <= time) {
+                    event = removeNextEventLocked(lowestTime);
+                }
+            }
+        }
+        // Log.i(TAG, "getNextEvent: event = " + event);
+        return event;
+    }
+
+    /**
+     * Return the next available event or wait until there is an event ready to
+     * be processed. This method assumes that the timestamps are in nanoseconds
+     * and that the current time is System.nanoTime().
+     *
+     * @return event
+     * @throws InterruptedException
+     */
+    public SchedulableEvent waitNextEvent() throws InterruptedException {
+        SchedulableEvent event = null;
+        while (true) {
+            long millisToWait = Integer.MAX_VALUE;
+            synchronized (lock) {
+                if (!mEventBuffer.isEmpty()) {
+                    long now = System.nanoTime();
+                    long lowestTime = mEventBuffer.firstKey();
+                    // Is it time for the earliest list to be processed?
+                    if (lowestTime <= now) {
+                        event = removeNextEventLocked(lowestTime);
+                        break;
+                    } else {
+                        // Figure out how long to sleep until next event.
+                        long nanosToWait = lowestTime - now;
+                        // Add 1 millisecond so we don't wake up before it is
+                        // ready.
+                        millisToWait = 1 + (nanosToWait / NANOS_PER_MILLI);
+                        // Clip 64-bit value to 32-bit max.
+                        if (millisToWait > Integer.MAX_VALUE) {
+                            millisToWait = Integer.MAX_VALUE;
+                        }
+                    }
+                }
+                lock.wait((int) millisToWait);
+            }
+        }
+        return event;
+    }
+}
diff --git a/core/java/com/android/internal/midi/MidiConstants.java b/core/java/com/android/internal/midi/MidiConstants.java
new file mode 100644
index 0000000..87552e4
--- /dev/null
+++ b/core/java/com/android/internal/midi/MidiConstants.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.midi;
+
+/**
+ * MIDI related constants and static methods.
+ */
+public class MidiConstants {
+    public static final byte STATUS_COMMAND_MASK = (byte) 0xF0;
+    public static final byte STATUS_CHANNEL_MASK = (byte) 0x0F;
+
+    // Channel voice messages.
+    public static final byte STATUS_NOTE_OFF = (byte) 0x80;
+    public static final byte STATUS_NOTE_ON = (byte) 0x90;
+    public static final byte STATUS_POLYPHONIC_AFTERTOUCH = (byte) 0xA0;
+    public static final byte STATUS_CONTROL_CHANGE = (byte) 0xB0;
+    public static final byte STATUS_PROGRAM_CHANGE = (byte) 0xC0;
+    public static final byte STATUS_CHANNEL_PRESSURE = (byte) 0xD0;
+    public static final byte STATUS_PITCH_BEND = (byte) 0xE0;
+
+    // System Common Messages.
+    public static final byte STATUS_SYSTEM_EXCLUSIVE = (byte) 0xF0;
+    public static final byte STATUS_MIDI_TIME_CODE = (byte) 0xF1;
+    public static final byte STATUS_SONG_POSITION = (byte) 0xF2;
+    public static final byte STATUS_SONG_SELECT = (byte) 0xF3;
+    public static final byte STATUS_TUNE_REQUEST = (byte) 0xF6;
+    public static final byte STATUS_END_SYSEX = (byte) 0xF7;
+
+    // System Real-Time Messages
+    public static final byte STATUS_TIMING_CLOCK = (byte) 0xF8;
+    public static final byte STATUS_START = (byte) 0xFA;
+    public static final byte STATUS_CONTINUE = (byte) 0xFB;
+    public static final byte STATUS_STOP = (byte) 0xFC;
+    public static final byte STATUS_ACTIVE_SENSING = (byte) 0xFE;
+    public static final byte STATUS_RESET = (byte) 0xFF;
+
+    /** Number of bytes in a message nc from 8c to Ec */
+    public final static int CHANNEL_BYTE_LENGTHS[] = { 3, 3, 3, 3, 2, 2, 3 };
+
+    /** Number of bytes in a message Fn from F0 to FF */
+    public final static int SYSTEM_BYTE_LENGTHS[] = { 1, 2, 3, 2, 1, 1, 1, 1, 1,
+            1, 1, 1, 1, 1, 1, 1 };
+
+    /********************************************************************/
+
+    public static int getBytesPerMessage(int command) {
+        if ((command < 0x80) || (command > 0xFF)) {
+            return 0;
+        } else if (command >= 0xF0) {
+            return SYSTEM_BYTE_LENGTHS[command & 0x0F];
+        } else {
+            return CHANNEL_BYTE_LENGTHS[(command >> 4) - 8];
+        }
+    }
+
+    /**
+     * @param msg
+     * @param offset
+     * @param count
+     * @return true if the entire message is ActiveSensing commands
+     */
+    public static boolean isAllActiveSensing(byte[] msg, int offset,
+            int count) {
+        // Count bytes that are not active sensing.
+        int goodBytes = 0;
+        for (int i = 0; i < count; i++) {
+            byte b = msg[offset + i];
+            if (b != MidiConstants.STATUS_ACTIVE_SENSING) {
+                goodBytes++;
+            }
+        }
+        return (goodBytes == 0);
+    }
+}
diff --git a/media/java/android/media/midi/MidiDispatcher.java b/core/java/com/android/internal/midi/MidiDispatcher.java
similarity index 73%
rename from media/java/android/media/midi/MidiDispatcher.java
rename to core/java/com/android/internal/midi/MidiDispatcher.java
index 0868346..377bc68 100644
--- a/media/java/android/media/midi/MidiDispatcher.java
+++ b/core/java/com/android/internal/midi/MidiDispatcher.java
@@ -14,19 +14,20 @@
  * limitations under the License.
  */
 
-package android.media.midi;
+package com.android.internal.midi;
+
+import android.media.midi.MidiReceiver;
+import android.media.midi.MidiSender;
 
 import java.io.IOException;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
- * Utility class for dispatching MIDI data to a list of {@link MidiReceiver}s.
- * This class subclasses {@link MidiReceiver} and dispatches any data it receives
+ * Utility class for dispatching MIDI data to a list of {@link android.media.midi.MidiReceiver}s.
+ * This class subclasses {@link android.media.midi.MidiReceiver} and dispatches any data it receives
  * to its receiver list. Any receivers that throw an exception upon receiving data will
  * be automatically removed from the receiver list, but no IOException will be returned
- * from the dispatcher's {@link #onReceive} in that case.
- *
- * @hide
+ * from the dispatcher's {@link android.media.midi.MidiReceiver#onReceive} in that case.
  */
 public final class MidiDispatcher extends MidiReceiver {
 
@@ -35,7 +36,7 @@
 
     private final MidiSender mSender = new MidiSender() {
         /**
-         * Called to connect a {@link MidiReceiver} to the sender
+         * Called to connect a {@link android.media.midi.MidiReceiver} to the sender
          *
          * @param receiver the receiver to connect
          */
@@ -44,7 +45,7 @@
         }
 
         /**
-         * Called to disconnect a {@link MidiReceiver} from the sender
+         * Called to disconnect a {@link android.media.midi.MidiReceiver} from the sender
          *
          * @param receiver the receiver to disconnect
          */
@@ -54,7 +55,7 @@
     };
 
     /**
-     * Returns the number of {@link MidiReceiver}s this dispatcher contains.
+     * Returns the number of {@link android.media.midi.MidiReceiver}s this dispatcher contains.
      * @return the number of receivers
      */
     public int getReceiverCount() {
@@ -62,7 +63,8 @@
     }
 
     /**
-     * Returns a {@link MidiSender} which is used to add and remove {@link MidiReceiver}s
+     * Returns a {@link android.media.midi.MidiSender} which is used to add and remove
+     * {@link android.media.midi.MidiReceiver}s
      * to the dispatcher's receiver list.
      * @return the dispatcher's MidiSender
      */
diff --git a/core/java/com/android/internal/midi/MidiEventScheduler.java b/core/java/com/android/internal/midi/MidiEventScheduler.java
new file mode 100644
index 0000000..3a1d3fc
--- /dev/null
+++ b/core/java/com/android/internal/midi/MidiEventScheduler.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.midi;
+
+import android.media.midi.MidiReceiver;
+
+import java.io.IOException;
+
+/**
+ * Add MIDI Events to an EventScheduler
+ */
+public class MidiEventScheduler extends EventScheduler {
+    private static final String TAG = "MidiEventScheduler";
+    // Maintain a pool of scheduled events to reduce memory allocation.
+    // This pool increases performance by about 14%.
+    private final static int POOL_EVENT_SIZE = 16;
+    private MidiReceiver mReceiver = new SchedulingReceiver();
+
+    private class SchedulingReceiver extends MidiReceiver
+    {
+        /**
+         * Store these bytes in the EventScheduler to be delivered at the specified
+         * time.
+         */
+        @Override
+        public void onReceive(byte[] msg, int offset, int count, long timestamp)
+                throws IOException {
+            MidiEvent event = createScheduledEvent(msg, offset, count, timestamp);
+            if (event != null) {
+                add(event);
+            }
+        }
+    }
+
+    public static class MidiEvent extends SchedulableEvent {
+        public int count = 0;
+        public byte[] data;
+
+        private MidiEvent(int count) {
+            super(0);
+            data = new byte[count];
+        }
+
+        private MidiEvent(byte[] msg, int offset, int count, long timestamp) {
+            super(timestamp);
+            data = new byte[count];
+            System.arraycopy(msg, offset, data, 0, count);
+            this.count = count;
+        }
+
+        @Override
+        public String toString() {
+            String text = "Event: ";
+            for (int i = 0; i < count; i++) {
+                text += data[i] + ", ";
+            }
+            return text;
+        }
+    }
+
+    /**
+     * Create an event that contains the message.
+     */
+    private MidiEvent createScheduledEvent(byte[] msg, int offset, int count,
+            long timestamp) {
+        MidiEvent event;
+        if (count > POOL_EVENT_SIZE) {
+            event = new MidiEvent(msg, offset, count, timestamp);
+        } else {
+            event = (MidiEvent) removeEventfromPool();
+            if (event == null) {
+                event = new MidiEvent(POOL_EVENT_SIZE);
+            }
+            System.arraycopy(msg, offset, event.data, 0, count);
+            event.count = count;
+            event.setTimestamp(timestamp);
+        }
+        return event;
+    }
+
+    /**
+     * Return events to a pool so they can be reused.
+     *
+     * @param event
+     */
+    @Override
+    public void addEventToPool(SchedulableEvent event) {
+        // Make sure the event is suitable for the pool.
+        if (event instanceof MidiEvent) {
+            MidiEvent midiEvent = (MidiEvent) event;
+            if (midiEvent.data.length == POOL_EVENT_SIZE) {
+                super.addEventToPool(event);
+            }
+        }
+    }
+
+    /**
+     * This MidiReceiver will write date to the scheduling buffer.
+     * @return the MidiReceiver
+     */
+    public MidiReceiver getReceiver() {
+        return mReceiver;
+    }
+
+}
diff --git a/core/java/com/android/internal/midi/MidiFramer.java b/core/java/com/android/internal/midi/MidiFramer.java
new file mode 100644
index 0000000..53d71bb
--- /dev/null
+++ b/core/java/com/android/internal/midi/MidiFramer.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.midi;
+
+import android.media.midi.MidiReceiver;
+
+import java.io.IOException;
+
+/**
+ * Convert stream of bytes to discrete messages.
+ *
+ * Parses the incoming bytes and then posts individual messages to the receiver
+ * specified in the constructor. Short messages of 1-3 bytes will be complete.
+ * System Exclusive messages may be posted in pieces.
+ *
+ * Resolves Running Status and
+ * interleaved System Real-Time messages.
+ */
+public class MidiFramer extends MidiReceiver {
+
+    public String TAG = "MidiFramer";
+    private MidiReceiver mReceiver;
+    private byte[] mBuffer = new byte[3];
+    private int mCount;
+    private int mRunningStatus;
+    private int mNeeded;
+
+    public MidiFramer(MidiReceiver receiver) {
+        mReceiver = receiver;
+    }
+
+    public static String formatMidiData(byte[] data, int offset, int count) {
+        String text = "MIDI+" + offset + " : ";
+        for (int i = 0; i < count; i++) {
+            text += String.format("0x%02X, ", data[offset + i]);
+        }
+        return text;
+    }
+
+    /*
+     * @see android.midi.MidiReceiver#onPost(byte[], int, int, long)
+     */
+    @Override
+    public void onReceive(byte[] data, int offset, int count, long timestamp)
+            throws IOException {
+        // Log.i(TAG, formatMidiData(data, offset, count));
+        for (int i = 0; i < count; i++) {
+            int b = data[offset] & 0xFF;
+            if (b >= 0x80) { // status byte?
+                if (b < 0xF0) { // channel message?
+                    mRunningStatus = (byte) b;
+                    mCount = 1;
+                    mNeeded = MidiConstants.getBytesPerMessage(b) - 1;
+                } else if (b < 0xF8) { // system common?
+                    mBuffer[0] = (byte) b;
+                    mRunningStatus = 0;
+                    mCount = 1;
+                    mNeeded = MidiConstants.getBytesPerMessage(b) - 1;
+                } else { // real-time?
+                    // Single byte message interleaved with other data.
+                    mReceiver.sendWithTimestamp(data, offset, 1, timestamp);
+                }
+            } else { // data byte
+                mBuffer[mCount++] = (byte) b;
+                if (--mNeeded == 0) {
+                    if (mRunningStatus != 0) {
+                        mBuffer[0] = (byte) mRunningStatus;
+                    }
+                    mReceiver.sendWithTimestamp(mBuffer, 0, mCount, timestamp);
+                    mNeeded = MidiConstants.getBytesPerMessage(mBuffer[0]) - 1;
+                    mCount = 1;
+                }
+            }
+            ++offset;
+        }
+    }
+
+}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 8674a21..75b6446 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -41,15 +41,10 @@
     /** enable the JIT compiler */
     public static final int DEBUG_ENABLE_JIT         = 1 << 5;
 
-
     /** No external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_NONE = 0;
-    /** Single-user external storage should be mounted. */
-    public static final int MOUNT_EXTERNAL_SINGLEUSER = 1;
-    /** Multi-user external storage should be mounted. */
-    public static final int MOUNT_EXTERNAL_MULTIUSER = 2;
-    /** All multi-user external storage should be mounted. */
-    public static final int MOUNT_EXTERNAL_MULTIUSER_ALL = 3;
+    /** Default user-specific external storage should be mounted. */
+    public static final int MOUNT_EXTERNAL_DEFAULT = 1;
 
     private static final ZygoteHooks VM_HOOKS = new ZygoteHooks();
 
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 4d405b2..9106ccd 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -514,10 +514,8 @@
                                 "Duplicate arg specified");
                     }
                     niceName = arg.substring(arg.indexOf('=') + 1);
-                } else if (arg.equals("--mount-external-multiuser")) {
-                    mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
-                } else if (arg.equals("--mount-external-multiuser-all")) {
-                    mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL;
+                } else if (arg.equals("--mount-external-default")) {
+                    mountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
                 } else if (arg.equals("--query-abi-list")) {
                     abiListQuery = true;
                 } else if (arg.startsWith("--instruction-set=")) {
diff --git a/core/java/com/android/internal/widget/ViewPager.java b/core/java/com/android/internal/widget/ViewPager.java
index 8018942..8d66191 100644
--- a/core/java/com/android/internal/widget/ViewPager.java
+++ b/core/java/com/android/internal/widget/ViewPager.java
@@ -42,6 +42,7 @@
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.accessibility.AccessibilityRecord;
 import android.view.animation.Interpolator;
 import android.widget.EdgeEffect;
@@ -371,8 +372,6 @@
         mCloseEnough = (int) (CLOSE_ENOUGH * density);
         mDefaultGutterSize = (int) (DEFAULT_GUTTER_SIZE * density);
 
-        setAccessibilityDelegate(new MyAccessibilityDelegate());
-
         if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
             setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
         }
@@ -2695,29 +2694,6 @@
     }
 
     @Override
-    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
-        // Dispatch scroll events from this ViewPager.
-        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
-            return super.dispatchPopulateAccessibilityEvent(event);
-        }
-
-        // Dispatch all other accessibility events from the current page.
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final View child = getChildAt(i);
-            if (child.getVisibility() == VISIBLE) {
-                final ItemInfo ii = infoForChild(child);
-                if (ii != null && ii.position == mCurItem &&
-                        child.dispatchPopulateAccessibilityEvent(event)) {
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    @Override
     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
         return new LayoutParams();
     }
@@ -2737,60 +2713,63 @@
         return new LayoutParams(getContext(), attrs);
     }
 
-    class MyAccessibilityDelegate extends AccessibilityDelegate {
 
-        @Override
-        public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
-            super.onInitializeAccessibilityEvent(host, event);
-            event.setClassName(ViewPager.class.getName());
-            final AccessibilityRecord record = AccessibilityRecord.obtain();
-            record.setScrollable(canScroll());
-            if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED
-                    && mAdapter != null) {
-                record.setItemCount(mAdapter.getCount());
-                record.setFromIndex(mCurItem);
-                record.setToIndex(mCurItem);
-            }
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+
+        event.setClassName(ViewPager.class.getName());
+        event.setScrollable(canScroll());
+
+        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED && mAdapter != null) {
+            event.setItemCount(mAdapter.getCount());
+            event.setFromIndex(mCurItem);
+            event.setToIndex(mCurItem);
+        }
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+
+        info.setClassName(ViewPager.class.getName());
+        info.setScrollable(canScroll());
+
+        if (canScrollHorizontally(1)) {
+            info.addAction(AccessibilityAction.ACTION_SCROLL_FORWARD);
         }
 
-        @Override
-        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
-            super.onInitializeAccessibilityNodeInfo(host, info);
-            info.setClassName(ViewPager.class.getName());
-            info.setScrollable(canScroll());
-            if (canScrollHorizontally(1)) {
-                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
-            }
-            if (canScrollHorizontally(-1)) {
-                info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
-            }
+        if (canScrollHorizontally(-1)) {
+            info.addAction(AccessibilityAction.ACTION_SCROLL_BACKWARD);
+        }
+    }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle args) {
+        if (super.performAccessibilityAction(action, args)) {
+            return true;
         }
 
-        @Override
-        public boolean performAccessibilityAction(View host, int action, Bundle args) {
-            if (super.performAccessibilityAction(host, action, args)) {
-                return true;
-            }
-            switch (action) {
-                case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
-                    if (canScrollHorizontally(1)) {
-                        setCurrentItem(mCurItem + 1);
-                        return true;
-                    }
-                } return false;
-                case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
-                    if (canScrollHorizontally(-1)) {
-                        setCurrentItem(mCurItem - 1);
-                        return true;
-                    }
-                } return false;
-            }
-            return false;
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+                if (canScrollHorizontally(1)) {
+                    setCurrentItem(mCurItem + 1);
+                    return true;
+                }
+                return false;
+            case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+                if (canScrollHorizontally(-1)) {
+                    setCurrentItem(mCurItem - 1);
+                    return true;
+                }
+                return false;
         }
 
-        private boolean canScroll() {
-            return (mAdapter != null) && (mAdapter.getCount() > 1);
-        }
+        return false;
+    }
+
+    private boolean canScroll() {
+        return mAdapter != null && mAdapter.getCount() > 1;
     }
 
     private class PagerObserver extends DataSetObserver {
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 34de6c5..a4c91b3 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -558,6 +558,8 @@
     char dex2oatXmxFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];
     char dex2oatCompilerFilterBuf[sizeof("--compiler-filter=")-1 + PROPERTY_VALUE_MAX];
     char dex2oatImageCompilerFilterBuf[sizeof("--compiler-filter=")-1 + PROPERTY_VALUE_MAX];
+    char dex2oatThreadsBuf[sizeof("-j")-1 + PROPERTY_VALUE_MAX];
+    char dex2oatThreadsImageBuf[sizeof("-j")-1 + PROPERTY_VALUE_MAX];
     char dex2oatFlagsBuf[PROPERTY_VALUE_MAX];
     char dex2oatImageFlagsBuf[PROPERTY_VALUE_MAX];
     char extraOptsBuf[PROPERTY_VALUE_MAX];
@@ -733,6 +735,9 @@
         parseCompilerOption("dalvik.vm.dex2oat-filter", dex2oatCompilerFilterBuf,
                             "--compiler-filter=", "-Xcompiler-option");
     }
+    parseCompilerOption("dalvik.vm.dex2oat-threads", dex2oatThreadsBuf, "-j", "-Xcompiler-option");
+    parseCompilerOption("dalvik.vm.image-dex2oat-threads", dex2oatThreadsImageBuf, "-j",
+                        "-Ximage-compiler-option");
     property_get("dalvik.vm.dex2oat-flags", dex2oatFlagsBuf, "");
     parseExtraOpts(dex2oatFlagsBuf, "-Xcompiler-option");
 
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 49ee6c5..4c08b4b 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -89,7 +89,7 @@
 static void restore(JNIEnv* env, jobject, jlong canvasHandle) {
     Canvas* canvas = get_canvas(canvasHandle);
     if (canvas->getSaveCount() <= 1) {  // cannot restore anymore
-        // fail silently on underflow, so as not to break existing apps that miscount
+        doThrowISE(env, "Underflow in restore - more restores than saves");
         return;
     }
     canvas->restore();
@@ -98,7 +98,7 @@
 static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle, jint restoreCount) {
     Canvas* canvas = get_canvas(canvasHandle);
     if (restoreCount < 1 || restoreCount > canvas->getSaveCount()) {
-        // fail silently on underflow, so as not to break existing apps that miscount
+        doThrowIAE(env, "Underflow in restoreToCount - more restores than saves");
         return;
     }
     canvas->restoreToCount(restoreCount);
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 6cc1f68..c020020 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -21,6 +21,7 @@
 #include "unicode/brkiter.h"
 #include "utils/misc.h"
 #include "utils/Log.h"
+#include "ScopedStringChars.h"
 #include "ScopedPrimitiveArray.h"
 #include "JNIHelp.h"
 #include "core_jni_helpers.h"
@@ -71,14 +72,14 @@
 }
 
 static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
-                        jfloatArray recycleWidths, jbooleanArray recycleFlags,
+                        jfloatArray recycleWidths, jintArray recycleFlags,
                         jint recycleLength, size_t nBreaks, const jint* breaks,
-                        const jfloat* widths, const jboolean* flags) {
+                        const jfloat* widths, const jint* flags) {
     if ((size_t)recycleLength < nBreaks) {
         // have to reallocate buffers
         recycleBreaks = env->NewIntArray(nBreaks);
         recycleWidths = env->NewFloatArray(nBreaks);
-        recycleFlags = env->NewBooleanArray(nBreaks);
+        recycleFlags = env->NewIntArray(nBreaks);
 
         env->SetObjectField(recycle, gLineBreaks_fieldID.breaks, recycleBreaks);
         env->SetObjectField(recycle, gLineBreaks_fieldID.widths, recycleWidths);
@@ -87,12 +88,12 @@
     // copy data
     env->SetIntArrayRegion(recycleBreaks, 0, nBreaks, breaks);
     env->SetFloatArrayRegion(recycleWidths, 0, nBreaks, widths);
-    env->SetBooleanArrayRegion(recycleFlags, 0, nBreaks, flags);
+    env->SetIntArrayRegion(recycleFlags, 0, nBreaks, flags);
 }
 
 static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
                                jobject recycle, jintArray recycleBreaks,
-                               jfloatArray recycleWidths, jbooleanArray recycleFlags,
+                               jfloatArray recycleWidths, jintArray recycleFlags,
                                jint recycleLength) {
     LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
 
@@ -119,12 +120,20 @@
     b->finish();
 }
 
-static void nSetLocale(JNIEnv* env, jclass, jlong nativePtr, jstring javaLocaleName) {
+static jlong nLoadHyphenator(JNIEnv* env, jclass, jstring patternData) {
+    ScopedStringChars str(env, patternData);
+    Hyphenator* hyphenator = Hyphenator::load(str.get(), str.size());
+    return reinterpret_cast<jlong>(hyphenator);
+}
+
+static void nSetLocale(JNIEnv* env, jclass, jlong nativePtr, jstring javaLocaleName,
+        jlong nativeHyphenator) {
     ScopedIcuLocale icuLocale(env, javaLocaleName);
     LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
+    Hyphenator* hyphenator = reinterpret_cast<Hyphenator*>(nativeHyphenator);
 
     if (icuLocale.valid()) {
-        b->setLocale(icuLocale.locale());
+        b->setLocale(icuLocale.locale(), hyphenator);
     }
 }
 
@@ -164,13 +173,14 @@
     {"nNewBuilder", "()J", (void*) nNewBuilder},
     {"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
     {"nFinishBuilder", "(J)V", (void*) nFinishBuilder},
-    {"nSetLocale", "(JLjava/lang/String;)V", (void*) nSetLocale},
+    {"nLoadHyphenator", "(Ljava/lang/String;)J", (void*) nLoadHyphenator},
+    {"nSetLocale", "(JLjava/lang/String;J)V", (void*) nSetLocale},
     {"nSetupParagraph", "(J[CIFIF[III)V", (void*) nSetupParagraph},
     {"nAddStyleRun", "(JJJIIZ)F", (void*) nAddStyleRun},
     {"nAddMeasuredRun", "(JII[F)V", (void*) nAddMeasuredRun},
     {"nAddReplacementRun", "(JIIF)V", (void*) nAddReplacementRun},
     {"nGetWidths", "(J[F)V", (void*) nGetWidths},
-    {"nComputeLineBreaks", "(JLandroid/text/StaticLayout$LineBreaks;[I[F[ZI)I",
+    {"nComputeLineBreaks", "(JLandroid/text/StaticLayout$LineBreaks;[I[F[II)I",
         (void*) nComputeLineBreaks}
 };
 
@@ -181,7 +191,7 @@
 
     gLineBreaks_fieldID.breaks = GetFieldIDOrDie(env, gLineBreaks_class, "breaks", "[I");
     gLineBreaks_fieldID.widths = GetFieldIDOrDie(env, gLineBreaks_class, "widths", "[F");
-    gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[Z");
+    gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[I");
 
     return RegisterMethodsOrDie(env, "android/text/StaticLayout", gMethods, NELEM(gMethods));
 }
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index fff8604..f1c90ea 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -192,6 +192,9 @@
         case HAL_PIXEL_FORMAT_YCbCr_422_I:
             // Name differs, though the value is the same
             return PublicFormat::YUY2;
+        case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+            // Name differs, though the value is the same
+            return PublicFormat::PRIVATE;
         case HAL_PIXEL_FORMAT_Y16:
             // Dataspace-dependent
             switch (dataSpace) {
@@ -216,7 +219,6 @@
             break;
         case HAL_PIXEL_FORMAT_BGRA_8888:
         case HAL_PIXEL_FORMAT_RAW_OPAQUE:
-        case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
             // Not defined in public API
             return PublicFormat::UNKNOWN;
 
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 3d9a9ed..11b3805 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -21,6 +21,7 @@
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
 #include "core_jni_helpers.h"
+#include <ScopedPrimitiveArray.h>
 
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
@@ -35,6 +36,7 @@
 #include <Animator.h>
 #include <AnimationContext.h>
 #include <IContextFactory.h>
+#include <JankTracker.h>
 #include <RenderNode.h>
 #include <renderthread/CanvasContext.h>
 #include <renderthread/RenderProxy.h>
@@ -219,6 +221,12 @@
     proxy->setTextureAtlas(buffer, map, len);
 }
 
+static void android_view_ThreadedRenderer_setProcessStatsBuffer(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jint fd) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setProcessStatsBuffer(fd);
+}
+
 static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
     RootRenderNode* node = new RootRenderNode(env);
     node->incStrong(0);
@@ -403,6 +411,16 @@
     proxy->dumpProfileInfo(fd, dumpFlags);
 }
 
+static void android_view_ThreadedRenderer_dumpProfileData(JNIEnv* env, jobject clazz,
+        jbyteArray jdata, jobject javaFileDescriptor) {
+    int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
+    ScopedByteArrayRO buffer(env, jdata);
+    if (buffer.get()) {
+        JankTracker::dumpBuffer(buffer.get(), buffer.size(), fd);
+    }
+}
+
+
 // ----------------------------------------------------------------------------
 // Shaders
 // ----------------------------------------------------------------------------
@@ -423,6 +441,7 @@
 
 static JNINativeMethod gMethods[] = {
     { "nSetAtlas", "(JLandroid/view/GraphicBuffer;[J)V",   (void*) android_view_ThreadedRenderer_setAtlas },
+    { "nSetProcessStatsBuffer", "(JI)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer },
     { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
     { "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy },
     { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy },
@@ -449,6 +468,7 @@
     { "nStopDrawing", "(J)V", (void*) android_view_ThreadedRenderer_stopDrawing },
     { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
     { "nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo },
+    { "nDumpProfileData", "([BLjava/io/FileDescriptor;)V", (void*) android_view_ThreadedRenderer_dumpProfileData },
     { "setupShadersDiskCache", "(Ljava/lang/String;)V",
                 (void*) android_view_ThreadedRenderer_setupShadersDiskCache },
 };
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 2bfeadb..76db5d3 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -65,9 +65,7 @@
 // Must match values in com.android.internal.os.Zygote.
 enum MountExternalKind {
   MOUNT_EXTERNAL_NONE = 0,
-  MOUNT_EXTERNAL_SINGLEUSER = 1,
-  MOUNT_EXTERNAL_MULTIUSER = 2,
-  MOUNT_EXTERNAL_MULTIUSER_ALL = 3,
+  MOUNT_EXTERNAL_DEFAULT = 1,
 };
 
 static void RuntimeAbort(JNIEnv* env) {
@@ -269,57 +267,16 @@
   // See storage config details at http://source.android.com/tech/storage/
   userid_t user_id = multiuser_get_user_id(uid);
 
-  // Create bind mounts to expose external storage
-  if (mount_mode == MOUNT_EXTERNAL_MULTIUSER || mount_mode == MOUNT_EXTERNAL_MULTIUSER_ALL) {
-    // These paths must already be created by init.rc
-    const char* source = getenv("EMULATED_STORAGE_SOURCE");
-    const char* target = getenv("EMULATED_STORAGE_TARGET");
-    const char* legacy = getenv("EXTERNAL_STORAGE");
-    if (source == NULL || target == NULL || legacy == NULL) {
-      ALOGW("Storage environment undefined; unable to provide external storage");
-      return false;
-    }
+  // Bind mount user-specific storage into place
+  const String8 source(String8::format("/mnt/user/%d", user_id));
+  const String8 target(String8::format("/storage/self"));
 
-    // Prepare source paths
+  if (fs_prepare_dir(source.string(), 0755, 0, 0) == -1) {
+    return false;
+  }
 
-    // /mnt/shell/emulated/0
-    const String8 source_user(String8::format("%s/%d", source, user_id));
-    // /storage/emulated/0
-    const String8 target_user(String8::format("%s/%d", target, user_id));
-
-    if (fs_prepare_dir(source_user.string(), 0000, 0, 0) == -1
-        || fs_prepare_dir(target_user.string(), 0000, 0, 0) == -1) {
-      return false;
-    }
-
-    if (mount_mode == MOUNT_EXTERNAL_MULTIUSER_ALL) {
-      // Mount entire external storage tree for all users
-      if (TEMP_FAILURE_RETRY(mount(source, target, NULL, MS_BIND, NULL)) == -1) {
-        ALOGW("Failed to mount %s to %s: %s", source, target, strerror(errno));
-        return false;
-      }
-    } else {
-      // Only mount user-specific external storage
-      if (TEMP_FAILURE_RETRY(mount(source_user.string(), target_user.string(), NULL,
-                                   MS_BIND, NULL)) == -1) {
-        ALOGW("Failed to mount %s to %s: %s", source_user.string(), target_user.string(),
-              strerror(errno));
-        return false;
-      }
-    }
-
-    if (fs_prepare_dir(legacy, 0000, 0, 0) == -1) {
-        return false;
-    }
-
-    // Finally, mount user-specific path into place for legacy users
-    if (TEMP_FAILURE_RETRY(
-            mount(target_user.string(), legacy, NULL, MS_BIND | MS_REC, NULL)) == -1) {
-      ALOGW("Failed to mount %s to %s: %s", target_user.string(), legacy, strerror(errno));
-      return false;
-    }
-  } else {
-    ALOGW("Mount mode %d unsupported", mount_mode);
+  if (TEMP_FAILURE_RETRY(mount(source.string(), target.string(), NULL, MS_BIND, NULL)) == -1) {
+    ALOGW("Failed to mount %s to %s: %s", source.string(), target.string(), strerror(errno));
     return false;
   }
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 851c4bf..0b1b807 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -73,6 +73,7 @@
     <protected-broadcast android:name="android.intent.action.USER_BACKGROUND" />
     <protected-broadcast android:name="android.intent.action.USER_FOREGROUND" />
     <protected-broadcast android:name="android.intent.action.USER_SWITCHED" />
+    <protected-broadcast android:name="android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION" />
 
     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED" />
     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGING" />
@@ -2799,6 +2800,23 @@
         android:description="@string/permdesc_bindPackageVerifier"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi @hide Intent filter verifier needs to have this permission before the
+         PackageManager will trust it to verify intent filters.
+    -->
+    <permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"
+        android:label="@string/permlab_intentFilterVerificationAgent"
+        android:description="@string/permdesc_intentFilterVerificationAgent"
+        android:protectionLevel="signature|system" />
+
+    <!-- Must be required by intent filter verifier receiver, to ensure that only the
+         system can interact with it.
+         @hide
+    -->
+    <permission android:name="android.permission.BIND_INTENT_FILTER_VERIFIER"
+        android:label="@string/permlab_bindIntentFilterVerifier"
+        android:description="@string/permdesc_bindIntentFilterVerifier"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi Allows applications to access serial ports via the SerialManager.
          @hide -->
     <permission android:name="android.permission.SERIAL_PORT"
diff --git a/core/res/res/color/primary_text_secondary_when_activated_material.xml b/core/res/res/color/primary_text_secondary_when_activated_material.xml
new file mode 100644
index 0000000..7ab4a2e
--- /dev/null
+++ b/core/res/res/color/primary_text_secondary_when_activated_material.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_activated="true"
+        android:color="?attr/textColorPrimary" />
+    <item
+        android:color="?attr/textColorSecondary" />
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_header_text_material.xml b/core/res/res/color/primary_text_secondary_when_activated_material_inverse.xml
similarity index 100%
rename from core/res/res/color/date_picker_header_text_material.xml
rename to core/res/res/color/primary_text_secondary_when_activated_material_inverse.xml
diff --git a/core/res/res/color/time_picker_header_text_material.xml b/core/res/res/color/time_picker_header_text_material.xml
deleted file mode 100644
index cda894b..0000000
--- a/core/res/res/color/time_picker_header_text_material.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item
-        android:state_selected="true"
-        android:color="?attr/textColorPrimaryInverse" />
-    <item
-        android:color="?attr/textColorSecondaryInverse" />
-</selector>
\ No newline at end of file
diff --git a/core/res/res/drawable/ic_chevron_left.xml b/core/res/res/drawable/ic_chevron_left.xml
new file mode 100644
index 0000000..dc24706
--- /dev/null
+++ b/core/res/res/drawable/ic_chevron_left.xml
@@ -0,0 +1,25 @@
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M15.41 7.41L14 6l-6 6 6 6 1.41,-1.41L10.83 12z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_chevron_right.xml b/core/res/res/drawable/ic_chevron_right.xml
new file mode 100644
index 0000000..4e6d8e3
--- /dev/null
+++ b/core/res/res/drawable/ic_chevron_right.xml
@@ -0,0 +1,25 @@
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6,-6z"/>
+</vector>
diff --git a/core/res/res/layout-land/time_picker_material.xml b/core/res/res/layout-land/time_picker_material.xml
index 1b85e8f..89c3749 100644
--- a/core/res/res/layout-land/time_picker_material.xml
+++ b/core/res/res/layout-land/time_picker_material.xml
@@ -53,6 +53,7 @@
                 android:id="@+id/hours"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
                 android:singleLine="true"
                 android:ellipsize="none"
                 android:gravity="right"
@@ -64,6 +65,7 @@
                 android:id="@+id/separator"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
                 android:importantForAccessibility="no"
                 tools:text=":"
                 tools:textSize="@dimen/timepicker_time_label_size"
@@ -75,6 +77,7 @@
                 android:id="@+id/minutes"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
                 android:singleLine="true"
                 android:ellipsize="none"
                 android:gravity="left"
@@ -97,6 +100,7 @@
                 android:id="@+id/am_label"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
                 android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
                 android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
                 android:paddingTop="@dimen/timepicker_am_top_padding"
@@ -111,6 +115,7 @@
                 android:id="@+id/pm_label"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
+                android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
                 android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
                 android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
                 android:paddingTop="@dimen/timepicker_pm_top_padding"
diff --git a/core/res/res/layout/date_picker_month_item_material.xml b/core/res/res/layout/date_picker_month_item_material.xml
new file mode 100644
index 0000000..cb79cee
--- /dev/null
+++ b/core/res/res/layout/date_picker_month_item_material.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<android.widget.SimpleMonthView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/month_view"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="@dimen/day_picker_padding_horizontal"
+    android:paddingEnd="@dimen/day_picker_padding_horizontal"
+    android:paddingTop="@dimen/day_picker_padding_top" />
diff --git a/core/res/res/layout/date_picker_view_animator_material.xml b/core/res/res/layout/date_picker_view_animator_material.xml
index 98ef1dd..620ddfa 100644
--- a/core/res/res/layout/date_picker_view_animator_material.xml
+++ b/core/res/res/layout/date_picker_view_animator_material.xml
@@ -26,9 +26,8 @@
         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:inAnimation="@anim/fade_in"
+        android:outAnimation="@anim/fade_out" />
 
     <android.widget.YearPickerView
         android:id="@+id/date_picker_year_picker"
diff --git a/core/res/res/layout/time_picker_header_material.xml b/core/res/res/layout/time_picker_header_material.xml
index 0ef404d..be9e443 100644
--- a/core/res/res/layout/time_picker_header_material.xml
+++ b/core/res/res/layout/time_picker_header_material.xml
@@ -15,6 +15,8 @@
   ~ limitations under the License
   -->
 
+<!-- This layout is duplicated in land/time_picker_material.xml, so any
+     changes made here need to be manually copied over. -->
 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                 xmlns:tools="http://schemas.android.com/tools"
                 android:id="@+id/time_header"
@@ -32,6 +34,7 @@
         android:layout_height="wrap_content"
         android:layout_toLeftOf="@+id/separator"
         android:layout_alignBaseline="@+id/separator"
+        android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
         android:singleLine="true"
         android:ellipsize="none"
         android:gravity="right"
@@ -46,6 +49,7 @@
         android:layout_marginLeft="@dimen/timepicker_separator_padding"
         android:layout_marginRight="@dimen/timepicker_separator_padding"
         android:layout_centerInParent="true"
+        android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
         android:importantForAccessibility="no"
         tools:text=":"
         tools:textSize="@dimen/timepicker_time_label_size"
@@ -59,6 +63,7 @@
         android:layout_height="wrap_content"
         android:layout_toRightOf="@+id/separator"
         android:layout_alignBaseline="@+id/separator"
+        android:textAppearance="@style/TextAppearance.Material.TimePicker.TimeLabel"
         android:singleLine="true"
         android:ellipsize="none"
         android:gravity="left"
@@ -83,6 +88,7 @@
             android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
             android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
             android:paddingTop="@dimen/timepicker_am_top_padding"
+            android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
             android:lines="1"
             android:ellipsize="none"
             tools:text="AM"
@@ -95,6 +101,7 @@
             android:paddingStart="@dimen/timepicker_ampm_horizontal_padding"
             android:paddingEnd="@dimen/timepicker_ampm_horizontal_padding"
             android:paddingTop="@dimen/timepicker_pm_top_padding"
+            android:textAppearance="@style/TextAppearance.Material.TimePicker.AmPmLabel"
             android:lines="1"
             android:ellipsize="none"
             tools:text="PM"
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index 8ab4a03..9a59303 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -237,7 +237,7 @@
     <string name="permgrouplab_writeDictionary" msgid="8090237702432576788">"Գրել օգտվողի բառարանում"</string>
     <string name="permgroupdesc_writeDictionary" msgid="2711561994497361646">"Ավելացնել բառեր օգտվողի բառարանում:"</string>
     <string name="permgrouplab_bookmarks" msgid="1949519673103968229">"Էջանիշեր և պատմություն"</string>
-    <string name="permgroupdesc_bookmarks" msgid="4169771606257963028">"Ուղղակի մուտք դեպի էջանիշեր և դիտարկչի պատմություն:"</string>
+    <string name="permgroupdesc_bookmarks" msgid="4169771606257963028">"Ուղղակի մուտք դեպի էջանիշեր և դիտարկիչի պատմություն:"</string>
     <string name="permgrouplab_deviceAlarms" msgid="6117704629728824101">"Ազդանշան"</string>
     <string name="permgroupdesc_deviceAlarms" msgid="4769356362251641175">"Կարգավորել զարթուցիչի ժամացույցը:"</string>
     <string name="permgrouplab_voicemail" msgid="4162237145027592133">"Ձայնային փոստ"</string>
@@ -1117,11 +1117,11 @@
     <string name="autofill_area" msgid="3547409050889952423">"Տարածք"</string>
     <string name="autofill_emirate" msgid="2893880978835698818">"Էմիրություն"</string>
     <string name="permlab_readHistoryBookmarks" msgid="3775265775405106983">"կարդալ ձեր վեբ էջանիշերը և պատմությունը"</string>
-    <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Թույլ է տալիս հավելվածին կարդալ դիտարկչի այցելած բոլոր URL-ների պատմությունը և դիտարկչի բոլոր էջանիշերը: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
+    <string name="permdesc_readHistoryBookmarks" msgid="8462378226600439658">"Թույլ է տալիս հավելվածին կարդալ դիտարկիչի այցելած բոլոր URL-ների պատմությունը և դիտարկիչի բոլոր էջանիշերը: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
     <string name="permlab_writeHistoryBookmarks" msgid="3714785165273314490">"գրել վեբ էջանիշերը և պատմությունը"</string>
-    <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Թույլ է տալիս հավելվածին փոփոխել դիտարկչի պատմությունը կամ ձեր գրասալիկում պահված էջանիշերը: Այն կարող է թույլ տալ հավելվածին ջնջել կամ փոփոխել դիտարկչի տվյալները: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
+    <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="6825527469145760922">"Թույլ է տալիս հավելվածին փոփոխել դիտարկիչի պատմությունը կամ ձեր գրասալիկում պահված էջանիշերը: Այն կարող է թույլ տալ հավելվածին ջնջել կամ փոփոխել դիտարկիչի տվյալները: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
     <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="7007393823197766548">"Թույլ է տալիս հավելվածին փոփոխել դիտարկիչի պատմությունը կամ հեռուստացույցում պահված էջանիշները: Սա կարող է թույլ տալ հավելվածին ջնջել կամ փոփոխել դիտարկիչի տվյալները: Ուշադրություն. այս թույլտվությունը չի կարող հարկադրվել երրորդ կողմի դիտարկիչների կամ այլ հավելվածների կողմից, որոնք նույնպես կարողանում են վեբ էջեր բացել:"</string>
-    <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Թույլ է տալիս հավելվածին փոփոխել դիտարկչի պատմությունը կամ ձեր հեռախոսում պահված էջանիշերը: Այն կարող է թույլ տալ հավելվածին ջնջել կամ փոփոխել դիտարկչի տվյալները: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
+    <string name="permdesc_writeHistoryBookmarks" product="default" msgid="8497389531014185509">"Թույլ է տալիս հավելվածին փոփոխել դիտարկիչի պատմությունը կամ ձեր հեռախոսում պահված էջանիշերը: Այն կարող է թույլ տալ հավելվածին ջնջել կամ փոփոխել դիտարկիչի տվյալները: Նշում. այս թույլտվությունը չի կարող գործածվել կողմնակի դիտարկիչների կամ վեբ զննարկման հնարավորություններով այլ հավելվածների կողմից:"</string>
     <string name="permlab_setAlarm" msgid="1379294556362091814">"դնել ազդանշան"</string>
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Թույլ է տալիս հավելվածին սահմանել զարթուցիչի ծրագրում տեղադրված ազդանշանը: Զարթուցիչի որոշ հավելվածներ չեն կարող կիրառել այս հատկությունը:"</string>
     <string name="permlab_writeVoicemail" msgid="7309899891683938100">"գրել ձայնային փոստ"</string>
@@ -1130,8 +1130,8 @@
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Թույլ է տալիս հավելվածին ավելացնել հաղորդագրություններ ձեր ձայնային փոստի արկղում:"</string>
     <string name="permlab_readVoicemail" msgid="8415201752589140137">"կարդալ ձայնային փոստը"</string>
     <string name="permdesc_readVoicemail" msgid="8926534735321616550">"Ծրագրին թույլ է տալիս կարդալ ձեր ձայնային փոստը"</string>
-    <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"փոփոխել դիտարկչի աշխարհագրական տեղանքի թույլտվությունները"</string>
-    <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Թույլ է տալիս հավելվածին փոփոխել դիտարկչի աշխարհագրական դիրքի թույլտվությունները: Վնասարար հավելվածները կարող են օգտագործել սա` թույլատրելու ուղարկել տեղադրության վերաբերյալ տեղեկությունները կամայական վեբ կայքերին:"</string>
+    <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"փոփոխել դիտարկիչի աշխարհագրական տեղանքի թույլտվությունները"</string>
+    <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Թույլ է տալիս հավելվածին փոփոխել դիտարկիչի աշխարհագրական դիրքի թույլտվությունները: Վնասարար հավելվածները կարող են օգտագործել սա` թույլատրելու ուղարկել տեղադրության վերաբերյալ տեղեկությունները կամայական վեբ կայքերին:"</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"հաստատել փաթեթները"</string>
     <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Թույլ է տալիս հավելվածին հաստատել, որ փաթեթը տեղադրելի է:"</string>
     <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"միանալ փաթեթի ստուգիչին"</string>
diff --git a/core/res/res/values-mcc310-mnc260-zh-rCN/strings.xml b/core/res/res/values-mcc310-mnc260-zh-rCN/strings.xml
index 05e2d12..a359441 100644
--- a/core/res/res/values-mcc310-mnc260-zh-rCN/strings.xml
+++ b/core/res/res/values-mcc310-mnc260-zh-rCN/strings.xml
@@ -25,6 +25,5 @@
   <string-array name="wfcOperatorErrorMessages">
     <item msgid="931634632269046788">"WLAN 通话功能不可用。请与您的运营商联系,以便启用 WLAN 通话功能。"</item>
   </string-array>
-    <!-- no translation found for wfcSpnFormat (4982938551498609442) -->
-    <skip />
+    <string name="wfcSpnFormat" msgid="4982938551498609442">"%s WLAN 通话功能"</string>
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 2fd3c62..63dd5c7 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -126,8 +126,7 @@
     <string name="wfcRegErrorTitle" msgid="2301376280632110664">"WLAN 通话"</string>
   <string-array name="wfcOperatorErrorMessages">
   </string-array>
-    <!-- no translation found for wfcSpnFormat (8211621332478306568) -->
-    <skip />
+    <string name="wfcSpnFormat" msgid="8211621332478306568">"%s"</string>
     <string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:无法转接"</string>
     <string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
     <string name="cfTemplateForwardedTime" msgid="9206251736527085256">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>:<xliff:g id="TIME_DELAY">{2}</xliff:g>秒后<xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -748,28 +747,22 @@
     <string name="permdesc_manageFingerprint" msgid="178208705828055464">"允许该应用调用方法来添加和删除可用的指纹模板。"</string>
     <string name="permlab_useFingerprint" msgid="3150478619915124905">"使用指纹硬件"</string>
     <string name="permdesc_useFingerprint" msgid="9165097460730684114">"允许该应用使用指纹硬件进行身份验证"</string>
-    <!-- no translation found for fingerprint_acquired_partial (735082772341716043) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_insufficient (4596546021310923214) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_imager_dirty (1087209702421076105) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_fast (5303368850245663580) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_too_slow (7381891107120721078) -->
-    <skip />
-    <!-- no translation found for fingerprint_acquired_vendor:0 (2892952818207766996) -->
-    <!-- no translation found for fingerprint_error_unable_to_process (4232401562838100026) -->
-    <skip />
-    <!-- no translation found for fingerprint_error_hw_not_available (6162709753784993771) -->
-    <skip />
-    <!-- no translation found for fingerprint_error_no_space (1055819001126053318) -->
-    <skip />
-    <!-- no translation found for fingerprint_error_timeout (3927186043737732875) -->
-    <skip />
-    <!-- no translation found for fingerprint_error_vendor (3175724710791609491) -->
-    <skip />
-    <!-- no translation found for fingerprint_error_vendor:0 (5804600450373644614) -->
+    <string name="fingerprint_acquired_partial" msgid="735082772341716043">"仅检测到部分指纹,请重试。"</string>
+    <string name="fingerprint_acquired_insufficient" msgid="4596546021310923214">"无法处理指纹,请重试。"</string>
+    <string name="fingerprint_acquired_imager_dirty" msgid="1087209702421076105">"指纹传感器有脏污。请擦拭干净,然后重试。"</string>
+    <string name="fingerprint_acquired_too_fast" msgid="5303368850245663580">"手指移动太快,请重试。"</string>
+    <string name="fingerprint_acquired_too_slow" msgid="7381891107120721078">"手指移动太慢,请重试。"</string>
+  <string-array name="fingerprint_acquired_vendor">
+    <item msgid="2892952818207766996">"针对供应商的指纹获取错误消息 0"</item>
+  </string-array>
+    <string name="fingerprint_error_unable_to_process" msgid="4232401562838100026">"无法处理指纹,请重试。"</string>
+    <string name="fingerprint_error_hw_not_available" msgid="6162709753784993771">"硬件无法使用。"</string>
+    <string name="fingerprint_error_no_space" msgid="1055819001126053318">"无法存储指纹。请移除一个现有的指纹。"</string>
+    <string name="fingerprint_error_timeout" msgid="3927186043737732875">"指纹录入操作超时,请重试。"</string>
+    <string name="fingerprint_error_vendor" msgid="3175724710791609491">"指纹录入操作超时,请重试。"</string>
+  <string-array name="fingerprint_error_vendor">
+    <item msgid="5804600450373644614">"针对供应商的错误消息"</item>
+  </string-array>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"读取同步设置"</string>
     <string name="permdesc_readSyncSettings" msgid="2706745674569678644">"允许该应用读取某个帐户的同步设置。例如,此权限可确定“联系人”应用是否与某个帐户同步。"</string>
     <string name="permlab_writeSyncSettings" msgid="5408694875793945314">"启用和停用同步"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 79a6bde..dcb4b9e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4444,11 +4444,6 @@
     </declare-styleable>
 
     <declare-styleable name="DatePicker">
-        <!-- The first year (inclusive), for example "1940". {@deprecated Use minDate instead.} -->
-        <attr name="startYear" format="integer" />
-        <!-- The last year (inclusive), for example "2010". {@deprecated Use maxDate instead.} -->
-        <attr name="endYear" format="integer" />
-
         <!-- 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. -->
@@ -4474,12 +4469,14 @@
         <!-- The background for the selected date header. -->
         <attr name="headerBackground" />
 
-        <!-- The list year's text appearance in the list. -->
+        <!-- The list year's text appearance in the list.
+             {@deprecated Use yearListTextColor. }-->
         <attr name="yearListItemTextAppearance" format="reference" />
         <!-- @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" />
+
         <!-- 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. -->
@@ -4490,17 +4487,29 @@
             <enum name="calendar" value="2" />
         </attr>
 
-        <!-- @deprecated The text appearance for the month (ex. May) in the selected date header. -->
+        <!-- The first year (inclusive), for example "1940".
+             {@deprecated Use minDate instead.} -->
+        <attr name="startYear" format="integer" />
+        <!-- The last year (inclusive), for example "2010".
+             {@deprecated Use maxDate instead.} -->
+        <attr name="endYear" format="integer" />
+        <!-- The text appearance for the month (ex. May) in the selected date header.
+             {@deprecated Use headerTextColor instead.} -->
         <attr name="headerMonthTextAppearance" format="reference" />
-        <!-- @deprecated The text appearance for the day of month (ex. 28) in the selected date header. -->
+        <!-- The text appearance for the day of month (ex. 28) in the selected date header.
+             {@deprecated Use headerTextColor instead.} -->
         <attr name="headerDayOfMonthTextAppearance" format="reference" />
-        <!-- The text appearance for the year (ex. 2014) in the selected date header. -->
+        <!-- The text appearance for the year (ex. 2014) in the selected date header.
+             {@deprecated Use headerTextColor instead.} -->
         <attr name="headerYearTextAppearance" format="reference" />
-        <!-- @deprecated The background color for the header's day of week. -->
+        <!-- The background color for the header's day of week.
+             {@deprecated No longer displayed.} -->
         <attr name="dayOfWeekBackground" format="color" />
-        <!-- @deprecated The text color for the header's day of week. -->
+        <!-- The text color for the header's day of week.
+             {@deprecated No longer displayed.} -->
         <attr name="dayOfWeekTextAppearance" format="reference" />
-        <!-- @deprecated The list year's selected circle color in the list. -->
+        <!-- The list year's selected circle color in the list.
+             {@deprecated No longer displayed.} -->
         <attr name="yearListSelectorColor" format="color" />
     </declare-styleable>
 
@@ -4787,24 +4796,27 @@
         <attr name="legacyLayout" format="reference" />
         <!-- @hide The layout of the time picker. -->
         <attr name="internalLayout" />
-        <!-- The text appearance for the AM/PM header. -->
-        <attr name="headerAmPmTextAppearance" format="reference" />
-        <!-- The text appearance for the time header. -->
-        <attr name="headerTimeTextAppearance" format="reference" />
+
+        <!-- The text color for the selected time header text, ex. "12" or
+             "PM". This should be a color state list where the activated state
+             will be used when the minute picker or hour picker is active.-->
+        <attr name="headerTextColor" />
         <!-- The background for the header containing the currently selected time. -->
         <attr name="headerBackground" />
-        <!-- The color for the hours/minutes numbers. -->
+
+        <!-- The color for the hours/minutes numbers. This should be a color
+             state list where the activated state will be used when the number
+             is active.-->
         <attr name="numbersTextColor" format="color" />
-        <!-- The color for the inner hours numbers used in 24-hour mode. -->
+        <!-- The color for the inner hours numbers used in 24-hour mode. This
+             should be a color state list where the activated state will be
+             used when the number is active.-->
         <attr name="numbersInnerTextColor" format="color" />
         <!-- The background color for the hours/minutes numbers. -->
         <attr name="numbersBackgroundColor" format="color" />
-        <!-- The color for the AM/PM selectors. -->
-        <attr name="amPmTextColor" format="color" />
-        <!-- The background color state list for the AM/PM selectors. -->
-        <attr name="amPmBackgroundColor" format="color" />
         <!-- The color for the hours/minutes selector. -->
         <attr name="numbersSelectorColor" format="color" />
+
         <!-- Defines the look of the widget. Prior to the L release, the only choice was
              spinner. As of L, with the Material theme selected, the default layout is clock,
              but this attribute can be used to force spinner to be used instead. -->
@@ -4814,6 +4826,19 @@
             <!-- Time picker with clock face to select the time. -->
             <enum name="clock" value="2" />
         </attr>
+
+        <!-- The text appearance for the AM/PM header.
+             @deprecated Use headerTextColor instead. -->
+        <attr name="headerAmPmTextAppearance" format="reference" />
+        <!-- The text appearance for the time header.
+             @deprecated Use headerTextColor instead. -->
+        <attr name="headerTimeTextAppearance" format="reference" />
+        <!-- The color for the AM/PM selectors.
+             {@deprecated Use headerTextColor instead.}-->
+        <attr name="amPmTextColor" format="color" />
+        <!-- The background color state list for the AM/PM selectors.
+             {@deprecated Use headerBackground instead.}-->
+        <attr name="amPmBackgroundColor" format="color" />
     </declare-styleable>
 
     <!-- ========================= -->
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 283c237..b0b4e3a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1042,6 +1042,18 @@
          libraries in the apk must be stored and page-aligned.  -->
     <attr name="extractNativeLibs" format="boolean"/>
 
+    <!-- Specify whether an activity intent filter will need to be verified thru its set
+         of data URIs. This will only be used when the Intent's action is set to
+         {@link android.content.Intent#ACTION_VIEW Intent.ACTION_VIEW} and the Intent's category is
+         set to {@link android.content.Intent#CATEGORY_BROWSABLE Intent.CATEGORY_BROWSABLE} and the
+         intern filter data scheme is set to "http" or "https". When set to true, the intent filter
+         will need to use its data tag for getting the URIs to verify with.
+
+         For each URI, an HTTPS network request will be done to <code>/.well-known/associations.json</code>
+         host to verify that the web site is okay with the app intercepting the URI.
+         -->
+    <attr name="autoVerify" format="boolean" />
+
     <!-- The <code>manifest</code> tag is the root of an
          <code>AndroidManifest.xml</code> file,
          describing the contents of an Android package (.apk) file.  One
@@ -1840,6 +1852,7 @@
         <attr name="banner" />
         <attr name="logo" />
         <attr name="priority" />
+        <attr name="autoVerify" />
     </declare-styleable>
     
     <!-- Attributes that can be supplied in an AndroidManifest.xml
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 3431f35..7d08e7f 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -364,27 +364,26 @@
 
     <dimen name="resolver_max_width">480dp</dimen>
 
-     <!-- Size of the offset applied to the position of the circular mask. This is only
-     used on circular displays. In the case where there is no "chin", this will default
-     to 0 -->
-     <dimen name="circular_display_mask_offset">0px</dimen>
+    <!-- Size of the offset applied to the position of the circular mask. This
+         is only used on circular displays. In the case where there is no
+         "chin", this will default to 0 -->
+    <dimen name="circular_display_mask_offset">0px</dimen>
+    <!-- Amount to reduce the size of the circular mask by (to compensate for
+         aliasing effects). This is only used on circular displays. -->
+    <dimen name="circular_display_mask_thickness">1px</dimen>
 
-     <!-- Amount to reduce the size of the circular mask by (to compensate for aliasing
-     effects). This is only used on circular displays. -->
-     <dimen name="circular_display_mask_thickness">1px</dimen>
+    <dimen name="lock_pattern_dot_line_width">3dp</dimen>
+    <dimen name="lock_pattern_dot_size">12dp</dimen>
+    <dimen name="lock_pattern_dot_size_activated">28dp</dimen>
 
-     <dimen name="lock_pattern_dot_line_width">3dp</dimen>
-     <dimen name="lock_pattern_dot_size">12dp</dimen>
-     <dimen name="lock_pattern_dot_size_activated">28dp</dimen>
+    <dimen name="text_handle_min_size">40dp</dimen>
 
-     <dimen name="text_handle_min_size">40dp</dimen>
-
-     <!-- Lighting and shadow properties -->
-     <dimen name="light_y">-200dp</dimen>
-     <dimen name="light_z">800dp</dimen>
-     <dimen name="light_radius">600dp</dimen>
-     <item type="dimen" format="float" name="ambient_shadow_alpha">0.075</item>
-     <item type="dimen" format="float" name="spot_shadow_alpha">0.15</item>
+    <!-- Lighting and shadow properties -->
+    <dimen name="light_y">-200dp</dimen>
+    <dimen name="light_z">800dp</dimen>
+    <dimen name="light_radius">600dp</dimen>
+    <item type="dimen" format="float" name="ambient_shadow_alpha">0.075</item>
+    <item type="dimen" format="float" name="spot_shadow_alpha">0.15</item>
 
      <!-- Floating toolbar dimensions -->
      <dimen name="floating_toolbar_height">48dp</dimen>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index ef7bfaf..5c7daf2 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2653,4 +2653,8 @@
 
   <public type="attr" name="extractNativeLibs" />
   <public type="attr" name="usesCleartextTraffic" />
+
+  <!--IntentFilter auto verification -->
+  <public type="attr" name="autoVerify" />
+
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 88225bd..702510a 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3367,6 +3367,22 @@
     <string name="permdesc_bindPackageVerifier">Allows the holder to make requests of
         package verifiers. Should never be needed for normal apps.</string>
 
+    <!-- Title of an application permission which allows the application to verify whether
+         a different intent filter is able to be verified by some internal logic. [CHAR LIMIT=40] -->
+    <string name="permlab_intentFilterVerificationAgent">verify intent filter</string>
+    <!-- Description of an application permission which allows the application to verify whether
+         a different intent filter is able to be verified by some internal heuristic. [CHAR LIMIT=NONE] -->
+    <string name="permdesc_intentFilterVerificationAgent">Allows the app to check if an intent filter
+        is verified or not.</string>
+
+    <!-- Title of an application permission which allows the application to verify whether
+         a different intent filter is able to be verified by some internal logic. [CHAR LIMIT=40] -->
+    <string name="permlab_bindIntentFilterVerifier">bind to an intent filter verifier</string>
+    <!-- Description of an application permission which allows the application to verify whether
+         a different intent filter is able to be verified by some internal logic. [CHAR LIMIT=NONE] -->
+    <string name="permdesc_bindIntentFilterVerifier">Allows the holder to make requests of
+        intent filter verifiers. Should never be needed for normal apps.</string>
+
     <!-- Title of an application permission which allows the application to access serial ports via the SerialManager. [CHAR LIMIT=40] -->
     <string name="permlab_serialPort">access serial ports</string>
     <!-- Description of an application permission which allows the application access serial ports via the SerialManager. [CHAR LIMIT=NONE] -->
@@ -4415,6 +4431,10 @@
     <string name="date_picker_increment_year_button">Increase year</string>
     <!-- Description of the button to decrease the DatePicker's year value. [CHAR LIMIT=NONE] -->
     <string name="date_picker_decrement_year_button">Decrease year</string>
+    <!-- Description of the button to move to the previous month. [CHAR LIMIT=NONE] -->
+    <string name="date_picker_prev_month_button">Previous month</string>
+    <!-- Description of the button to move to the next month. [CHAR LIMIT=NONE] -->
+    <string name="date_picker_next_month_button">Next month</string>
 
     <!-- KeyboardView - accessibility support -->
     <!-- Description of the Alt button in a KeyboardView. [CHAR LIMIT=NONE] -->
@@ -5078,8 +5098,6 @@
     <string name="select_day">Select month and day</string>
     <!-- Accessibility announcement for the year picker [CHAR LIMIT=NONE] -->
     <string name="select_year">Select year</string>
-    <!-- Accessibility description for the item that is currently selected. -->
-    <string name="item_is_selected"><xliff:g id="item" example="2013">%1$s</xliff:g> selected</string>
     <!-- Accessibility announcement when a number that had been typed in is deleted [CHAR_LIMIT=NONE] -->
     <string name="deleted_key"><xliff:g id="key" example="4">%1$s</xliff:g> deleted</string>
 
diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml
index 223578d..23ac221 100644
--- a/core/res/res/values/styles_device_defaults.xml
+++ b/core/res/res/values/styles_device_defaults.xml
@@ -240,10 +240,6 @@
     <!-- @deprecated Action bars are now themed using the inheritable android:theme attribute. -->
     <style name="TextAppearance.DeviceDefault.Widget.ActionMode.Subtitle.Inverse" parent="TextAppearance.Material.Widget.ActionMode.Subtitle.Inverse"/>
     <style name="TextAppearance.DeviceDefault.Widget.ActionBar.Menu" parent="TextAppearance.Material.Widget.ActionBar.Menu"/>
-    <style name="TextAppearance.DeviceDefault.DatePicker.DayOfWeekLabel" parent="TextAppearance.Material.DatePicker.DayOfWeekLabel"/>
-    <style name="TextAppearance.DeviceDefault.DatePicker.MonthLabel" parent="TextAppearance.Material.DatePicker.MonthLabel"/>
-    <style name="TextAppearance.DeviceDefault.DatePicker.DayOfMonthLabel" parent="TextAppearance.Material.DatePicker.DayOfMonthLabel"/>
-    <style name="TextAppearance.DeviceDefault.DatePicker.YearLabel" parent="TextAppearance.Material.DatePicker.YearLabel"/>
 
     <!-- Preference Styles -->
     <style name="Preference.DeviceDefault" parent="Preference.Material"/>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index f1f7462..9cf7884 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -381,40 +381,22 @@
 
     <style name="TextAppearance.Material.TimePicker.TimeLabel" parent="TextAppearance.Material">
         <item name="textSize">@dimen/timepicker_time_label_size</item>
-        <item name="textColor">@color/time_picker_header_text_material</item>
+        <item name="textColor">@color/primary_text_secondary_when_activated_material_inverse</item>
     </style>
 
     <style name="TextAppearance.Material.TimePicker.AmPmLabel" parent="TextAppearance.Material.Button">
         <item name="textSize">@dimen/timepicker_ampm_label_size</item>
-        <item name="textColor">@color/time_picker_header_text_material</item>
-    </style>
-
-    <style name="TextAppearance.Material.DatePicker.DayOfWeekLabel" parent="TextAppearance.Material">
-        <item name="includeFontPadding">false</item>
-        <item name="textColor">?attr/textColorPrimaryInverse</item>
-        <item name="textSize">@dimen/datepicker_header_text_size</item>
-    </style>
-
-    <style name="TextAppearance.Material.DatePicker.MonthLabel" parent="TextAppearance.Material">
-        <item name="includeFontPadding">false</item>
-        <item name="textColor">@color/date_picker_header_text_material</item>
-        <item name="textSize">@dimen/datepicker_selected_date_month_size</item>
-    </style>
-
-    <style name="TextAppearance.Material.DatePicker.DayOfMonthLabel" parent="TextAppearance.Material">
-        <item name="includeFontPadding">false</item>
-        <item name="textColor">@color/date_picker_header_text_material</item>
-        <item name="textSize">@dimen/datepicker_selected_date_day_size</item>
+        <item name="textColor">@color/primary_text_secondary_when_activated_material_inverse</item>
     </style>
 
     <style name="TextAppearance.Material.DatePicker.YearLabel" parent="TextAppearance.Material">
-        <item name="textColor">@color/date_picker_header_text_material</item>
+        <item name="textColor">@color/primary_text_secondary_when_activated_material_inverse</item>
         <item name="textSize">@dimen/date_picker_year_label_size</item>
         <item name="fontFamily">sans-serif-medium</item>
     </style>
 
     <style name="TextAppearance.Material.DatePicker.DateLabel" parent="TextAppearance.Material">
-        <item name="textColor">@color/date_picker_header_text_material</item>
+        <item name="textColor">@color/primary_text_secondary_when_activated_material_inverse</item>
         <item name="textSize">@dimen/date_picker_date_label_size</item>
         <item name="fontFamily">sans-serif-medium</item>
     </style>
@@ -660,8 +642,7 @@
         <item name="legacyLayout">@layout/time_picker_legacy_material</item>
         <!-- Attributes for new-style TimePicker. -->
         <item name="internalLayout">@layout/time_picker_material</item>
-        <item name="headerTimeTextAppearance">@style/TextAppearance.Material.TimePicker.TimeLabel</item>
-        <item name="headerAmPmTextAppearance">@style/TextAppearance.Material.TimePicker.AmPmLabel</item>
+        <item name="headerTextColor">@color/primary_text_secondary_when_activated_material</item>
         <item name="headerBackground">#ff555555</item>
         <item name="numbersTextColor">?attr/textColorPrimaryActivated</item>
         <item name="numbersInnerTextColor">?attr/textColorSecondaryActivated</item>
@@ -676,8 +657,7 @@
         <item name="calendarViewShown">true</item>
         <!-- Attributes for new-style DatePicker. -->
         <item name="internalLayout">@layout/date_picker_material</item>
-        <item name="yearListItemTextAppearance">@style/TextAppearance.Material.DatePicker.List.YearLabel</item>
-        <item name="yearListItemActivatedTextAppearance">@style/TextAppearance.Material.DatePicker.List.YearLabel.Activated</item>
+        <item name="headerTextColor">@color/primary_text_secondary_when_activated_material</item>
         <item name="headerBackground">#ff555555</item>
     </style>
 
@@ -1033,11 +1013,13 @@
     <style name="Widget.Material.Light.NumberPicker" parent="Widget.Material.NumberPicker"/>
 
     <style name="Widget.Material.Light.TimePicker" parent="Widget.Material.TimePicker">
+        <item name="headerTextColor">@color/primary_text_secondary_when_activated_material_inverse</item>
         <item name="headerBackground">?attr/colorAccent</item>
         <item name="numbersBackgroundColor">#ffeeeeee</item>
     </style>
 
     <style name="Widget.Material.Light.DatePicker" parent="Widget.Material.DatePicker">
+        <item name="headerTextColor">@color/primary_text_secondary_when_activated_material_inverse</item>
         <item name="headerBackground">?attr/colorAccent</item>
     </style>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 67d54729..19352c9 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2040,7 +2040,6 @@
   <java-symbol type="integer" name="config_downtime_condition_lookahead_threshold_hrs" />
   <java-symbol type="string" name="muted_by" />
 
-  <java-symbol type="string" name="item_is_selected" />
   <java-symbol type="string" name="select_day" />
   <java-symbol type="string" name="select_year" />
 
@@ -2190,9 +2189,15 @@
   <java-symbol type="style" name="TextAppearance.Material.Widget.Calendar.Month" />
   <java-symbol type="style" name="TextAppearance.Material.Widget.Calendar.DayOfWeek" />
   <java-symbol type="style" name="TextAppearance.Material.Widget.Calendar.Day" />
+  <java-symbol type="style" name="TextAppearance.Material.DatePicker.List.YearLabel" />
+  <java-symbol type="style" name="TextAppearance.Material.DatePicker.List.YearLabel.Activated" />
   <java-symbol type="dimen" name="day_picker_padding_top"/>
   <java-symbol type="dimen" name="date_picker_day_of_week_height"/>
 
+  <java-symbol type="string" name="storage_internal" />
+  <java-symbol type="string" name="storage_sd_card" />
+  <java-symbol type="string" name="storage_usb" />
+
   <java-symbol type="id" name="accessibility_action_show_on_screen" />
 
   <!-- Floating toolbar -->
@@ -2205,4 +2210,11 @@
   <java-symbol type="dimen" name="floating_toolbar_menu_button_minimum_width" />
   <java-symbol type="dimen" name="floating_toolbar_default_width" />
   <java-symbol type="dimen" name="floating_toolbar_minimum_overflow_height" />
+
+  <java-symbol type="drawable" name="ic_chevron_left" />
+  <java-symbol type="drawable" name="ic_chevron_right" />
+  <java-symbol type="string" name="date_picker_prev_month_button" />
+  <java-symbol type="string" name="date_picker_next_month_button" />
+  <java-symbol type="layout" name="date_picker_month_item_material" />
+  <java-symbol type="id" name="month_view" />
 </resources>
diff --git a/docs/html/tools/revisions/build-tools.jd b/docs/html/tools/revisions/build-tools.jd
index b08fbcf..e8706c1 100644
--- a/docs/html/tools/revisions/build-tools.jd
+++ b/docs/html/tools/revisions/build-tools.jd
@@ -78,6 +78,19 @@
 <div class="toggle-content opened">
   <p><a href="#" onclick="return toggleContent(this)">
     <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+      alt=""/>Build Tools, Revision 22.0.1</a> <em>(March 2015)</em>
+  </p>
+  <div class="toggle-content-toggleme">
+    <p>Fixed compatibility issues with
+    <a href="{@docRoot}guide/topics/renderscript/compute.html">RenderScript</a> kernels on
+    Android 4.4 (API level 19) to Android 4.1 (API level 16) devices.</p>
+  </div>
+</div>
+
+
+<div class="toggle-content closed">
+  <p><a href="#" onclick="return toggleContent(this)">
+    <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
       alt=""/>Build Tools, Revision 22.0.0</a> <em>(March 2015)</em>
   </p>
   <div class="toggle-content-toggleme">
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 49c4247..ada8c12 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -388,6 +388,33 @@
     public static final int DEPTH_POINT_CLOUD = 0x101;
 
     /**
+     * Android private opaque image format.
+     * <p>
+     * The choices of the actual format and pixel data layout are entirely up to
+     * the device-specific and framework internal implementations, and may vary
+     * depending on use cases even for the same device. The buffers of this
+     * format can be produced by components like
+     * {@link android.media.ImageWriter ImageWriter} , and interpreted correctly
+     * by consumers like {@link android.hardware.camera2.CameraDevice
+     * CameraDevice} based on the device/framework private information. However,
+     * these buffers are not directly accessible to the application.
+     * </p>
+     * <p>
+     * When an {@link android.media.Image Image} of this format is obtained from
+     * an {@link android.media.ImageReader ImageReader} or
+     * {@link android.media.ImageWriter ImageWriter}, the
+     * {@link android.media.Image#getPlanes() getPlanes()} method will return an
+     * empty {@link android.media.Image.Plane Plane} array.
+     * </p>
+     * <p>
+     * If a buffer of this format is to be used as an OpenGL ES texture, the
+     * framework will assume that sampling the texture will always return an
+     * alpha value of 1.0 (i.e. the buffer contains only opaque pixel values).
+     * </p>
+     */
+    public static final int PRIVATE = 0x22;
+
+    /**
      * Use this function to retrieve the number of bits per pixel of an
      * ImageFormat.
      *
@@ -447,6 +474,7 @@
             case RAW10:
             case DEPTH16:
             case DEPTH_POINT_CLOUD:
+            case PRIVATE:
                 return true;
         }
 
diff --git a/include/android_runtime/android_view_Surface.h b/include/android_runtime/android_view_Surface.h
index a6836a8..ed83314 100644
--- a/include/android_runtime/android_view_Surface.h
+++ b/include/android_runtime/android_view_Surface.h
@@ -43,6 +43,7 @@
     NV21              = 0x11,
     YUY2              = 0x14,
     RAW_SENSOR        = 0x20,
+    PRIVATE           = 0x22,
     YUV_420_888       = 0x23,
     RAW10             = 0x25,
     JPEG              = 0x100,
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index f3eb317..1d16ca1 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -494,6 +494,19 @@
             args.addInt(KeymasterDefs.KM_TAG_DIGEST,
                     KeyStoreKeyConstraints.Digest.toKeymaster(digest));
         }
+        if (keyAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) {
+            if (digest == null) {
+                throw new IllegalStateException("Digest algorithm must be specified for key"
+                        + " algorithm " + keyAlgorithmString);
+            }
+            Integer digestOutputSizeBytes =
+                    KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest);
+            if (digestOutputSizeBytes != null) {
+                // TODO: Remove MAC length constraint once Keymaster API no longer requires it.
+                // TODO: Switch to bits instead of bytes, once this is fixed in Keymaster
+                args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes);
+            }
+        }
 
         @KeyStoreKeyConstraints.PurposeEnum int purposes = (params.getPurposes() != null)
                 ? params.getPurposes()
diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java
index 598bcd8..7313c48 100644
--- a/keystore/java/android/security/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/AndroidKeyStoreProvider.java
@@ -39,5 +39,9 @@
         // javax.crypto.KeyGenerator
         put("KeyGenerator.AES", KeyStoreKeyGeneratorSpi.AES.class.getName());
         put("KeyGenerator.HmacSHA256", KeyStoreKeyGeneratorSpi.HmacSHA256.class.getName());
+
+        // javax.crypto.Mac
+        put("Mac.HmacSHA256", KeyStoreHmacSpi.HmacSHA256.class.getName());
+        put("Mac.HmacSHA256 SupportedKeyClasses", KeyStoreSecretKey.class.getName());
     }
 }
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index f68b3f6..94a479b 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -476,4 +476,34 @@
             return SYSTEM_ERROR;
         }
     }
+
+    /**
+     * Check if the operation referenced by {@code token} is currently authorized.
+     *
+     * @param token An operation token returned by a call to {@link KeyStore.begin}.
+     */
+    public boolean isOperationAuthorized(IBinder token) {
+        try {
+            return mBinder.isOperationAuthorized(token);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return false;
+        }
+    }
+
+    /**
+     * Add an authentication record to the keystore authorization table.
+     *
+     * @param authToken The packed bytes of a hw_auth_token_t to be provided to keymaster.
+     * @return {@code KeyStore.NO_ERROR} on success, otherwise an error value corresponding to
+     * a {@code KeymasterDefs.KM_ERROR_} value or {@code KeyStore} ResponseCode.
+     */
+    public int addAuthToken(byte[] authToken) {
+        try {
+            return mBinder.addAuthToken(authToken);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return SYSTEM_ERROR;
+        }
+    }
 }
diff --git a/keystore/java/android/security/KeyStoreConnectException.java b/keystore/java/android/security/KeyStoreConnectException.java
new file mode 100644
index 0000000..4c465a4
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreConnectException.java
@@ -0,0 +1,12 @@
+package android.security;
+
+/**
+ * Indicates a communications error with keystore service.
+ *
+ * @hide
+ */
+public class KeyStoreConnectException extends CryptoOperationException {
+    public KeyStoreConnectException() {
+        super("Failed to communicate with keystore service");
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
new file mode 100644
index 0000000..a37ddce
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreCryptoOperationChunkedStreamer.java
@@ -0,0 +1,228 @@
+package android.security;
+
+import android.security.keymaster.OperationResult;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
+ * {@code update} and {@code finish} operations.
+ *
+ * <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's
+ * update and finish operations. Firstly, KeyStore's update and finish operations can consume only a
+ * limited amount of data in one go because the operations are marshalled via Binder. Secondly, the
+ * update operation may consume less data than provided, in which case the caller has to buffer
+ * the remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and
+ * {@link #doFinal(byte[], int, int) doFinal} operations which can be used to conveniently implement
+ * various JCA crypto primitives.
+ *
+ * <p>KeyStore operation through which data is streamed is abstracted away as
+ * {@link KeyStoreOperation} to avoid having this class deal with operation tokens and occasional
+ * additional parameters to update and final operations.
+ *
+ * @hide
+ */
+public class KeyStoreCryptoOperationChunkedStreamer {
+    public interface KeyStoreOperation {
+        /**
+         * Returns the result of the KeyStore update operation or null if keystore couldn't be
+         * reached.
+         */
+        OperationResult update(byte[] input);
+
+        /**
+         * Returns the result of the KeyStore finish operation or null if keystore couldn't be
+         * reached.
+         */
+        OperationResult finish(byte[] input);
+    }
+
+    // Binder buffer is about 1MB, but it's shared between all active transactions of the process.
+    // Thus, it's safer to use a much smaller upper bound.
+    private static final int DEFAULT_MAX_CHUNK_SIZE = 64 * 1024;
+    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+
+    private final KeyStoreOperation mKeyStoreOperation;
+    private final int mMaxChunkSize;
+
+    private byte[] mBuffered = EMPTY_BYTE_ARRAY;
+    private int mBufferedOffset;
+    private int mBufferedLength;
+
+    public KeyStoreCryptoOperationChunkedStreamer(KeyStoreOperation operation) {
+        this(operation, DEFAULT_MAX_CHUNK_SIZE);
+    }
+
+    public KeyStoreCryptoOperationChunkedStreamer(KeyStoreOperation operation, int maxChunkSize) {
+        mKeyStoreOperation = operation;
+        mMaxChunkSize = maxChunkSize;
+    }
+
+    public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeymasterException {
+        if (inputLength == 0) {
+            // No input provided
+            return EMPTY_BYTE_ARRAY;
+        }
+
+        ByteArrayOutputStream bufferedOutput = null;
+
+        while (inputLength > 0) {
+            byte[] chunk;
+            int inputBytesInChunk;
+            if ((mBufferedLength + inputLength) > mMaxChunkSize) {
+                // Too much input for one chunk -- extract one max-sized chunk and feed it into the
+                // update operation.
+                chunk = new byte[mMaxChunkSize];
+                System.arraycopy(mBuffered, mBufferedOffset, chunk, 0, mBufferedLength);
+                inputBytesInChunk = chunk.length - mBufferedLength;
+                System.arraycopy(input, inputOffset, chunk, mBufferedLength, inputBytesInChunk);
+            } else {
+                // All of available input fits into one chunk.
+                if ((mBufferedLength == 0) && (inputOffset == 0)
+                        && (inputLength == input.length)) {
+                    // Nothing buffered and all of input array needs to be fed into the update
+                    // operation.
+                    chunk = input;
+                    inputBytesInChunk = input.length;
+                } else {
+                    // Need to combine buffered data with input data into one array.
+                    chunk = new byte[mBufferedLength + inputLength];
+                    inputBytesInChunk = inputLength;
+                    System.arraycopy(mBuffered, mBufferedOffset, chunk, 0, mBufferedLength);
+                    System.arraycopy(input, inputOffset, chunk, mBufferedLength, inputLength);
+                }
+            }
+            // Update input array references to reflect that some of its bytes are now in mBuffered.
+            inputOffset += inputBytesInChunk;
+            inputLength -= inputBytesInChunk;
+
+            OperationResult opResult = mKeyStoreOperation.update(chunk);
+            if (opResult == null) {
+                throw new KeyStoreConnectException();
+            } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+                throw KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode);
+            }
+
+            if (opResult.inputConsumed == chunk.length) {
+                // The whole chunk was consumed
+                mBuffered = EMPTY_BYTE_ARRAY;
+                mBufferedOffset = 0;
+                mBufferedLength = 0;
+            } else if (opResult.inputConsumed == 0) {
+                // Nothing was consumed. More input needed.
+                if (inputLength > 0) {
+                    // More input is available, but it wasn't included into the previous chunk
+                    // because the chunk reached its maximum permitted size.
+                    // Shouldn't have happened.
+                    throw new CryptoOperationException("Nothing consumed from max-sized chunk: "
+                            + chunk.length + " bytes");
+                }
+                mBuffered = chunk;
+                mBufferedOffset = 0;
+                mBufferedLength = chunk.length;
+            } else if (opResult.inputConsumed < chunk.length) {
+                // The chunk was consumed only partially -- buffer the rest of the chunk
+                mBuffered = chunk;
+                mBufferedOffset = opResult.inputConsumed;
+                mBufferedLength = chunk.length - opResult.inputConsumed;
+            } else {
+                throw new CryptoOperationException("Consumed more than provided: "
+                        + opResult.inputConsumed + ", provided: " + chunk.length);
+            }
+
+            if ((opResult.output != null) && (opResult.output.length > 0)) {
+                if (inputLength > 0) {
+                    // More output might be produced in this loop -- buffer the current output
+                    if (bufferedOutput == null) {
+                        bufferedOutput = new ByteArrayOutputStream();
+                        try {
+                            bufferedOutput.write(opResult.output);
+                        } catch (IOException e) {
+                            throw new CryptoOperationException("Failed to buffer output", e);
+                        }
+                    }
+                } else {
+                    // No more output will be produced in this loop
+                    if (bufferedOutput == null) {
+                        // No previously buffered output
+                        return opResult.output;
+                    } else {
+                        // There was some previously buffered output
+                        try {
+                            bufferedOutput.write(opResult.output);
+                        } catch (IOException e) {
+                            throw new CryptoOperationException("Failed to buffer output", e);
+                        }
+                        return bufferedOutput.toByteArray();
+                    }
+                }
+            }
+        }
+
+        if (bufferedOutput == null) {
+            // No output produced
+            return EMPTY_BYTE_ARRAY;
+        } else {
+            return bufferedOutput.toByteArray();
+        }
+    }
+
+    public byte[] doFinal(byte[] input, int inputOffset, int inputLength)
+            throws KeymasterException {
+        if (inputLength == 0) {
+            // No input provided -- simplify the rest of the code
+            input = EMPTY_BYTE_ARRAY;
+            inputOffset = 0;
+        }
+
+        byte[] updateOutput = null;
+        if ((mBufferedLength + inputLength) > mMaxChunkSize) {
+            updateOutput = update(input, inputOffset, inputLength);
+            inputOffset += inputLength;
+            inputLength = 0;
+        }
+        // All of available input fits into one chunk.
+
+        byte[] finalChunk;
+        if ((mBufferedLength == 0) && (inputOffset == 0)
+                && (inputLength == input.length)) {
+            // Nothing buffered and all of input array needs to be fed into the finish operation.
+            finalChunk = input;
+        } else {
+            // Need to combine buffered data with input data into one array.
+            finalChunk = new byte[mBufferedLength + inputLength];
+            System.arraycopy(mBuffered, mBufferedOffset, finalChunk, 0, mBufferedLength);
+            System.arraycopy(input, inputOffset, finalChunk, mBufferedLength, inputLength);
+        }
+        mBuffered = EMPTY_BYTE_ARRAY;
+        mBufferedLength = 0;
+        mBufferedOffset = 0;
+
+        OperationResult opResult = mKeyStoreOperation.finish(finalChunk);
+        if (opResult == null) {
+            throw new KeyStoreConnectException();
+        } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+            throw KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode);
+        }
+
+        if (opResult.inputConsumed != finalChunk.length) {
+            throw new CryptoOperationException("Unexpected number of bytes consumed by finish: "
+                    + opResult.inputConsumed + " instead of " + finalChunk.length);
+        }
+
+        // Return the concatenation of the output of update and finish.
+        byte[] result;
+        byte[] finishOutput = opResult.output;
+        if ((updateOutput == null) || (updateOutput.length == 0)) {
+            result = finishOutput;
+        } else if ((finishOutput == null) || (finishOutput.length == 0)) {
+            result = updateOutput;
+        } else {
+            result = new byte[updateOutput.length + finishOutput.length];
+            System.arraycopy(updateOutput, 0, result, 0, updateOutput.length);
+            System.arraycopy(finishOutput, 0, result, updateOutput.length, finishOutput.length);
+        }
+        return (result != null) ? result : EMPTY_BYTE_ARRAY;
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreHmacSpi.java b/keystore/java/android/security/KeyStoreHmacSpi.java
new file mode 100644
index 0000000..3080d7b
--- /dev/null
+++ b/keystore/java/android/security/KeyStoreHmacSpi.java
@@ -0,0 +1,174 @@
+package android.security;
+
+import android.os.IBinder;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+import android.security.keymaster.OperationResult;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.MacSpi;
+
+/**
+ * {@link MacSpi} which provides HMAC implementations backed by Android KeyStore.
+ *
+ * @hide
+ */
+public abstract class KeyStoreHmacSpi extends MacSpi {
+
+    public static class HmacSHA256 extends KeyStoreHmacSpi {
+        public HmacSHA256() {
+            super(KeyStoreKeyConstraints.Digest.SHA256, 256 / 8);
+        }
+    }
+
+    private final KeyStore mKeyStore = KeyStore.getInstance();
+    private final @KeyStoreKeyConstraints.DigestEnum int mDigest;
+    private final int mMacSizeBytes;
+
+    private String mKeyAliasInKeyStore;
+
+    // The fields below are reset by the engineReset operation.
+    private KeyStoreCryptoOperationChunkedStreamer mChunkedStreamer;
+    private IBinder mOperationToken;
+
+    protected KeyStoreHmacSpi(@KeyStoreKeyConstraints.DigestEnum int digest, int macSizeBytes) {
+        mDigest = digest;
+        mMacSizeBytes = macSizeBytes;
+    }
+
+    @Override
+    protected int engineGetMacLength() {
+        return mMacSizeBytes;
+    }
+
+    @Override
+    protected void engineInit(Key key, AlgorithmParameterSpec params) throws InvalidKeyException,
+            InvalidAlgorithmParameterException {
+        if (key == null) {
+            throw new InvalidKeyException("key == null");
+        } else if (!(key instanceof KeyStoreSecretKey)) {
+            throw new InvalidKeyException(
+                    "Only Android KeyStore secret keys supported. Key: " + key);
+        }
+
+        if (params != null) {
+            throw new InvalidAlgorithmParameterException(
+                    "Unsupported algorithm parameters: " + params);
+        }
+
+        mKeyAliasInKeyStore = ((KeyStoreSecretKey) key).getAlias();
+        engineReset();
+    }
+
+    @Override
+    protected void engineReset() {
+        IBinder operationToken = mOperationToken;
+        if (operationToken != null) {
+            mOperationToken = null;
+            mKeyStore.abort(operationToken);
+        }
+        mChunkedStreamer = null;
+
+        KeymasterArguments keymasterArgs = new KeymasterArguments();
+        keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mDigest);
+
+        OperationResult opResult = mKeyStore.begin(mKeyAliasInKeyStore,
+                KeymasterDefs.KM_PURPOSE_SIGN,
+                true,
+                keymasterArgs,
+                null,
+                new KeymasterArguments());
+        if (opResult == null) {
+            throw new KeyStoreConnectException();
+        } else if (opResult.resultCode != KeyStore.NO_ERROR) {
+            throw new CryptoOperationException("Failed to start keystore operation",
+                    KeymasterUtils.getExceptionForKeymasterError(opResult.resultCode));
+        }
+        mOperationToken = opResult.token;
+        if (mOperationToken == null) {
+            throw new CryptoOperationException("Keystore returned null operation token");
+        }
+        mChunkedStreamer = new KeyStoreCryptoOperationChunkedStreamer(
+                new KeyStoreStreamingConsumer(mKeyStore, mOperationToken));
+    }
+
+    @Override
+    protected void engineUpdate(byte input) {
+        engineUpdate(new byte[] {input}, 0, 1);
+    }
+
+    @Override
+    protected void engineUpdate(byte[] input, int offset, int len) {
+        if (mChunkedStreamer == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+
+        byte[] output;
+        try {
+            output = mChunkedStreamer.update(input, offset, len);
+        } catch (KeymasterException e) {
+            throw new CryptoOperationException("Keystore operation failed", e);
+        }
+        if ((output != null) && (output.length != 0)) {
+            throw new CryptoOperationException("Update operation unexpectedly produced output");
+        }
+    }
+
+    @Override
+    protected byte[] engineDoFinal() {
+        if (mChunkedStreamer == null) {
+            throw new IllegalStateException("Not initialized");
+        }
+
+        byte[] result;
+        try {
+            result = mChunkedStreamer.doFinal(null, 0, 0);
+        } catch (KeymasterException e) {
+            throw new CryptoOperationException("Keystore operation failed", e);
+        }
+
+        engineReset();
+        return result;
+    }
+
+    @Override
+    public void finalize() throws Throwable {
+        try {
+            IBinder operationToken = mOperationToken;
+            if (operationToken != null) {
+                mOperationToken = null;
+                mKeyStore.abort(operationToken);
+            }
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * KeyStore-backed consumer of {@code MacSpi}'s chunked stream.
+     */
+    private static class KeyStoreStreamingConsumer
+            implements KeyStoreCryptoOperationChunkedStreamer.KeyStoreOperation {
+        private final KeyStore mKeyStore;
+        private final IBinder mOperationToken;
+
+        private KeyStoreStreamingConsumer(KeyStore keyStore, IBinder operationToken) {
+            mKeyStore = keyStore;
+            mOperationToken = operationToken;
+        }
+
+        @Override
+        public OperationResult update(byte[] input) {
+            return mKeyStore.update(mOperationToken, null, input);
+        }
+
+        @Override
+        public OperationResult finish(byte[] input) {
+            return mKeyStore.finish(mOperationToken, null, input);
+        }
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java
index 47bb1cc..b5e2436 100644
--- a/keystore/java/android/security/KeyStoreKeyConstraints.java
+++ b/keystore/java/android/security/KeyStoreKeyConstraints.java
@@ -401,6 +401,20 @@
                     throw new IllegalArgumentException("Unknown digest: " + digest);
             }
         }
+
+        /**
+         * @hide
+         */
+        public static Integer getOutputSizeBytes(@DigestEnum int digest) {
+            switch (digest) {
+                case NONE:
+                    return null;
+                case SHA256:
+                    return 256 / 8;
+                default:
+                    throw new IllegalArgumentException("Unknown digest: " + digest);
+            }
+        }
     }
 
     @Retention(RetentionPolicy.SOURCE)
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
index 86950dd..f1f9436 100644
--- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -28,7 +28,8 @@
         public HmacSHA256() {
             super(KeyStoreKeyConstraints.Algorithm.HMAC,
                     KeyStoreKeyConstraints.Digest.SHA256,
-                    256);
+                    KeyStoreKeyConstraints.Digest.getOutputSizeBytes(
+                            KeyStoreKeyConstraints.Digest.SHA256) * 8);
         }
     }
 
@@ -76,6 +77,19 @@
             args.addInt(KeymasterDefs.KM_TAG_DIGEST,
                     KeyStoreKeyConstraints.Digest.toKeymaster(mDigest));
         }
+        if (mAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) {
+            if (mDigest == null) {
+                throw new IllegalStateException("Digest algorithm must be specified for key"
+                        + " algorithm " + KeyStoreKeyConstraints.Algorithm.toString(mAlgorithm));
+            }
+            Integer digestOutputSizeBytes =
+                    KeyStoreKeyConstraints.Digest.getOutputSizeBytes(mDigest);
+            if (digestOutputSizeBytes != null) {
+                // TODO: Remove MAC length constraint once Keymaster API no longer requires it.
+                // TODO: Switch to bits instead of bytes, once this is fixed in Keymaster
+                args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes);
+            }
+        }
         int keySizeBits = (spec.getKeySize() != null) ? spec.getKeySize() : mDefaultKeySizeBits;
         args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits);
         @KeyStoreKeyConstraints.PurposeEnum int purposes = (spec.getPurposes() != null)
diff --git a/keystore/java/android/security/KeymasterException.java b/keystore/java/android/security/KeymasterException.java
index 4ff7115..bc8198f 100644
--- a/keystore/java/android/security/KeymasterException.java
+++ b/keystore/java/android/security/KeymasterException.java
@@ -7,7 +7,14 @@
  */
 public class KeymasterException extends Exception {
 
-    public KeymasterException(String message) {
+    private final int mErrorCode;
+
+    public KeymasterException(int errorCode, String message) {
         super(message);
+        mErrorCode = errorCode;
+    }
+
+    public int getErrorCode() {
+        return mErrorCode;
     }
 }
diff --git a/keystore/java/android/security/KeymasterUtils.java b/keystore/java/android/security/KeymasterUtils.java
index e6e88c7..4f17586 100644
--- a/keystore/java/android/security/KeymasterUtils.java
+++ b/keystore/java/android/security/KeymasterUtils.java
@@ -13,9 +13,11 @@
             case KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT:
                 // The name of this parameter significantly differs between Keymaster and framework
                 // APIs. Use the framework wording to make life easier for developers.
-                return new KeymasterException("Invalid user authentication validity duration");
+                return new KeymasterException(keymasterErrorCode,
+                        "Invalid user authentication validity duration");
             default:
-                return new KeymasterException(KeymasterDefs.getErrorMessage(keymasterErrorCode));
+                return new KeymasterException(keymasterErrorCode,
+                        KeymasterDefs.getErrorMessage(keymasterErrorCode));
         }
     }
 }
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 7df61f27..48f5dc1 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -16,8 +16,12 @@
 #include "JankTracker.h"
 
 #include <algorithm>
+#include <cutils/ashmem.h>
+#include <cutils/log.h>
 #include <cstdio>
+#include <errno.h>
 #include <inttypes.h>
+#include <sys/mman.h>
 
 namespace android {
 namespace uirenderer {
@@ -63,11 +67,114 @@
         = FrameInfoFlags::kWindowLayoutChanged
         | FrameInfoFlags::kSurfaceCanvas;
 
+// The bucketing algorithm controls so to speak
+// If a frame is <= to this it goes in bucket 0
+static const uint32_t kBucketMinThreshold = 7;
+// If a frame is > this, start counting in increments of 2ms
+static const uint32_t kBucket2msIntervals = 32;
+// If a frame is > this, start counting in increments of 4ms
+static const uint32_t kBucket4msIntervals = 48;
+
+// This will be called every frame, performance sensitive
+// Uses bit twiddling to avoid branching while achieving the packing desired
+static uint32_t frameCountIndexForFrameTime(nsecs_t frameTime, uint32_t max) {
+    uint32_t index = static_cast<uint32_t>(ns2ms(frameTime));
+    // If index > kBucketMinThreshold mask will be 0xFFFFFFFF as a result
+    // of negating 1 (twos compliment, yaay) else mask will be 0
+    uint32_t mask = -(index > kBucketMinThreshold);
+    // If index > threshold, this will essentially perform:
+    // amountAboveThreshold = index - threshold;
+    // index = threshold + (amountAboveThreshold / 2)
+    // However if index is <= this will do nothing. It will underflow, do
+    // a right shift by 0 (no-op), then overflow back to the original value
+    index = ((index - kBucket4msIntervals) >> (index > kBucket4msIntervals))
+            + kBucket4msIntervals;
+    index = ((index - kBucket2msIntervals) >> (index > kBucket2msIntervals))
+            + kBucket2msIntervals;
+    // If index was < minThreshold at the start of all this it's going to
+    // be a pretty garbage value right now. However, mask is 0 so we'll end
+    // up with the desired result of 0.
+    index = (index - kBucketMinThreshold) & mask;
+    return index < max ? index : max;
+}
+
+// Only called when dumping stats, less performance sensitive
+static uint32_t frameTimeForFrameCountIndex(uint32_t index) {
+    index = index + kBucketMinThreshold;
+    if (index > kBucket2msIntervals) {
+        index += (index - kBucket2msIntervals);
+    }
+    if (index > kBucket4msIntervals) {
+        // This works because it was already doubled by the above if
+        // 1 is added to shift slightly more towards the middle of the bucket
+        index += (index - kBucket4msIntervals) + 1;
+    }
+    return index;
+}
+
 JankTracker::JankTracker(nsecs_t frameIntervalNanos) {
+    // By default this will use malloc memory. It may be moved later to ashmem
+    // if there is shared space for it and a request comes in to do that.
+    mData = new ProfileData;
     reset();
     setFrameInterval(frameIntervalNanos);
 }
 
+JankTracker::~JankTracker() {
+    freeData();
+}
+
+void JankTracker::freeData() {
+    if (mIsMapped) {
+        munmap(mData, sizeof(ProfileData));
+    } else {
+        delete mData;
+    }
+    mIsMapped = false;
+    mData = nullptr;
+}
+
+void JankTracker::switchStorageToAshmem(int ashmemfd) {
+    int regionSize = ashmem_get_size_region(ashmemfd);
+    if (regionSize < static_cast<int>(sizeof(ProfileData))) {
+        ALOGW("Ashmem region is too small! Received %d, required %u",
+                regionSize, sizeof(ProfileData));
+        return;
+    }
+    ProfileData* newData = reinterpret_cast<ProfileData*>(
+            mmap(NULL, sizeof(ProfileData), PROT_READ | PROT_WRITE,
+            MAP_SHARED, ashmemfd, 0));
+    if (newData == MAP_FAILED) {
+        int err = errno;
+        ALOGW("Failed to move profile data to ashmem fd %d, error = %d",
+                ashmemfd, err);
+        return;
+    }
+
+    // The new buffer may have historical data that we want to build on top of
+    // But let's make sure we don't overflow Just In Case
+    uint32_t divider = 0;
+    if (newData->totalFrameCount > (1 << 24)) {
+        divider = 4;
+    }
+    for (size_t i = 0; i < mData->jankTypeCounts.size(); i++) {
+        newData->jankTypeCounts[i] >>= divider;
+        newData->jankTypeCounts[i] += mData->jankTypeCounts[i];
+    }
+    for (size_t i = 0; i < mData->frameCounts.size(); i++) {
+        newData->frameCounts[i] >>= divider;
+        newData->frameCounts[i] += mData->frameCounts[i];
+    }
+    newData->jankFrameCount >>= divider;
+    newData->jankFrameCount += mData->jankFrameCount;
+    newData->totalFrameCount >>= divider;
+    newData->totalFrameCount += mData->totalFrameCount;
+
+    freeData();
+    mData = newData;
+    mIsMapped = true;
+}
+
 void JankTracker::setFrameInterval(nsecs_t frameInterval) {
     mFrameInterval = frameInterval;
     mThresholds[kMissedVsync] = 1;
@@ -92,16 +199,15 @@
 }
 
 void JankTracker::addFrame(const FrameInfo& frame) {
-    mTotalFrameCount++;
+    mData->totalFrameCount++;
     // Fast-path for jank-free frames
     int64_t totalDuration =
             frame[FrameInfoIndex::kFrameCompleted] - frame[FrameInfoIndex::kIntendedVsync];
-    uint32_t framebucket = std::min(
-            static_cast<typeof mFrameCounts.size()>(ns2ms(totalDuration)),
-            mFrameCounts.size());
+    uint32_t framebucket = frameCountIndexForFrameTime(
+            totalDuration, mData->frameCounts.size());
     // Keep the fast path as fast as possible.
     if (CC_LIKELY(totalDuration < mFrameInterval)) {
-        mFrameCounts[framebucket]++;
+        mData->frameCounts[framebucket]++;
         return;
     }
 
@@ -109,47 +215,52 @@
         return;
     }
 
-    mFrameCounts[framebucket]++;
-    mJankFrameCount++;
+    mData->frameCounts[framebucket]++;
+    mData->jankFrameCount++;
 
     for (int i = 0; i < NUM_BUCKETS; i++) {
         int64_t delta = frame[COMPARISONS[i].end] - frame[COMPARISONS[i].start];
         if (delta >= mThresholds[i] && delta < IGNORE_EXCEEDING) {
-            mBuckets[i].count++;
+            mData->jankTypeCounts[i]++;
         }
     }
 }
 
-void JankTracker::dump(int fd) {
-    FILE* file = fdopen(fd, "a");
-    fprintf(file, "\nFrame stats:");
-    fprintf(file, "\n  Total frames rendered: %u", mTotalFrameCount);
-    fprintf(file, "\n  Janky frames: %u (%.2f%%)", mJankFrameCount,
-            (float) mJankFrameCount / (float) mTotalFrameCount * 100.0f);
-    fprintf(file, "\n  90th percentile: %ums", findPercentile(90));
-    fprintf(file, "\n  95th percentile: %ums", findPercentile(95));
-    fprintf(file, "\n  99th percentile: %ums", findPercentile(99));
-    for (int i = 0; i < NUM_BUCKETS; i++) {
-        fprintf(file, "\n   Number %s: %u", JANK_TYPE_NAMES[i], mBuckets[i].count);
+void JankTracker::dumpBuffer(const void* buffer, size_t bufsize, int fd) {
+    if (bufsize < sizeof(ProfileData)) {
+        return;
     }
-    fprintf(file, "\n");
-    fflush(file);
+    const ProfileData* data = reinterpret_cast<const ProfileData*>(buffer);
+    dumpData(data, fd);
+}
+
+void JankTracker::dumpData(const ProfileData* data, int fd) {
+    dprintf(fd, "\nTotal frames rendered: %u", data->totalFrameCount);
+    dprintf(fd, "\nJanky frames: %u (%.2f%%)", data->jankFrameCount,
+            (float) data->jankFrameCount / (float) data->totalFrameCount * 100.0f);
+    dprintf(fd, "\n90th percentile: %ums", findPercentile(data, 90));
+    dprintf(fd, "\n95th percentile: %ums", findPercentile(data, 95));
+    dprintf(fd, "\n99th percentile: %ums", findPercentile(data, 99));
+    for (int i = 0; i < NUM_BUCKETS; i++) {
+        dprintf(fd, "\nNumber %s: %u", JANK_TYPE_NAMES[i], data->jankTypeCounts[i]);
+    }
+    dprintf(fd, "\n");
 }
 
 void JankTracker::reset() {
-    mBuckets.fill({0});
-    mFrameCounts.fill(0);
-    mTotalFrameCount = 0;
-    mJankFrameCount = 0;
+    mData->jankTypeCounts.fill(0);
+    mData->frameCounts.fill(0);
+    mData->totalFrameCount = 0;
+    mData->jankFrameCount = 0;
 }
 
-uint32_t JankTracker::findPercentile(int percentile) {
-    int pos = percentile * mTotalFrameCount / 100;
-    int remaining = mTotalFrameCount - pos;
-    for (int i = mFrameCounts.size() - 1; i >= 0; i--) {
-        remaining -= mFrameCounts[i];
+uint32_t JankTracker::findPercentile(const ProfileData* data, int percentile) {
+    int pos = percentile * data->totalFrameCount / 100;
+    int remaining = data->totalFrameCount - pos;
+    for (int i = data->frameCounts.size() - 1; i >= 0; i--) {
+        remaining -= data->frameCounts[i];
         if (remaining <= 0) {
-            return i;
+            return frameTimeForFrameCountIndex(i);
         }
     }
     return 0;
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index ae339ec..4783001 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -20,6 +20,8 @@
 #include "renderthread/TimeLord.h"
 #include "utils/RingBuffer.h"
 
+#include <cutils/compiler.h>
+
 #include <array>
 #include <memory>
 
@@ -37,33 +39,45 @@
     NUM_BUCKETS,
 };
 
-struct JankBucket {
-    // Number of frames that hit this bucket
-    uint32_t count;
+// Try to keep as small as possible, should match ASHMEM_SIZE in
+// GraphicsStatsService.java
+struct ProfileData {
+    std::array<uint32_t, NUM_BUCKETS> jankTypeCounts;
+    // See comments on kBucket* constants for what this holds
+    std::array<uint32_t, 57> frameCounts;
+
+    uint32_t totalFrameCount;
+    uint32_t jankFrameCount;
 };
 
 // TODO: Replace DrawProfiler with this
 class JankTracker {
 public:
     JankTracker(nsecs_t frameIntervalNanos);
-
-    void setFrameInterval(nsecs_t frameIntervalNanos);
+    ~JankTracker();
 
     void addFrame(const FrameInfo& frame);
 
-    void dump(int fd);
+    void dump(int fd) { dumpData(mData, fd); }
     void reset();
 
+    void switchStorageToAshmem(int ashmemfd);
+
+    uint32_t findPercentile(int p) { return findPercentile(mData, p); }
+
+    ANDROID_API static void dumpBuffer(const void* buffer, size_t bufsize, int fd);
+
 private:
-    uint32_t findPercentile(int p);
+    void freeData();
+    void setFrameInterval(nsecs_t frameIntervalNanos);
 
-    std::array<JankBucket, NUM_BUCKETS> mBuckets;
+    static uint32_t findPercentile(const ProfileData* data, int p);
+    static void dumpData(const ProfileData* data, int fd);
+
     std::array<int64_t, NUM_BUCKETS> mThresholds;
-    std::array<uint32_t, 128> mFrameCounts;
-
     int64_t mFrameInterval;
-    uint32_t mTotalFrameCount;
-    uint32_t mJankFrameCount;
+    ProfileData* mData;
+    bool mIsMapped = false;
 };
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 3781969..02fbd89 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -858,6 +858,8 @@
                 .setModelViewMapUnitToRectOptionalSnap(snap, rect)
                 .setRoundRectClipState(currentSnapshot()->roundRectClipState)
                 .build();
+        renderGlop(glop);
+        return;
     }
 
     float alpha = getLayerAlpha(layer);
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 9456073..9237151 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -41,15 +41,10 @@
         RenderNode* rootRenderNode, IContextFactory* contextFactory)
         : mRenderThread(thread)
         , mEglManager(thread.eglManager())
-        , mEglSurface(EGL_NO_SURFACE)
-        , mBufferPreserved(false)
-        , mSwapBehavior(kSwap_default)
         , mOpaque(!translucent)
-        , mCanvas(nullptr)
-        , mHaveNewSurface(false)
         , mAnimationContext(contextFactory->createAnimationContext(mRenderThread.timeLord()))
         , mRootRenderNode(rootRenderNode)
-        , mCurrentFrameInfo(nullptr) {
+        , mJankTracker(thread.timeLord().frameIntervalNanos()) {
     mRenderThread.renderState().registerCanvasContext(this);
     mProfiler.setDensity(mRenderThread.mainDisplayInfo().density);
 }
@@ -258,6 +253,7 @@
 
     // TODO: Use a fence for real completion?
     mCurrentFrameInfo->markFrameCompleted();
+    mJankTracker.addFrame(*mCurrentFrameInfo);
     mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo);
     profiler().finishFrame();
 }
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index c3904c2..f5f1f54 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -127,23 +127,24 @@
     RenderThread& mRenderThread;
     EglManager& mEglManager;
     sp<ANativeWindow> mNativeWindow;
-    EGLSurface mEglSurface;
-    bool mBufferPreserved;
-    SwapBehavior mSwapBehavior;
+    EGLSurface mEglSurface = EGL_NO_SURFACE;
+    bool mBufferPreserved = false;
+    SwapBehavior mSwapBehavior = kSwap_default;
 
     bool mOpaque;
-    OpenGLRenderer* mCanvas;
-    bool mHaveNewSurface;
+    OpenGLRenderer* mCanvas = nullptr;
+    bool mHaveNewSurface = false;
     DamageAccumulator mDamageAccumulator;
     std::unique_ptr<AnimationContext> mAnimationContext;
 
     const sp<RenderNode> mRootRenderNode;
 
     DrawProfiler mProfiler;
-    FrameInfo* mCurrentFrameInfo;
+    FrameInfo* mCurrentFrameInfo = nullptr;
     // Ring buffer large enough for 1 second worth of frames
     RingBuffer<FrameInfo, 60> mFrames;
     std::string mName;
+    JankTracker mJankTracker;
 
     std::set<RenderNode*> mPrefetechedLayers;
 };
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 0091790..cc87241 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -441,6 +441,19 @@
     post(task);
 }
 
+CREATE_BRIDGE2(setProcessStatsBuffer, RenderThread* thread, int fd) {
+    args->thread->jankTracker().switchStorageToAshmem(args->fd);
+    close(args->fd);
+    return nullptr;
+}
+
+void RenderProxy::setProcessStatsBuffer(int fd) {
+    SETUP_TASK(setProcessStatsBuffer);
+    args->thread = &mRenderThread;
+    args->fd = dup(fd);
+    post(task);
+}
+
 void RenderProxy::post(RenderTask* task) {
     mRenderThread.queue(task);
 }
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 43cbe07..29c6f08 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -99,6 +99,7 @@
     ANDROID_API static void dumpGraphicsMemory(int fd);
 
     ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size);
+    ANDROID_API void setProcessStatsBuffer(int fd);
 
 private:
     RenderThread& mRenderThread;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index d77fcd8..83954ae 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2602,15 +2602,21 @@
                 return;
 
             case MEDIA_STOPPED:
-                if (mTimeProvider != null) {
-                    mTimeProvider.onStopped();
+                {
+                    TimeProvider timeProvider = mTimeProvider;
+                    if (timeProvider != null) {
+                        timeProvider.onStopped();
+                    }
                 }
                 break;
 
             case MEDIA_STARTED:
             case MEDIA_PAUSED:
-                if (mTimeProvider != null) {
-                    mTimeProvider.onPaused(msg.what == MEDIA_PAUSED);
+                {
+                    TimeProvider timeProvider = mTimeProvider;
+                    if (timeProvider != null) {
+                        timeProvider.onPaused(msg.what == MEDIA_PAUSED);
+                    }
                 }
                 break;
 
@@ -2620,21 +2626,26 @@
                 return;
 
             case MEDIA_SEEK_COMPLETE:
-              if (mOnSeekCompleteListener != null) {
-                  mOnSeekCompleteListener.onSeekComplete(mMediaPlayer);
-              }
-              // fall through
+                if (mOnSeekCompleteListener != null) {
+                    mOnSeekCompleteListener.onSeekComplete(mMediaPlayer);
+                }
+                // fall through
 
             case MEDIA_SKIPPED:
-              if (mTimeProvider != null) {
-                  mTimeProvider.onSeekComplete(mMediaPlayer);
-              }
-              return;
+                {
+                    TimeProvider timeProvider = mTimeProvider;
+                    if (timeProvider != null) {
+                        timeProvider.onSeekComplete(mMediaPlayer);
+                    }
+                }
+                return;
 
             case MEDIA_SET_VIDEO_SIZE:
-              if (mOnVideoSizeChangedListener != null)
-                  mOnVideoSizeChangedListener.onVideoSizeChanged(mMediaPlayer, msg.arg1, msg.arg2);
-              return;
+                if (mOnVideoSizeChangedListener != null) {
+                    mOnVideoSizeChangedListener.onVideoSizeChanged(
+                        mMediaPlayer, msg.arg1, msg.arg2);
+                }
+                return;
 
             case MEDIA_ERROR:
                 Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 1255276..9ea6722 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -553,15 +553,8 @@
                         boolean isimage = MediaFile.isImageFileType(mFileType);
 
                         if (isaudio || isvideo || isimage) {
-                            if (mExternalIsEmulated && path.startsWith(mExternalStoragePath)) {
-                                // try to rewrite the path to bypass the sd card fuse layer
-                                String directPath = Environment.getMediaStorageDirectory() +
-                                        path.substring(mExternalStoragePath.length());
-                                File f = new File(directPath);
-                                if (f.exists()) {
-                                    path = directPath;
-                                }
-                            }
+                            path = Environment.maybeTranslateEmulatedPathToInternal(new File(path))
+                                    .getAbsolutePath();
                         }
 
                         // we only extract metadata for audio and video files
diff --git a/media/java/android/media/midi/MidiDeviceServer.java b/media/java/android/media/midi/MidiDeviceServer.java
index d27351f..bc85f92 100644
--- a/media/java/android/media/midi/MidiDeviceServer.java
+++ b/media/java/android/media/midi/MidiDeviceServer.java
@@ -24,6 +24,8 @@
 import android.system.OsConstants;
 import android.util.Log;
 
+import com.android.internal.midi.MidiDispatcher;
+
 import dalvik.system.CloseGuard;
 
 import libcore.io.IoUtils;
diff --git a/media/java/android/media/midi/MidiOutputPort.java b/media/java/android/media/midi/MidiOutputPort.java
index b8ed36f..0290a76 100644
--- a/media/java/android/media/midi/MidiOutputPort.java
+++ b/media/java/android/media/midi/MidiOutputPort.java
@@ -21,6 +21,8 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.internal.midi.MidiDispatcher;
+
 import dalvik.system.CloseGuard;
 
 import libcore.io.IoUtils;
diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java
index e20eabc..7b8102b 100644
--- a/media/java/android/mtp/MtpStorage.java
+++ b/media/java/android/mtp/MtpStorage.java
@@ -59,7 +59,7 @@
      *
      * @return the storage ID
      */
-    public static int getStorageId(int index) {
+    public static int getStorageIdForIndex(int index) {
         // storage ID is 0x00010001 for primary storage,
         // then 0x00020001, 0x00030001, etc. for secondary storages
         return ((index + 1) << 16) + 1;
diff --git a/packages/IntentFilterVerifier/Android.mk b/packages/IntentFilterVerifier/Android.mk
new file mode 100644
index 0000000..99feda5
--- /dev/null
+++ b/packages/IntentFilterVerifier/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH:= $(call my-dir)
+
+# Build the IntentFilterVerifier.
+include $(CLEAR_VARS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+        volley \
+
+LOCAL_JAVA_LIBRARIES += org.apache.http.legacy
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := IntentFilterVerifier
+
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_PROGUARD_FLAGS := $(proguard.flags)
+
+include $(BUILD_PACKAGE)
+
+# Build the test package.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/IntentFilterVerifier/AndroidManifest.xml b/packages/IntentFilterVerifier/AndroidManifest.xml
new file mode 100644
index 0000000..3829cc5
--- /dev/null
+++ b/packages/IntentFilterVerifier/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.verifier.intentfilter"
+        coreApp="true">
+    <uses-permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+
+    <application
+            android:label="@string/service_name"
+            android:allowBackup="false">
+
+        <receiver
+                android:name="com.android.verifier.intentfilter.IntentVerificationReceiver"
+                android:permission="android.permission.BIND_INTENT_FILTER_VERIFIER" >
+            <intent-filter
+                    android:priority="-1" >
+                <action android:name="android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION" />
+                <data android:mimeType="application/vnd.android.package-archive" />
+            </intent-filter>
+        </receiver>
+
+        <service android:name=".IntentVerificationService"
+                 android:label="@string/service_name"
+                 android:exported="false"/>
+
+    </application>
+
+</manifest>
diff --git a/packages/IntentFilterVerifier/CleanSpec.mk b/packages/IntentFilterVerifier/CleanSpec.mk
new file mode 100644
index 0000000..e4575ae
--- /dev/null
+++ b/packages/IntentFilterVerifier/CleanSpec.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# *****************************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THE BANNER
+# *****************************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# ******************************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
+# ******************************************************************
diff --git a/packages/IntentFilterVerifier/proguard.flags b/packages/IntentFilterVerifier/proguard.flags
new file mode 100644
index 0000000..6e4bec3
--- /dev/null
+++ b/packages/IntentFilterVerifier/proguard.flags
@@ -0,0 +1 @@
+-verbose
\ No newline at end of file
diff --git a/packages/IntentFilterVerifier/res/values/strings.xml b/packages/IntentFilterVerifier/res/values/strings.xml
new file mode 100644
index 0000000..22f3cd5
--- /dev/null
+++ b/packages/IntentFilterVerifier/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Package name shown to users when they look at installed applications
+         and running processes. This service verifies packages that are
+         requested to be installed. [CHAR LIMIT=50] -->
+    <string name="service_name">Basic Intent Filter Verification Service</string>
+
+</resources>
diff --git a/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationReceiver.java b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationReceiver.java
new file mode 100644
index 0000000..de25f8c
--- /dev/null
+++ b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationReceiver.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.verifier.intentfilter;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.Slog;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class IntentVerificationReceiver extends BroadcastReceiver {
+    static final String TAG = IntentVerificationReceiver.class.getName();
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        if (Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION.equals(action)) {
+            Bundle extras = intent.getExtras();
+            if (extras != null) {
+                int verificationId = extras.getInt(
+                        PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID);
+                String hosts = extras.getString(
+                        PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS);
+
+                Log.d(TAG, "Received IntentFilter verification broadcast with verificationId: "
+                        + verificationId);
+
+                if (canDoVerification(context)) {
+                    Intent serviceIntent = new Intent(context, IntentVerificationService.class);
+                    serviceIntent.fillIn(intent, 0);
+                    serviceIntent.putExtras(intent.getExtras());
+
+                    Slog.d(TAG, "Starting Intent Verification Service.");
+
+                    context.startService(serviceIntent);
+                } else {
+                    sendVerificationFailure(context, verificationId, hosts);
+                }
+            }
+
+        } else {
+            Log.w(TAG, "Unexpected action: " + action);
+        }
+    }
+
+    private void sendVerificationFailure(Context context, int verificationId, String hosts) {
+        List<String> list = Arrays.asList(hosts.split(" "));
+        context.getPackageManager().verifyIntentFilter(
+                verificationId, PackageManager.INTENT_FILTER_VERIFICATION_FAILURE, list);
+
+        Log.d(TAG, "No network! Failing IntentFilter verification with verificationId: " +
+                verificationId + " and hosts: " + hosts);
+    }
+
+    private boolean canDoVerification(Context context) {
+        return hasNetwork(context);
+    }
+
+    public boolean hasNetwork(Context context) {
+        ConnectivityManager cm =
+                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        if (cm != null) {
+            NetworkInfo info = cm.getActiveNetworkInfo();
+            return (info != null) && info.isConnected();
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationRequest.java b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationRequest.java
new file mode 100644
index 0000000..8f9c86f
--- /dev/null
+++ b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationRequest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.verifier.intentfilter;
+
+import com.android.volley.Response;
+import com.android.volley.toolbox.JsonArrayRequest;
+import org.json.JSONArray;
+
+public class IntentVerificationRequest extends JsonArrayRequest {
+
+    public IntentVerificationRequest(String url, Response.Listener<JSONArray> listener,
+            Response.ErrorListener errorListener) {
+        super(url, listener, errorListener);
+    }
+}
diff --git a/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationService.java b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationService.java
new file mode 100644
index 0000000..3e4db6c
--- /dev/null
+++ b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationService.java
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.verifier.intentfilter;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Slog;
+import com.android.volley.RequestQueue;
+import com.android.volley.Response;
+import com.android.volley.VolleyError;
+import com.android.volley.toolbox.Volley;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+public class IntentVerificationService extends Service {
+    private static final String TAG = "IntentVerificationService";
+
+    private static final String WELL_KNOWN_ASSOCIATIONS_JSON = "/.well-known/associations.json";
+    private static final String DEFAULT_SCHEME = "https";
+
+    private static final String JSON_KEY_TARGET = "target";
+    private static final String JSON_KEY_NAMESPACE = "namespace";
+    private static final String JSON_KEY_PACKAGE_NAME = "package_name";
+    private static final String JSON_KEY_CERT_FINGERPRINTS = "sha256_cert_fingerprints";
+
+    private static final String JSON_VAL_ANDROID_APP = "android_app";
+
+    private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+            'A', 'B', 'C', 'D', 'E', 'F' };
+
+    private ConnectivityManager mConnectivityManager;
+    private Looper mHandlerLooper;
+    private VerificationHandler mHandler;
+    private RequestQueue mRequestQueue;
+
+    private static class VerificationState {
+        public final int verificationId;
+        public final String hosts;
+        public final String packageName;
+        public final Set<String> fingerprints;
+        public int responseCode = PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS;
+        public int counter;
+        public int numberOfHosts;
+        public ArrayList<String> failedHosts = new ArrayList<>();
+
+        private final Object lock = new Object();
+
+        public VerificationState(int id, String h, String p, Set<String> fps) {
+            verificationId = id;
+            hosts = h;
+            packageName = p;
+            fingerprints = fps;
+            numberOfHosts = hosts.split(" ").length;
+        }
+        public boolean setResponseCodeAndCheckMax(int code) {
+            synchronized (lock) {
+                if (code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) {
+                    responseCode = code;
+                    counter++;
+                } else if (code == PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS) {
+                    counter++;
+                }
+                return (counter == numberOfHosts);
+            }
+        }
+
+        public void addFailedHost(String host) {
+            synchronized (failedHosts) {
+                failedHosts.add(host);
+            }
+        }
+
+        public ArrayList<String> getFailedHosts() {
+            return failedHosts;
+        }
+    }
+
+    private HashMap<Integer, VerificationState> mVerificationMap =
+            new HashMap<Integer, VerificationState>();
+
+    private class VerificationHandler extends Handler {
+        private static final int MSG_STOP_SERVICE = 0;
+        private static final int MSG_VERIFY_INTENT_START = 1;
+        private static final int MSG_VERIFY_INTENT_DONE = 2;
+
+        private static final long SHUTDOWN_DELAY_MILLIS = 8 * 1000;
+
+        private final Context mContext;
+
+        public VerificationHandler(Context context, Looper looper) {
+            super(looper);
+
+            mContext = context;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_VERIFY_INTENT_START:
+                    final Intent intent = (Intent) msg.obj;
+                    Bundle extras = intent.getExtras();
+                    boolean immediate = false;
+
+                    if (extras != null) {
+                        immediate = doVerification(extras);
+                    }
+
+                    // There was no network, so we can stop soon
+                    if (immediate) {
+                        stopDelayed();
+                    }
+                    break;
+
+                case MSG_VERIFY_INTENT_DONE:
+                    VerificationState vs = (VerificationState) msg.obj;
+                    processVerificationDone(mContext, vs);
+                    clearVerificationState(vs);
+                    break;
+
+                case MSG_STOP_SERVICE:
+                    stopSelf();
+                    break;
+
+                default:
+                    Slog.i(TAG, "Unknown message posted " + msg.toString());
+                    break;
+
+            }
+        }
+
+        private void stopDelayed() {
+            removeMessages(MSG_STOP_SERVICE);
+            sendEmptyMessageDelayed(MSG_STOP_SERVICE, SHUTDOWN_DELAY_MILLIS);
+        }
+    }
+
+    private VerificationState getVerificationState(int id, String hosts, String packageName,
+            Set<String> fingerprints) {
+        synchronized (mVerificationMap) {
+            VerificationState vs = mVerificationMap.get(id);
+            if (vs == null) {
+                vs = new VerificationState(id, hosts, packageName, fingerprints);
+            }
+            return vs;
+        }
+    }
+
+    private void clearVerificationState(VerificationState vs) {
+        mVerificationMap.remove(vs);
+    }
+
+    private boolean doVerification(Bundle extras) {
+        String scheme = extras.getString(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME);
+        if (TextUtils.isEmpty(scheme)) {
+            scheme = DEFAULT_SCHEME;
+        }
+
+        int verificationId = extras.getInt(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID);
+        String hosts = extras.getString(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS);
+        String packageName = extras.getString(
+                PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME);
+
+        Set<String> fingerprints = getFingerprints(packageName);
+
+        Log.d(TAG, "Received IntentFilter verification broadcast with verificationId:" +
+                verificationId + " hosts:'" + hosts + "' scheme:" + scheme);
+
+        VerificationState vs = getVerificationState(verificationId, hosts, packageName,
+                fingerprints);
+
+        if (hasNetwork()) {
+            sendNetworkVerifications(scheme, vs);
+            return false;
+        }
+
+        // No network, so fail immediately
+        sendFailureResponseIfNeeded(vs);
+
+        return true;
+    }
+
+    private Set<String> getFingerprints(String packageName) {
+        Context context = getApplicationContext();
+        try {
+            Signature[] signatures = context.getPackageManager().getPackageInfo(packageName,
+                    PackageManager.GET_SIGNATURES).signatures;
+            if (signatures.length > 0) {
+                HashSet<String> result = new HashSet<String>();
+                for (Signature sig : signatures) {
+                    String fingerprint = computeNormalizedSha256Fingerprint(sig.toByteArray());
+                    result.add(fingerprint);
+                }
+                return result;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Cannot get signatures for package name: " + packageName);
+        }
+        return Collections.EMPTY_SET;
+    }
+
+    private static String computeNormalizedSha256Fingerprint(byte[] signature) {
+        MessageDigest digester;
+        try {
+            digester = MessageDigest.getInstance("SHA-256");
+        } catch (NoSuchAlgorithmException e) {
+            throw new AssertionError("No SHA-256 implementation found.");
+        }
+        digester.update(signature);
+        return byteArrayToHexString(digester.digest());
+    }
+
+    private static String byteArrayToHexString(byte[] array) {
+        if (array.length == 0) {
+            return "";
+        }
+        char[] buf = new char[array.length * 3 - 1];
+
+        int bufIndex = 0;
+        for (int i = 0; i < array.length; i++) {
+            byte b = array[i];
+            if (i > 0) {
+                buf[bufIndex++] = ':';
+            }
+            buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F];
+            buf[bufIndex++] = HEX_DIGITS[b & 0x0F];
+        }
+        return new String(buf);
+    }
+
+    private static String getAssociationPath() {
+        return WELL_KNOWN_ASSOCIATIONS_JSON;
+    }
+
+    private void sendNetworkVerifications(String scheme, final VerificationState vs) {
+        final int verificationId = vs.verificationId;
+        final String hosts = vs.hosts;
+
+        String[] array = hosts.split(" ");
+        for (final String host : array) {
+            try {
+                final URL url = new URL(scheme, host, getAssociationPath());
+                final String urlStr = url.toString();
+                Log.d(TAG, "Using verification URL: " + urlStr);
+                IntentVerificationRequest req = new IntentVerificationRequest(urlStr,
+                    new Response.Listener<JSONArray>() {
+                        @Override
+                        public void onResponse(JSONArray response) {
+                            Log.d(TAG, "From: " + urlStr + " received response: "
+                                    + response.toString());
+                            handleResponse(vs, host, response);
+                        }
+                    }, new Response.ErrorListener() {
+                        @Override
+                        public void onErrorResponse(VolleyError error) {
+                            Slog.d(TAG, "From: " + urlStr + " got error: " + error.getMessage()
+                                    + (error.networkResponse != null ? " with status code: "
+                                    + error.networkResponse.statusCode : ""));
+                            handleError(vs, host);
+                        }
+                    }
+                );
+                mRequestQueue.add(req);
+            } catch (MalformedURLException e) {
+                Log.w(TAG, "Cannot send verificationId: " + verificationId + " to host: " + host);
+            }
+        }
+    }
+
+    private void handleError(VerificationState vs, String host) {
+        vs.addFailedHost(host);
+        sendFailureResponseIfNeeded(vs);
+    }
+
+    private void handleResponse(VerificationState vs, String host, JSONArray response) {
+        try {
+            if (response.length() == 0) {
+                Log.d(TAG, "Domain response is empty!");
+                handleError(vs, host);
+                return;
+            }
+
+            JSONObject firstRelation = (JSONObject) response.get(0);
+            if (firstRelation == null) {
+                Log.d(TAG, "Domain response is should have a relation!");
+                handleError(vs, host);
+                return;
+            }
+
+            JSONObject target = (JSONObject) firstRelation.get(JSON_KEY_TARGET);
+            if (target == null) {
+                Log.d(TAG, "Domain response target is empty!");
+                handleError(vs, host);
+                return;
+            }
+
+            String nameSpace = target.getString(JSON_KEY_NAMESPACE);
+            if (TextUtils.isEmpty(nameSpace) || !nameSpace.equals(JSON_VAL_ANDROID_APP)) {
+                Log.d(TAG, "Domain response target name space is not valid: " + nameSpace);
+                handleError(vs, host);
+                return;
+            }
+
+            String packageName = target.getString(JSON_KEY_PACKAGE_NAME);
+            JSONArray certFingerprints = target.getJSONArray(JSON_KEY_CERT_FINGERPRINTS);
+
+            // Early exits is the JSON response is not correct for the package name or signature
+            if (TextUtils.isEmpty(packageName)) {
+                Log.d(TAG, "Domain response has empty package name!");
+                handleError(vs, host);
+                return;
+            }
+            if (certFingerprints.length() == 0) {
+                Log.d(TAG, "Domain response has empty cert signature!");
+                handleError(vs, host);
+                return;
+            }
+            // Now do the real test on package name and signature
+            if (!packageName.equalsIgnoreCase(vs.packageName)) {
+                Log.d(TAG, "Domain response has package name mismatch!" + packageName +
+                        " vs " + vs.packageName);
+                handleError(vs, host);
+                return;
+            }
+            final int count = certFingerprints.length();
+            for (int i = 0; i < count; i++) {
+                String fingerprint = certFingerprints.getString(i);
+                if (!vs.fingerprints.contains(fingerprint)) {
+                    Log.d(TAG, "Domain response has cert fingerprint mismatch! " +
+                            "The domain fingerprint '" + fingerprint + "' is not from the App");
+                    handleError(vs, host);
+                    return;
+                }
+            }
+            sendSuccessResponseIfNeeded(vs);
+        } catch (JSONException e) {
+            Log.d(TAG, "Domain response is not well formed", e);
+            handleError(vs, host);
+        }
+    }
+
+    private void sendSuccessResponseIfNeeded(VerificationState vs) {
+        if (vs.setResponseCodeAndCheckMax(PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS)) {
+            sendMessage(vs);
+        }
+    }
+
+    private void sendFailureResponseIfNeeded(VerificationState vs) {
+        if (vs.setResponseCodeAndCheckMax(PackageManager.INTENT_FILTER_VERIFICATION_FAILURE)) {
+            sendMessage(vs);
+        }
+    }
+
+    private void sendMessage(VerificationState vs) {
+        Message msg = mHandler.obtainMessage(VerificationHandler.MSG_VERIFY_INTENT_DONE);
+        msg.obj = vs;
+        mHandler.sendMessage(msg);
+    }
+
+    private void processVerificationDone(Context context, VerificationState state) {
+        int verificationId = state.verificationId;
+        String hosts = state.hosts;
+        int responseCode = state.responseCode;
+
+        final PackageManager pm = context.getPackageManager();
+
+        // Callback the PackageManager
+        pm.verifyIntentFilter(verificationId, responseCode, state.getFailedHosts());
+        Log.d(TAG, "IntentFilter with verificationId: " + verificationId + " and hosts: " +
+                hosts + " got verification code: " + responseCode);
+    }
+
+    /**
+     * We only connect to this service from the same process.
+     */
+    public class LocalBinder extends Binder {
+        IntentVerificationService getService() { return IntentVerificationService.this; }
+    }
+
+    private final IBinder mBinder = new LocalBinder();
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Slog.i(TAG, "Received start id " + startId + ": " + intent);
+
+        final Message msg = mHandler.obtainMessage(VerificationHandler.MSG_VERIFY_INTENT_START);
+        msg.obj = intent;
+        mHandler.sendMessage(msg);
+
+        return START_STICKY;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        Slog.d(TAG, "Starting up...");
+
+        final HandlerThread handlerThread = new HandlerThread("IntentVerificationService");
+        handlerThread.start();
+        mHandlerLooper = handlerThread.getLooper();
+
+        mHandler = new VerificationHandler(getApplicationContext(), mHandlerLooper);
+
+        mRequestQueue = Volley.newRequestQueue(this);
+        mRequestQueue.start();
+
+        mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+
+        Slog.d(TAG, "Shutting down...");
+
+        mHandlerLooper.quit();
+        mRequestQueue.stop();
+    }
+
+    private boolean hasNetwork() {
+        NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
+        return (info != null) && info.isConnected();
+    }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
index 3627e3e..7d5bf6b 100644
--- a/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
+++ b/packages/Keyguard/src/com/android/keyguard/EmergencyButton.java
@@ -36,7 +36,10 @@
  * allows the user to return to the call.
  */
 public class EmergencyButton extends Button {
-    private static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL";
+    private static final Intent INTENT_EMERGENCY_DIAL = new Intent()
+            .setAction("com.android.phone.EmergencyDialer.DIAL")
+            .setPackage("com.android.phone")
+            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
 
     KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
 
@@ -112,12 +115,9 @@
                 mEmergencyButtonCallback.onEmergencyButtonClickedWhenInCall();
             }
         } else {
-            final boolean bypassHandler = true;
-            KeyguardUpdateMonitor.getInstance(mContext).reportEmergencyCallAction(bypassHandler);
-            Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
-            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-            getContext().startActivityAsUser(intent,
+            KeyguardUpdateMonitor.getInstance(mContext).reportEmergencyCallAction(
+                    true /* bypassHandler */);
+            getContext().startActivityAsUser(INTENT_EMERGENCY_DIAL,
                     new UserHandle(mLockPatternUtils.getCurrentUser()));
         }
     }
diff --git a/packages/Shell/res/values-zh-rCN/strings.xml b/packages/Shell/res/values-zh-rCN/strings.xml
index 02ca707..17fdedd 100644
--- a/packages/Shell/res/values-zh-rCN/strings.xml
+++ b/packages/Shell/res/values-zh-rCN/strings.xml
@@ -22,6 +22,5 @@
     <string name="bugreport_finished_text" product="default" msgid="3559904746859400732">"触摸即可分享您的错误报告"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"错误报告包含的数据来自于系统的各个日志文件,其中包含个人信息和隐私信息。请务必只与您信任的应用和用户分享错误报告。"</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"下次再显示这条讯息"</string>
-    <!-- no translation found for bugreport_storage_title (5332488144740527109) -->
-    <skip />
+    <string name="bugreport_storage_title" msgid="5332488144740527109">"错误报告"</string>
 </resources>
diff --git a/packages/SystemUI/res/layout-sw600dp/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
similarity index 84%
rename from packages/SystemUI/res/layout-sw600dp/recents_task_resize_dialog.xml
rename to packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
index 29e4bce..26c9b1a 100644
--- a/packages/SystemUI/res/layout-sw600dp/recents_task_resize_dialog.xml
+++ b/packages/SystemUI/res/layout-sw600dp-land/recents_task_resize_dialog.xml
@@ -41,20 +41,6 @@
             android:layout_margin="10dp"
             android:background="@drawable/vector_drawable_place_right" />
         <Button
-            android:id="@+id/place_top"
-            android:layout_width="36dp"
-            android:layout_height="36dp"
-            android:layout_weight="1"
-            android:layout_margin="10dp"
-            android:background="@drawable/vector_drawable_place_top" />
-        <Button
-            android:id="@+id/place_bottom"
-            android:layout_width="36dp"
-            android:layout_height="36dp"
-            android:layout_weight="1"
-            android:layout_margin="10dp"
-            android:background="@drawable/vector_drawable_place_bottom" />
-        <Button
             android:id="@+id/place_top_left"
             android:layout_width="36dp"
             android:layout_height="36dp"
diff --git a/packages/SystemUI/res/layout-sw600dp/recents_task_resize_dialog.xml b/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
similarity index 84%
copy from packages/SystemUI/res/layout-sw600dp/recents_task_resize_dialog.xml
copy to packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
index 29e4bce..e180daa 100644
--- a/packages/SystemUI/res/layout-sw600dp/recents_task_resize_dialog.xml
+++ b/packages/SystemUI/res/layout-sw600dp-port/recents_task_resize_dialog.xml
@@ -27,20 +27,6 @@
         android:layout_height="wrap_content"
         android:orientation="horizontal">
         <Button
-            android:id="@+id/place_left"
-            android:layout_width="36dp"
-            android:layout_height="36dp"
-            android:layout_weight="1"
-            android:layout_margin="10dp"
-            android:background="@drawable/vector_drawable_place_left" />
-        <Button
-            android:id="@+id/place_right"
-            android:layout_width="36dp"
-            android:layout_height="36dp"
-            android:layout_weight="1"
-            android:layout_margin="10dp"
-            android:background="@drawable/vector_drawable_place_right" />
-        <Button
             android:id="@+id/place_top"
             android:layout_width="36dp"
             android:layout_height="36dp"
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index e81a1d0..3a97a41 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -815,7 +815,9 @@
         }
 
         // Start dozing
-        mUIDozeTrigger.startDozing();
+        if (!mConfig.multiStackEnabled) {
+            mUIDozeTrigger.startDozing();
+        }
     }
 
     /** Requests this task stacks to start it's enter-recents animation */
@@ -1219,7 +1221,7 @@
         RecentsTaskLoader.getInstance().loadTaskData(task);
 
         // If the doze trigger has already fired, then update the state for this task view
-        if (mUIDozeTrigger.hasTriggered()) {
+        if (mConfig.multiStackEnabled || mUIDozeTrigger.hasTriggered()) {
             tv.setNoUserInteractionState();
         }
 
diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java
index 60ff996..4b3e30f 100644
--- a/rs/java/android/renderscript/Element.java
+++ b/rs/java/android/renderscript/Element.java
@@ -119,9 +119,6 @@
      */
     public enum DataType {
         NONE (0, 0),
-        /**
-         *     @hide
-         */
         FLOAT_16 (1, 2),
         FLOAT_32 (2, 4),
         FLOAT_64 (3, 8),
@@ -390,9 +387,6 @@
         return rs.mElement_I64;
     }
 
-    /**
-     *     @hide
-     */
     public static Element F16(RenderScript rs) {
         if(rs.mElement_F16 == null) {
             rs.mElement_F16 = createUser(rs, DataType.FLOAT_16);
@@ -534,9 +528,6 @@
         return rs.mElement_RGBA_8888;
     }
 
-    /**
-     *     @hide
-     */
     public static Element F16_2(RenderScript rs) {
         if(rs.mElement_HALF_2 == null) {
             rs.mElement_HALF_2 = createVector(rs, DataType.FLOAT_16, 2);
@@ -544,9 +535,6 @@
         return rs.mElement_HALF_2;
     }
 
-    /**
-     *     @hide
-     */
     public static Element F16_3(RenderScript rs) {
         if(rs.mElement_FLOAT_3 == null) {
             rs.mElement_FLOAT_3 = createVector(rs, DataType.FLOAT_16, 3);
@@ -554,9 +542,6 @@
         return rs.mElement_HALF_3;
     }
 
-    /**
-     *     @hide
-     */
     public static Element F16_4(RenderScript rs) {
         if(rs.mElement_HALF_4 == null) {
             rs.mElement_HALF_4 = createVector(rs, DataType.FLOAT_16, 4);
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index e8e942c..7ef17a7 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -139,7 +139,8 @@
      * Returns an identifier that can be used to identify a particular
      * minor version of RS.
      *
-     * @hide
+     * @return The minor RenderScript version number
+     *
      */
     public static long getMinorID() {
         return sMinorID;
@@ -1321,7 +1322,6 @@
     /**
      * Create a RenderScript context.
      *
-     * @hide
      * @param ctx The context.
      * @return RenderScript
      */
@@ -1418,14 +1418,13 @@
      /**
      * 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) {
+    private static RenderScript create(Context ctx, int sdkVersion, ContextType ct, int flags) {
         if (sdkVersion < 23) {
             return internalCreate(ctx, sdkVersion, ct, flags);
         }
@@ -1448,8 +1447,6 @@
     }
 
     /**
-     * @hide
-     *
      * Releases all the process contexts.  This is the same as
      * calling .destroy() on each unique context retreived with
      * create(...). If no contexts have been created this
@@ -1486,7 +1483,6 @@
      *
      * If you need a single context please use create()
      *
-     * @hide
      * @param ctx The context.
      * @return RenderScript
      */
diff --git a/rs/java/android/renderscript/Script.java b/rs/java/android/renderscript/Script.java
index 65056ac..6a1efee 100644
--- a/rs/java/android/renderscript/Script.java
+++ b/rs/java/android/renderscript/Script.java
@@ -66,7 +66,6 @@
     }
 
     /**
-     * @hide Pending API review
      * InvokeID is an identifier for an invoke function. It is used
      * as an identifier for ScriptGroup creation.
      *
@@ -86,7 +85,6 @@
 
     private final SparseArray<InvokeID> mIIDs = new SparseArray<InvokeID>();
     /**
-     * @hide Pending API review
      * Only to be used by generated reflected classes.
      */
     protected InvokeID createInvokeID(int slot) {
@@ -222,22 +220,21 @@
 
     /**
      * Only intended for use by generated reflected code.
-     *
-     * @hide
      */
     protected void forEach(int slot, Allocation[] ains, Allocation aout,
                            FieldPacker v) {
+
+        // FieldPacker is kept here to support regular params in the future.
         forEach(slot, ains, aout, v, null);
     }
 
     /**
      * Only intended for use by generated reflected code.
-     *
-     * @hide
      */
     protected void forEach(int slot, Allocation[] ains, Allocation aout,
                            FieldPacker v, LaunchOptions sc) {
         // TODO: Is this necessary if nScriptForEach calls validate as well?
+        // FieldPacker is kept here to support regular params in the future.
         mRS.validate();
         if (ains != null) {
             for (Allocation ain : ains) {
@@ -476,7 +473,23 @@
 
 
     /**
-     * Class used to specify clipping for a kernel launch.
+     * Class for specifying the specifics about how a kernel will be
+     * launched
+     *
+     * This class can specify a potential range of cells on which to
+     * run a kernel.  If no set is called for a dimension then this
+     * class will have no impact on that dimension when the kernel
+     * is executed.
+     *
+     * The forEach launch will operate over the intersection of the
+     * dimensions.
+     *
+     * Example:
+     * LaunchOptions with setX(5, 15)
+     * Allocation with dimension X=10, Y=10
+     * The resulting forEach run would execute over x = 5 to 10 and
+     * y = 0 to 10.
+     *
      *
      */
     public static final class LaunchOptions {
diff --git a/rs/java/android/renderscript/Type.java b/rs/java/android/renderscript/Type.java
index a58e42c..cc9b58b 100644
--- a/rs/java/android/renderscript/Type.java
+++ b/rs/java/android/renderscript/Type.java
@@ -150,24 +150,29 @@
     }
 
     /**
-     * @hide
-     */
-    public int getArray(int dim) {
-        if ((dim < 0) || (dim >= mMaxArrays)) {
+      * Return the dimension of the specified array.
+      *
+      * @param arrayNum  The array dimension to query
+      * @return int
+      */
+    public int getArray(int arrayNum) {
+        if ((arrayNum < 0) || (arrayNum >= mMaxArrays)) {
             throw new RSIllegalArgumentException("Array dimension out of range.");
         }
 
-        if (mArrays == null || dim >= mArrays.length) {
+        if (mArrays == null || arrayNum >= mArrays.length) {
             // Dimension in range but no array for that dimension allocated
             return 0;
         }
 
-        return mArrays[dim];
+        return mArrays[arrayNum];
     }
 
     /**
-     * @hide
-     */
+      * Return the number of array dimensions.
+      *
+      * @return int
+      */
     public int getArrayCount() {
         if (mArrays != null) return mArrays.length;
         return 0;
@@ -377,7 +382,7 @@
         }
 
         /**
-         * @hide
+         * Adds an array dimension to the builder
          *
          * @param dim
          * @param value
diff --git a/services/core/java/com/android/server/GraphicsStatsService.java b/services/core/java/com/android/server/GraphicsStatsService.java
new file mode 100644
index 0000000..c79fdfc
--- /dev/null
+++ b/services/core/java/com/android/server/GraphicsStatsService.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.MemoryFile;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.IGraphicsStats;
+import android.view.ThreadedRenderer;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * This service's job is to collect aggregate rendering profile data. It
+ * does this by allowing rendering processes to request an ashmem buffer
+ * to place their stats into. This buffer will be pre-initialized with historical
+ * data for that process if it exists (if the userId & packageName match a buffer
+ * in the historical log)
+ *
+ * This service does not itself attempt to understand the data in the buffer,
+ * its primary job is merely to manage distributing these buffers. However,
+ * it is assumed that this buffer is for ThreadedRenderer and delegates
+ * directly to ThreadedRenderer for dumping buffers.
+ *
+ * MEMORY USAGE:
+ *
+ * This class consumes UP TO:
+ * 1) [active rendering processes] * (ASHMEM_SIZE * 2)
+ * 2) ASHMEM_SIZE (for scratch space used during dumping)
+ * 3) ASHMEM_SIZE * HISTORY_SIZE
+ *
+ * Currently ASHMEM_SIZE is 256 bytes and HISTORY_SIZE is 10. Assuming
+ * the system then also has 10 active rendering processes in the worst case
+ * this would end up using under 10KiB (8KiB for the buffers, plus some overhead
+ * for userId, pid, package name, and a couple other objects)
+ *
+ *  @hide */
+public class GraphicsStatsService extends IGraphicsStats.Stub {
+    public static final String GRAPHICS_STATS_SERVICE = "graphicsstats";
+
+    private static final String TAG = "GraphicsStatsService";
+    private static final int ASHMEM_SIZE = 256;
+    private static final int HISTORY_SIZE = 10;
+
+    private final Context mContext;
+    private final Object mLock = new Object();
+    private ArrayList<ActiveBuffer> mActive = new ArrayList<>();
+    private HistoricalData[] mHistoricalLog = new HistoricalData[HISTORY_SIZE];
+    private int mNextHistoricalSlot = 0;
+    private byte[] mTempBuffer = new byte[ASHMEM_SIZE];
+
+    public GraphicsStatsService(Context context) {
+        mContext = context;
+    }
+
+    private boolean isValid(int uid, String packageName) {
+        try {
+            PackageInfo info = mContext.getPackageManager().getPackageInfo(packageName, 0);
+            return info.applicationInfo.uid == uid;
+        } catch (NameNotFoundException e) {
+        }
+        return false;
+    }
+
+    @Override
+    public ParcelFileDescriptor requestBufferForProcess(String packageName, IBinder token)
+            throws RemoteException {
+        int uid = Binder.getCallingUid();
+        int pid = Binder.getCallingPid();
+        ParcelFileDescriptor pfd = null;
+        long callingIdentity = Binder.clearCallingIdentity();
+        try {
+            if (!isValid(uid, packageName)) {
+                throw new RemoteException("Invalid package name");
+            }
+            synchronized (mLock) {
+                pfd = requestBufferForProcessLocked(token, uid, pid, packageName);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(callingIdentity);
+        }
+        return pfd;
+    }
+
+    private ParcelFileDescriptor getPfd(MemoryFile file) {
+        try {
+            return new ParcelFileDescriptor(file.getFileDescriptor());
+        } catch (IOException ex) {
+            throw new IllegalStateException("Failed to get PFD from memory file", ex);
+        }
+    }
+
+    private ParcelFileDescriptor requestBufferForProcessLocked(IBinder token,
+            int uid, int pid, String packageName) throws RemoteException {
+        ActiveBuffer buffer = fetchActiveBuffersLocked(token, uid, pid, packageName);
+        return getPfd(buffer.mProcessBuffer);
+    }
+
+    private void processDied(ActiveBuffer buffer) {
+        synchronized (mLock) {
+            mActive.remove(buffer);
+            Log.d("GraphicsStats", "Buffer count: " + mActive.size());
+        }
+        HistoricalData data = buffer.mPreviousData;
+        buffer.mPreviousData = null;
+        if (data == null) {
+            data = mHistoricalLog[mNextHistoricalSlot];
+            if (data == null) {
+                data = new HistoricalData();
+            }
+        }
+        data.update(buffer.mPackageName, buffer.mUid, buffer.mProcessBuffer);
+        buffer.closeAllBuffers();
+
+        mHistoricalLog[mNextHistoricalSlot] = data;
+        mNextHistoricalSlot = (mNextHistoricalSlot + 1) % mHistoricalLog.length;
+    }
+
+    private ActiveBuffer fetchActiveBuffersLocked(IBinder token, int uid, int pid,
+            String packageName) throws RemoteException {
+        int size = mActive.size();
+        for (int i = 0; i < size; i++) {
+            ActiveBuffer buffers = mActive.get(i);
+            if (buffers.mPid == pid
+                    && buffers.mUid == uid) {
+                return buffers;
+            }
+        }
+        // Didn't find one, need to create it
+        try {
+            ActiveBuffer buffers = new ActiveBuffer(token, uid, pid, packageName);
+            mActive.add(buffers);
+            return buffers;
+        } catch (IOException ex) {
+            throw new RemoteException("Failed to allocate space");
+        }
+    }
+
+    private HistoricalData removeHistoricalDataLocked(int uid, String packageName) {
+        for (int i = 0; i < mHistoricalLog.length; i++) {
+            final HistoricalData data = mHistoricalLog[i];
+            if (data != null && data.mUid == uid
+                    && data.mPackageName.equals(packageName)) {
+                if (i == mNextHistoricalSlot) {
+                    mHistoricalLog[i] = null;
+                } else {
+                    mHistoricalLog[i] = mHistoricalLog[mNextHistoricalSlot];
+                    mHistoricalLog[mNextHistoricalSlot] = null;
+                }
+                return data;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
+        synchronized (mLock) {
+            for (int i = 0; i < mActive.size(); i++) {
+                final ActiveBuffer buffer = mActive.get(i);
+                fout.print("Package: ");
+                fout.print(buffer.mPackageName);
+                fout.flush();
+                try {
+                    buffer.mProcessBuffer.readBytes(mTempBuffer, 0, 0, ASHMEM_SIZE);
+                    ThreadedRenderer.dumpProfileData(mTempBuffer, fd);
+                } catch (IOException e) {
+                    fout.println("Failed to dump");
+                }
+                fout.println();
+            }
+            for (HistoricalData buffer : mHistoricalLog) {
+                if (buffer == null) continue;
+                fout.print("Package: ");
+                fout.print(buffer.mPackageName);
+                fout.flush();
+                ThreadedRenderer.dumpProfileData(buffer.mBuffer, fd);
+                fout.println();
+            }
+        }
+    }
+
+    private final class ActiveBuffer implements DeathRecipient {
+        final int mUid;
+        final int mPid;
+        final String mPackageName;
+        final IBinder mToken;
+        MemoryFile mProcessBuffer;
+        HistoricalData mPreviousData;
+
+        ActiveBuffer(IBinder token, int uid, int pid, String packageName)
+                throws RemoteException, IOException {
+            mUid = uid;
+            mPid = pid;
+            mPackageName = packageName;
+            mToken = token;
+            mToken.linkToDeath(this, 0);
+            mProcessBuffer = new MemoryFile("GFXStats-" + uid, ASHMEM_SIZE);
+            mPreviousData = removeHistoricalDataLocked(mUid, mPackageName);
+            if (mPreviousData != null) {
+                mProcessBuffer.writeBytes(mPreviousData.mBuffer, 0, 0, ASHMEM_SIZE);
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            mToken.unlinkToDeath(this, 0);
+            processDied(this);
+        }
+
+        void closeAllBuffers() {
+            if (mProcessBuffer != null) {
+                mProcessBuffer.close();
+                mProcessBuffer = null;
+            }
+        }
+    }
+
+    private final static class HistoricalData {
+        final byte[] mBuffer = new byte[ASHMEM_SIZE];
+        int mUid;
+        String mPackageName;
+
+        void update(String packageName, int uid, MemoryFile file) {
+            mUid = uid;
+            mPackageName = packageName;
+            try {
+                file.readBytes(mBuffer, 0, 0, ASHMEM_SIZE);
+            } catch (IOException e) {}
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index cea1ebe..744156b 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -47,6 +47,7 @@
     final private static String TAG = "IntentResolver";
     final private static boolean DEBUG = false;
     final private static boolean localLOGV = DEBUG || false;
+    final private static boolean localVerificationLOGV = DEBUG || false;
 
     public void addFilter(F f) {
         if (localLOGV) {
@@ -478,7 +479,7 @@
 
     /**
      * Returns whether the object associated with the given filter is
-     * "stopped," that is whether it should not be included in the result
+     * "stopped", that is whether it should not be included in the result
      * if the intent requests to excluded stopped objects.
      */
     protected boolean isFilterStopped(F filter, int userId) {
@@ -486,6 +487,22 @@
     }
 
     /**
+     * Returns whether the given filter is "verified" that is whether it has been verified against
+     * its data URIs.
+     *
+     * The verification would happen only and only if the Intent action is
+     * {@link android.content.Intent#ACTION_VIEW} and the Intent category is
+     * {@link android.content.Intent#CATEGORY_BROWSABLE} and the Intent data scheme
+     * is "http" or "https".
+     *
+     * @see android.content.IntentFilter#setAutoVerify(boolean)
+     * @see android.content.IntentFilter#getAutoVerify()
+     */
+    protected boolean isFilterVerified(F filter) {
+        return filter.isVerified();
+    }
+
+    /**
      * Returns whether this filter is owned by this package. This must be
      * implemented to provide correct filtering of Intents that have
      * specified a package name they are to be delivered to.
@@ -710,6 +727,13 @@
                 continue;
             }
 
+            // Are we verified ?
+            if (filter.getAutoVerify()) {
+                if (localVerificationLOGV || debug) {
+                    Slog.v(TAG, "  Filter verified: " + isFilterVerified(filter));
+                }
+            }
+
             // Do we already have this one?
             if (!allowFilterResult(filter, dest)) {
                 if (debug) {
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index b8d9ec5..61286e8 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -16,29 +16,21 @@
 
 package com.android.server;
 
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
 import android.Manifest;
 import android.app.ActivityManagerNative;
 import android.app.AppOpsManager;
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.content.res.ObbInfo;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.hardware.usb.UsbManager;
+import android.mtp.MtpStorage;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Environment.UserEnvironment;
+import android.os.FileUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -59,26 +51,24 @@
 import android.os.storage.StorageResultCode;
 import android.os.storage.StorageVolume;
 import android.text.TextUtils;
-import android.util.AttributeSet;
+import android.util.ArrayMap;
+import android.util.DebugUtils;
 import android.util.Slog;
-import android.util.Xml;
+import android.util.SparseArray;
+
+import libcore.util.EmptyArray;
+import libcore.util.HexEncoding;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IMediaContainerService;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
-import com.android.internal.util.XmlUtils;
 import com.android.server.NativeDaemonConnector.Command;
 import com.android.server.NativeDaemonConnector.SensitiveArg;
-import com.android.server.am.ActivityManagerService;
 import com.android.server.pm.PackageManagerService;
-import com.android.server.pm.UserManagerService;
 import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-import libcore.util.HexEncoding;
-
-import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -101,7 +91,6 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -110,19 +99,18 @@
 import javax.crypto.spec.PBEKeySpec;
 
 /**
- * MountService implements back-end services for platform storage
- * management.
- * @hide - Applications should use android.os.storage.StorageManager
- * to access the MountService.
+ * Service responsible for various storage media. Connects to {@code vold} to
+ * watch for and manage dynamically added storage, such as SD cards and USB mass
+ * storage. Also decides how storage should be presented to users on the device.
  */
 class MountService extends IMountService.Stub
         implements INativeDaemonConnectorCallbacks, Watchdog.Monitor {
 
+    // TODO: finish enforcing UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA
+
     // Static direct instance pointer for the tightly-coupled idle service to use
     static MountService sSelf = null;
 
-    // TODO: listen for user creation/deletion
-
     public static class Lifecycle extends SystemService {
         private MountService mMountService;
 
@@ -142,6 +130,16 @@
                 mMountService.systemReady();
             }
         }
+
+        @Override
+        public void onStartUser(int userHandle) {
+            mMountService.onStartUser(userHandle);
+        }
+
+        @Override
+        public void onCleanupUser(int userHandle) {
+            mMountService.onCleanupUser(userHandle);
+        }
     }
 
     private static final boolean LOCAL_LOGD = false;
@@ -159,21 +157,7 @@
     /** Maximum number of ASEC containers allowed to be mounted. */
     private static final int MAX_CONTAINERS = 250;
 
-    /*
-     * Internal vold volume state constants
-     */
-    class VolumeState {
-        public static final int Init       = -1;
-        public static final int NoMedia    = 0;
-        public static final int Idle       = 1;
-        public static final int Pending    = 2;
-        public static final int Checking   = 3;
-        public static final int Mounted    = 4;
-        public static final int Unmounting = 5;
-        public static final int Formatting = 6;
-        public static final int Shared     = 7;
-        public static final int SharedMnt  = 8;
-    }
+    private static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical";
 
     /*
      * Internal vold response code constants
@@ -209,12 +193,19 @@
         /*
          * 600 series - Unsolicited broadcasts.
          */
-        public static final int VolumeStateChange              = 605;
-        public static final int VolumeUuidChange               = 613;
-        public static final int VolumeUserLabelChange          = 614;
-        public static final int VolumeDiskInserted             = 630;
-        public static final int VolumeDiskRemoved              = 631;
-        public static final int VolumeBadRemoval               = 632;
+        public static final int DISK_CREATED = 640;
+        public static final int DISK_SIZE_CHANGED = 641;
+        public static final int DISK_LABEL_CHANGED = 642;
+        public static final int DISK_VOLUME_CREATED = 643;
+        public static final int DISK_DESTROYED = 649;
+
+        public static final int VOLUME_CREATED = 650;
+        public static final int VOLUME_STATE_CHANGED = 651;
+        public static final int VOLUME_FS_TYPE_CHANGED = 652;
+        public static final int VOLUME_FS_UUID_CHANGED = 653;
+        public static final int VOLUME_FS_LABEL_CHANGED = 654;
+        public static final int VOLUME_PATH_CHANGED = 655;
+        public static final int VOLUME_DESTROYED = 659;
 
         /*
          * 700 series - fstrim
@@ -222,6 +213,243 @@
         public static final int FstrimCompleted                = 700;
     }
 
+    private static SparseArray<String> sStateToEnvironment = new SparseArray<>();
+    private static ArrayMap<String, String> sEnvironmentToBroadcast = new ArrayMap<>();
+
+    static {
+        sStateToEnvironment.put(Volume.STATE_UNMOUNTED, Environment.MEDIA_UNMOUNTED);
+        sStateToEnvironment.put(Volume.STATE_MOUNTING, Environment.MEDIA_CHECKING);
+        sStateToEnvironment.put(Volume.STATE_MOUNTED, Environment.MEDIA_MOUNTED);
+        sStateToEnvironment.put(Volume.STATE_FORMATTING, Environment.MEDIA_UNMOUNTED);
+        sStateToEnvironment.put(Volume.STATE_UNMOUNTING, Environment.MEDIA_EJECTING);
+
+        sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTED, Intent.ACTION_MEDIA_UNMOUNTED);
+        sEnvironmentToBroadcast.put(Environment.MEDIA_CHECKING, Intent.ACTION_MEDIA_CHECKING);
+        sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED, Intent.ACTION_MEDIA_MOUNTED);
+        sEnvironmentToBroadcast.put(Environment.MEDIA_EJECTING, Intent.ACTION_MEDIA_EJECT);
+    }
+
+    /**
+     * <em>Never</em> hold the lock while performing downcalls into vold, since
+     * unsolicited events can suddenly appear to update data structures.
+     */
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private int[] mStartedUsers = EmptyArray.INT;
+    @GuardedBy("mLock")
+    private ArrayMap<String, Disk> mDisks = new ArrayMap<>();
+    @GuardedBy("mLock")
+    private ArrayMap<String, Volume> mVolumes = new ArrayMap<>();
+
+    @Deprecated
+    private Volume findVolumeByLegacyPath(String legacyPath) {
+        synchronized (mLock) {
+            for (Volume vol : mVolumes.values()) {
+                if (vol.path != null && legacyPath.startsWith(vol.path)) {
+                    return vol;
+                }
+            }
+        }
+        Slog.w(TAG, "Failed to find volume for path " + legacyPath);
+        return null;
+    }
+
+    /**
+     * Framework-side twin of android::vold::Disk
+     */
+    private class Disk {
+        public static final int FLAG_ADOPTABLE = 1 << 0;
+        public static final int FLAG_DEFAULT_PRIMARY = 1 << 1;
+        public static final int FLAG_SD = 1 << 2;
+        public static final int FLAG_USB = 1 << 3;
+
+        public final String id;
+        public final int flags;
+        public long size;
+        public String label;
+
+        public ArrayList<Volume> volumes = new ArrayList<>();
+
+        public Disk(String id, int flags) {
+            this.id = id;
+            this.flags = flags;
+        }
+
+        public void partitionPublic() throws NativeDaemonConnectorException {
+            mConnector.execute("volume", "partition", id, "public");
+        }
+
+        public void partitionPrivate() throws NativeDaemonConnectorException {
+            mConnector.execute("volume", "partition", id, "private");
+        }
+
+        public void partitionMixed(int frac) throws NativeDaemonConnectorException {
+            mConnector.execute("volume", "partition", id, "mixed", frac);
+        }
+
+        public void dump(IndentingPrintWriter pw) {
+            pw.println("Disk:");
+            pw.increaseIndent();
+            pw.printPair("id", id);
+            pw.printPair("flags", DebugUtils.flagsToString(getClass(), "FLAG_", flags));
+            pw.printPair("size", size);
+            pw.printPair("label", label);
+            pw.decreaseIndent();
+            pw.println();
+        }
+    }
+
+    private static int sNextMtpIndex = 1;
+
+    /**
+     * Framework-side twin of android::vold::VolumeBase
+     */
+    private class Volume {
+        public static final String ID_EMULATED_INTERNAL = "emulated";
+
+        public static final int TYPE_PUBLIC = 0;
+        public static final int TYPE_PRIVATE = 1;
+        public static final int TYPE_EMULATED = 2;
+        public static final int TYPE_ASEC = 3;
+        public static final int TYPE_OBB = 4;
+
+        public static final int STATE_UNMOUNTED = 0;
+        public static final int STATE_MOUNTING = 1;
+        public static final int STATE_MOUNTED = 2;
+        public static final int STATE_FORMATTING = 3;
+        public static final int STATE_UNMOUNTING = 4;
+
+        public static final int FLAG_PRIMARY = 1 << 0;
+        public static final int FLAG_VISIBLE = 1 << 1;
+
+        /** vold state */
+        public final String id;
+        public final int type;
+        public int flags = 0;
+        public int userId = -1;
+        public int state = STATE_UNMOUNTED;
+        public String fsType;
+        public String fsUuid;
+        public String fsLabel;
+        public String path = "/dev/null";
+
+        /** Framework state */
+        public final int mtpIndex;
+
+        public Disk disk;
+
+        public Volume(String id, int type) {
+            this.id = id;
+            this.type = type;
+
+            if (ID_EMULATED_INTERNAL.equals(id)) {
+                mtpIndex = 0;
+            } else {
+                mtpIndex = sNextMtpIndex++;
+            }
+        }
+
+        public boolean isPrimary() {
+            return (flags & FLAG_PRIMARY) != 0;
+        }
+
+        public boolean isVisible() {
+            return (flags & FLAG_VISIBLE) != 0;
+        }
+
+        public boolean isVisibleToUser(int userId) {
+            if (type == TYPE_PUBLIC && this.userId == userId) {
+                return isVisible();
+            } else if (type == TYPE_EMULATED) {
+                return isVisible();
+            } else {
+                return false;
+            }
+        }
+
+        public void mount() throws NativeDaemonConnectorException {
+            mConnector.execute("volume", "mount", id, flags, userId);
+        }
+
+        public void unmount() throws NativeDaemonConnectorException {
+            mConnector.execute("volume", "unmount", id);
+        }
+
+        public void format() throws NativeDaemonConnectorException {
+            mConnector.execute("volume", "format", id);
+        }
+
+        public StorageVolume buildVolumeForUser(int userId) {
+            final File userPath;
+            final boolean removable;
+            final boolean emulated;
+            final boolean allowMassStorage = false;
+            final int mtpStorageId = MtpStorage.getStorageIdForIndex(mtpIndex);
+            final String envState = sStateToEnvironment.get(state);
+
+            int descriptionId = com.android.internal.R.string.unknownName;
+            long mtpReserveSize = 0;
+            long maxFileSize = 0;
+
+            if (type == TYPE_EMULATED) {
+                userPath = new File(path, Integer.toString(userId));
+                emulated = true;
+                mtpReserveSize = StorageManager.from(mContext).getStorageLowBytes(userPath);
+                descriptionId = com.android.internal.R.string.storage_internal;
+
+                if (ID_EMULATED_INTERNAL.equals(id)) {
+                    removable = false;
+                } else {
+                    removable = true;
+                }
+
+            } else if (type == TYPE_PUBLIC) {
+                userPath = new File(path);
+                emulated = false;
+                removable = true;
+
+                if (disk != null) {
+                    if ((disk.flags & Disk.FLAG_SD) != 0) {
+                        descriptionId = com.android.internal.R.string.storage_sd_card;
+                    } else if ((disk.flags & Disk.FLAG_USB) != 0) {
+                        descriptionId = com.android.internal.R.string.storage_usb;
+                    }
+                }
+
+                if ("vfat".equals(fsType)) {
+                    maxFileSize = 4294967295L;
+                }
+
+            } else {
+                throw new IllegalStateException("Unexpected volume type " + type);
+            }
+
+            return new StorageVolume(id, mtpStorageId, userPath, descriptionId, isPrimary(),
+                    removable, emulated, mtpReserveSize, allowMassStorage, maxFileSize,
+                    new UserHandle(userId), fsUuid, fsLabel, envState);
+        }
+
+        public void dump(IndentingPrintWriter pw) {
+            pw.println("Volume:");
+            pw.increaseIndent();
+            pw.printPair("id", id);
+            pw.printPair("type", DebugUtils.valueToString(getClass(), "TYPE_", type));
+            pw.printPair("flags", DebugUtils.flagsToString(getClass(), "FLAG_", flags));
+            pw.printPair("userId", userId);
+            pw.printPair("state", DebugUtils.valueToString(getClass(), "STATE_", state));
+            pw.println();
+            pw.printPair("fsType", fsType);
+            pw.printPair("fsUuid", fsUuid);
+            pw.printPair("fsLabel", fsLabel);
+            pw.println();
+            pw.printPair("path", path);
+            pw.printPair("mtpIndex", mtpIndex);
+            pw.decreaseIndent();
+            pw.println();
+        }
+    }
+
     /** List of crypto types.
       * These must match CRYPT_TYPE_XXX in cryptfs.h AND their
       * corresponding commands in CommandListener.cpp */
@@ -231,33 +459,20 @@
     private final Context mContext;
     private final NativeDaemonConnector mConnector;
 
-    private final Object mVolumesLock = new Object();
-
-    /** When defined, base template for user-specific {@link StorageVolume}. */
-    private StorageVolume mEmulatedTemplate;
-
-    // TODO: separate storage volumes on per-user basis
-
-    @GuardedBy("mVolumesLock")
-    private final ArrayList<StorageVolume> mVolumes = Lists.newArrayList();
-    /** Map from path to {@link StorageVolume} */
-    @GuardedBy("mVolumesLock")
-    private final HashMap<String, StorageVolume> mVolumesByPath = Maps.newHashMap();
-    /** Map from path to state */
-    @GuardedBy("mVolumesLock")
-    private final HashMap<String, String> mVolumeStates = Maps.newHashMap();
-
     private volatile boolean mSystemReady = false;
+    private volatile boolean mDaemonConnected = false;
 
     private PackageManagerService                 mPms;
-    private boolean                               mUmsEnabling;
-    private boolean                               mUmsAvailable = false;
     // Used as a lock for methods that register/unregister listeners.
     final private ArrayList<MountServiceBinderListener> mListeners =
             new ArrayList<MountServiceBinderListener>();
+
     private final CountDownLatch mConnectedSignal = new CountDownLatch(1);
     private final CountDownLatch mAsecsScanned = new CountDownLatch(1);
-    private boolean                               mSendUmsConnectedOnBoot = false;
+
+    private final Object mUnmountLock = new Object();
+    @GuardedBy("mUnmountLock")
+    private CountDownLatch mUnmountSignal;
 
     /**
      * Private hash of currently mounted secure containers.
@@ -366,6 +581,7 @@
     final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
 
     class DefaultContainerConnection implements ServiceConnection {
+        @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             if (DEBUG_OBB)
                 Slog.i(TAG, "onServiceConnected");
@@ -373,6 +589,7 @@
             mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
         }
 
+        @Override
         public void onServiceDisconnected(ComponentName name) {
             if (DEBUG_OBB)
                 Slog.i(TAG, "onServiceDisconnected");
@@ -388,177 +605,27 @@
     private long mLastMaintenance;
 
     // Handler messages
-    private static final int H_UNMOUNT_PM_UPDATE = 1;
-    private static final int H_UNMOUNT_PM_DONE = 2;
-    private static final int H_UNMOUNT_MS = 3;
-    private static final int H_SYSTEM_READY = 4;
-    private static final int H_FSTRIM = 5;
-
-    private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
-    private static final int MAX_UNMOUNT_RETRIES = 4;
-
-    class UnmountCallBack {
-        final String path;
-        final boolean force;
-        final boolean removeEncryption;
-        int retries;
-
-        UnmountCallBack(String path, boolean force, boolean removeEncryption) {
-            retries = 0;
-            this.path = path;
-            this.force = force;
-            this.removeEncryption = removeEncryption;
-        }
-
-        void handleFinished() {
-            if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path);
-            doUnmountVolume(path, true, removeEncryption);
-        }
-    }
-
-    class UmsEnableCallBack extends UnmountCallBack {
-        final String method;
-
-        UmsEnableCallBack(String path, String method, boolean force) {
-            super(path, force, false);
-            this.method = method;
-        }
-
-        @Override
-        void handleFinished() {
-            super.handleFinished();
-            doShareUnshareVolume(path, method, true);
-        }
-    }
-
-    class ShutdownCallBack extends UnmountCallBack {
-        MountShutdownLatch mMountShutdownLatch;
-        ShutdownCallBack(String path, final MountShutdownLatch mountShutdownLatch) {
-            super(path, true, false);
-            mMountShutdownLatch = mountShutdownLatch;
-        }
-
-        @Override
-        void handleFinished() {
-            int ret = doUnmountVolume(path, true, removeEncryption);
-            Slog.i(TAG, "Unmount completed: " + path + ", result code: " + ret);
-            mMountShutdownLatch.countDown();
-        }
-    }
-
-    static class MountShutdownLatch {
-        private IMountShutdownObserver mObserver;
-        private AtomicInteger mCount;
-
-        MountShutdownLatch(final IMountShutdownObserver observer, int count) {
-            mObserver = observer;
-            mCount = new AtomicInteger(count);
-        }
-
-        void countDown() {
-            boolean sendShutdown = false;
-            if (mCount.decrementAndGet() == 0) {
-                sendShutdown = true;
-            }
-            if (sendShutdown && mObserver != null) {
-                try {
-                    mObserver.onShutDownComplete(StorageResultCode.OperationSucceeded);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "RemoteException when shutting down");
-                }
-            }
-        }
-    }
+    private static final int H_SYSTEM_READY = 1;
+    private static final int H_DAEMON_CONNECTED = 2;
+    private static final int H_SHUTDOWN = 3;
+    private static final int H_FSTRIM = 4;
+    private static final int H_VOLUME_MOUNT = 5;
+    private static final int H_VOLUME_BROADCAST = 6;
 
     class MountServiceHandler extends Handler {
-        ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>();
-        boolean mUpdatingStatus = false;
-
-        MountServiceHandler(Looper l) {
-            super(l);
+        public MountServiceHandler(Looper looper) {
+            super(looper);
         }
 
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case H_UNMOUNT_PM_UPDATE: {
-                    if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE");
-                    UnmountCallBack ucb = (UnmountCallBack) msg.obj;
-                    mForceUnmounts.add(ucb);
-                    if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus);
-                    // Register only if needed.
-                    if (!mUpdatingStatus) {
-                        if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
-                        mUpdatingStatus = true;
-                        mPms.updateExternalMediaStatus(false, true);
-                    }
-                    break;
-                }
-                case H_UNMOUNT_PM_DONE: {
-                    if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
-                    if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
-                    mUpdatingStatus = false;
-                    int size = mForceUnmounts.size();
-                    int sizeArr[] = new int[size];
-                    int sizeArrN = 0;
-                    // Kill processes holding references first
-                    ActivityManagerService ams = (ActivityManagerService)
-                    ServiceManager.getService("activity");
-                    for (int i = 0; i < size; i++) {
-                        UnmountCallBack ucb = mForceUnmounts.get(i);
-                        String path = ucb.path;
-                        boolean done = false;
-                        if (!ucb.force) {
-                            done = true;
-                        } else {
-                            int pids[] = getStorageUsers(path);
-                            if (pids == null || pids.length == 0) {
-                                done = true;
-                            } else {
-                                // Eliminate system process here?
-                                ams.killPids(pids, "unmount media", true);
-                                // Confirm if file references have been freed.
-                                pids = getStorageUsers(path);
-                                if (pids == null || pids.length == 0) {
-                                    done = true;
-                                }
-                            }
-                        }
-                        if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
-                            // Retry again
-                            Slog.i(TAG, "Retrying to kill storage users again");
-                            mHandler.sendMessageDelayed(
-                                    mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
-                                            ucb.retries++),
-                                    RETRY_UNMOUNT_DELAY);
-                        } else {
-                            if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
-                                Slog.i(TAG, "Failed to unmount media inspite of " +
-                                        MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
-                            }
-                            sizeArr[sizeArrN++] = i;
-                            mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
-                                    ucb));
-                        }
-                    }
-                    // Remove already processed elements from list.
-                    for (int i = (sizeArrN-1); i >= 0; i--) {
-                        mForceUnmounts.remove(sizeArr[i]);
-                    }
-                    break;
-                }
-                case H_UNMOUNT_MS: {
-                    if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
-                    UnmountCallBack ucb = (UnmountCallBack) msg.obj;
-                    ucb.handleFinished();
-                    break;
-                }
                 case H_SYSTEM_READY: {
-                    try {
-                        handleSystemReady();
-                    } catch (Exception ex) {
-                        Slog.e(TAG, "Boot-time mount exception", ex);
-                    }
+                    handleSystemReady();
+                    break;
+                }
+                case H_DAEMON_CONNECTED: {
+                    handleDaemonConnected();
                     break;
                 }
                 case H_FSTRIM: {
@@ -589,29 +656,68 @@
                     }
                     break;
                 }
+                case H_SHUTDOWN: {
+                    final IMountShutdownObserver obs = (IMountShutdownObserver) msg.obj;
+                    boolean success = false;
+                    try {
+                        success = mConnector.execute("volume", "shutdown").isClassOk();
+                    } catch (NativeDaemonConnectorException ignored) {
+                    }
+                    if (obs != null) {
+                        try {
+                            obs.onShutDownComplete(success ? 0 : -1);
+                        } catch (RemoteException ignored) {
+                        }
+                    }
+                    break;
+                }
+                case H_VOLUME_MOUNT: {
+                    final Volume vol = (Volume) msg.obj;
+                    try {
+                        vol.mount();
+                    } catch (NativeDaemonConnectorException ignored) {
+                    }
+                    break;
+                }
+                case H_VOLUME_BROADCAST: {
+                    final StorageVolume userVol = (StorageVolume) msg.obj;
+                    final String state = userVol.getState();
+                    Slog.d(TAG, "Volume " + userVol.getId() + " broadcasting " + state + " to "
+                            + userVol.getOwner());
+
+                    final String action = sEnvironmentToBroadcast.get(state);
+                    if (action != null) {
+                        final Intent intent = new Intent(action,
+                                Uri.fromFile(userVol.getPathFile()));
+                        intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, userVol);
+                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                        mContext.sendBroadcastAsUser(intent, userVol.getOwner());
+                    }
+                    break;
+                }
             }
         }
-    };
+    }
 
     private final Handler mHandler;
 
     @Override
     public void waitForAsecScan() {
-        waitForLatch(mAsecsScanned);
+        waitForLatch(mAsecsScanned, "mAsecsScanned");
     }
 
     private void waitForReady() {
-        waitForLatch(mConnectedSignal);
+        waitForLatch(mConnectedSignal, "mConnectedSignal");
     }
 
-    private void waitForLatch(CountDownLatch latch) {
+    private void waitForLatch(CountDownLatch latch, String condition) {
         for (;;) {
             try {
                 if (latch.await(5000, TimeUnit.MILLISECONDS)) {
                     return;
                 } else {
                     Slog.w(TAG, "Thread " + Thread.currentThread().getName()
-                            + " still waiting for MountService ready...");
+                            + " still waiting for " + condition + "...");
                 }
             } catch (InterruptedException e) {
                 Slog.w(TAG, "Interrupt while waiting for MountService to be ready.");
@@ -628,103 +734,72 @@
     }
 
     private void handleSystemReady() {
-        // Snapshot current volume states since it's not safe to call into vold
-        // while holding locks.
-        final HashMap<String, String> snapshot;
-        synchronized (mVolumesLock) {
-            snapshot = new HashMap<String, String>(mVolumeStates);
-        }
+        resetIfReadyAndConnected();
 
-        for (Map.Entry<String, String> entry : snapshot.entrySet()) {
-            final String path = entry.getKey();
-            final String state = entry.getValue();
-
-            if (state.equals(Environment.MEDIA_UNMOUNTED)) {
-                int rc = doMountVolume(path);
-                if (rc != StorageResultCode.OperationSucceeded) {
-                    Slog.e(TAG, String.format("Boot-time mount failed (%d)",
-                            rc));
-                }
-            } else if (state.equals(Environment.MEDIA_SHARED)) {
-                /*
-                 * Bootstrap UMS enabled state since vold indicates
-                 * the volume is shared (runtime restart while ums enabled)
-                 */
-                notifyVolumeStateChange(null, path, VolumeState.NoMedia,
-                        VolumeState.Shared);
-            }
-        }
-
-        // Push mounted state for all emulated storage
-        synchronized (mVolumesLock) {
-            for (StorageVolume volume : mVolumes) {
-                if (volume.isEmulated()) {
-                    updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
-                }
-            }
-        }
-
-        /*
-         * If UMS was connected on boot, send the connected event
-         * now that we're up.
-         */
-        if (mSendUmsConnectedOnBoot) {
-            sendUmsIntent(true);
-            mSendUmsConnectedOnBoot = false;
-        }
-
-        /*
-         * Start scheduling nominally-daily fstrim operations
-         */
+        // Start scheduling nominally-daily fstrim operations
         MountServiceIdler.scheduleIdlePass(mContext);
     }
 
-    private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
-            if (userId == -1) return;
-            final UserHandle user = new UserHandle(userId);
+    private void resetIfReadyAndConnected() {
+        Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady
+                + ", mDaemonConnected=" + mDaemonConnected);
+        if (mSystemReady && mDaemonConnected) {
+            mDisks.clear();
+            mVolumes.clear();
 
-            final String action = intent.getAction();
-            if (Intent.ACTION_USER_ADDED.equals(action)) {
-                synchronized (mVolumesLock) {
-                    createEmulatedVolumeForUserLocked(user);
-                }
-
-            } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
-                synchronized (mVolumesLock) {
-                    final List<StorageVolume> toRemove = Lists.newArrayList();
-                    for (StorageVolume volume : mVolumes) {
-                        if (user.equals(volume.getOwner())) {
-                            toRemove.add(volume);
-                        }
-                    }
-                    for (StorageVolume volume : toRemove) {
-                        removeVolumeLocked(volume);
-                    }
-                }
+            try {
+                mConnector.execute("volume", "reset");
+            } catch (NativeDaemonConnectorException e) {
+                Slog.w(TAG, "Failed to reset vold", e);
             }
         }
-    };
+    }
 
-    private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            boolean available = (intent.getBooleanExtra(UsbManager.USB_CONNECTED, false) &&
-                    intent.getBooleanExtra(UsbManager.USB_FUNCTION_MASS_STORAGE, false));
-            notifyShareAvailabilityChange(available);
+    private void onStartUser(int userId) {
+        Slog.d(TAG, "onStartUser " + userId);
+
+        // We purposefully block here to make sure that user-specific
+        // staging area is ready so it's ready for zygote-forked apps to
+        // bind mount against.
+        try {
+            mConnector.execute("volume", "start_user", userId);
+        } catch (NativeDaemonConnectorException ignored) {
         }
-    };
+
+        // Record user as started so newly mounted volumes kick off events
+        // correctly, then synthesize events for any already-mounted volumes.
+        synchronized (mVolumes) {
+            for (Volume vol : mVolumes.values()) {
+                if (vol.isVisibleToUser(userId) && vol.state == Volume.STATE_MOUNTED) {
+                    final StorageVolume userVol = vol.buildVolumeForUser(userId);
+                    mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
+                }
+            }
+            mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userId);
+        }
+    }
+
+    private void onCleanupUser(int userId) {
+        Slog.d(TAG, "onCleanupUser " + userId);
+
+        try {
+            mConnector.execute("volume", "cleanup_user", userId);
+        } catch (NativeDaemonConnectorException ignored) {
+        }
+
+        synchronized (mVolumes) {
+            mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userId);
+        }
+    }
 
     private final class MountServiceBinderListener implements IBinder.DeathRecipient {
         final IMountServiceListener mListener;
 
         MountServiceBinderListener(IMountServiceListener listener) {
             mListener = listener;
-
         }
 
+        @Override
         public void binderDied() {
             if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
             synchronized (mListeners) {
@@ -741,7 +816,7 @@
     // Binder entry point for kicking off an immediate fstrim
     @Override
     public void runMaintenance() {
-        validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
         runIdleMaintenance(null);
     }
 
@@ -750,144 +825,35 @@
         return mLastMaintenance;
     }
 
-    private void doShareUnshareVolume(String path, String method, boolean enable) {
-        // TODO: Add support for multiple share methods
-        if (!method.equals("ums")) {
-            throw new IllegalArgumentException(String.format("Method %s not supported", method));
-        }
-
-        try {
-            mConnector.execute("volume", enable ? "share" : "unshare", path, method);
-        } catch (NativeDaemonConnectorException e) {
-            Slog.e(TAG, "Failed to share/unshare", e);
-        }
-    }
-
-    private void updatePublicVolumeState(StorageVolume volume, String state) {
-        final String path = volume.getPath();
-        final String oldState;
-        synchronized (mVolumesLock) {
-            oldState = mVolumeStates.put(path, state);
-            volume.setState(state);
-        }
-
-        if (state.equals(oldState)) {
-            Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s",
-                    state, state, path));
-            return;
-        }
-
-        Slog.d(TAG, "volume state changed for " + path + " (" + oldState + " -> " + state + ")");
-
-        // Tell PackageManager about changes to primary volume state, but only
-        // when not emulated.
-        if (volume.isPrimary() && !volume.isEmulated()) {
-            if (Environment.MEDIA_UNMOUNTED.equals(state)) {
-                mPms.updateExternalMediaStatus(false, false);
-
-                /*
-                 * Some OBBs might have been unmounted when this volume was
-                 * unmounted, so send a message to the handler to let it know to
-                 * remove those from the list of mounted OBBS.
-                 */
-                mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
-                        OBB_FLUSH_MOUNT_STATE, path));
-            } else if (Environment.MEDIA_MOUNTED.equals(state)) {
-                mPms.updateExternalMediaStatus(true, false);
-            }
-        }
-
-        synchronized (mListeners) {
-            for (int i = mListeners.size() -1; i >= 0; i--) {
-                MountServiceBinderListener bl = mListeners.get(i);
-                try {
-                    bl.mListener.onStorageStateChanged(path, oldState, state);
-                } catch (RemoteException rex) {
-                    Slog.e(TAG, "Listener dead");
-                    mListeners.remove(i);
-                } catch (Exception ex) {
-                    Slog.e(TAG, "Listener failed", ex);
-                }
-            }
-        }
-    }
-
     /**
      * Callback from NativeDaemonConnector
      */
+    @Override
     public void onDaemonConnected() {
+        mDaemonConnected = true;
+        mHandler.obtainMessage(H_DAEMON_CONNECTED).sendToTarget();
+    }
+
+    private void handleDaemonConnected() {
+        resetIfReadyAndConnected();
+
         /*
-         * Since we'll be calling back into the NativeDaemonConnector,
-         * we need to do our work in a new thread.
+         * Now that we've done our initialization, release
+         * the hounds!
          */
-        new Thread("MountService#onDaemonConnected") {
-            @Override
-            public void run() {
-                /**
-                 * Determine media state and UMS detection status
-                 */
-                try {
-                    final String[] vols = NativeDaemonEvent.filterMessageList(
-                            mConnector.executeForList("volume", "list", "broadcast"),
-                            VoldResponseCode.VolumeListResult);
-                    for (String volstr : vols) {
-                        String[] tok = volstr.split(" ");
-                        // FMT: <label> <mountpoint> <state>
-                        String path = tok[1];
-                        String state = Environment.MEDIA_REMOVED;
+        mConnectedSignal.countDown();
 
-                        final StorageVolume volume;
-                        synchronized (mVolumesLock) {
-                            volume = mVolumesByPath.get(path);
-                        }
+        // On an encrypted device we can't see system properties yet, so pull
+        // the system locale out of the mount service.
+        if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
+            copyLocaleFromMountService();
+        }
 
-                        int st = Integer.parseInt(tok[2]);
-                        if (st == VolumeState.NoMedia) {
-                            state = Environment.MEDIA_REMOVED;
-                        } else if (st == VolumeState.Idle) {
-                            state = Environment.MEDIA_UNMOUNTED;
-                        } else if (st == VolumeState.Mounted) {
-                            state = Environment.MEDIA_MOUNTED;
-                            Slog.i(TAG, "Media already mounted on daemon connection");
-                        } else if (st == VolumeState.Shared) {
-                            state = Environment.MEDIA_SHARED;
-                            Slog.i(TAG, "Media shared on daemon connection");
-                        } else {
-                            throw new Exception(String.format("Unexpected state %d", st));
-                        }
+        // Let package manager load internal ASECs.
+        mPms.scanAvailableAsecs();
 
-                        if (state != null) {
-                            if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
-                            updatePublicVolumeState(volume, state);
-                        }
-                    }
-                } catch (Exception e) {
-                    Slog.e(TAG, "Error processing initial volume state", e);
-                    final StorageVolume primary = getPrimaryPhysicalVolume();
-                    if (primary != null) {
-                        updatePublicVolumeState(primary, Environment.MEDIA_REMOVED);
-                    }
-                }
-
-                /*
-                 * Now that we've done our initialization, release
-                 * the hounds!
-                 */
-                mConnectedSignal.countDown();
-
-                // On an encrypted device we can't see system properties yet, so pull
-                // the system locale out of the mount service.
-                if ("".equals(SystemProperties.get("vold.encrypt_progress"))) {
-                    copyLocaleFromMountService();
-                }
-
-                // Let package manager load internal ASECs.
-                mPms.scanAvailableAsecs();
-
-                // Notify people waiting for ASECs to be scanned that it's done.
-                mAsecsScanned.countDown();
-            }
-        }.start();
+        // Notify people waiting for ASECs to be scanned that it's done.
+        mAsecsScanned.countDown();
     }
 
     private void copyLocaleFromMountService() {
@@ -919,6 +885,7 @@
     /**
      * Callback from NativeDaemonConnector
      */
+    @Override
     public boolean onCheckHoldWakeLock(int code) {
         return false;
     }
@@ -926,347 +893,182 @@
     /**
      * Callback from NativeDaemonConnector
      */
+    @Override
     public boolean onEvent(int code, String raw, String[] cooked) {
-        if (DEBUG_EVENTS) {
-            StringBuilder builder = new StringBuilder();
-            builder.append("onEvent::");
-            builder.append(" raw= " + raw);
-            if (cooked != null) {
-                builder.append(" cooked = " );
-                for (String str : cooked) {
-                    builder.append(" " + str);
-                }
-            }
-            Slog.i(TAG, builder.toString());
+        synchronized (mLock) {
+            return onEventLocked(code, raw, cooked);
         }
-        if (code == VoldResponseCode.VolumeStateChange) {
-            /*
-             * One of the volumes we're managing has changed state.
-             * Format: "NNN Volume <label> <path> state changed
-             * from <old_#> (<old_str>) to <new_#> (<new_str>)"
-             */
-            notifyVolumeStateChange(
-                    cooked[2], cooked[3], Integer.parseInt(cooked[7]),
-                            Integer.parseInt(cooked[10]));
-        } else if (code == VoldResponseCode.VolumeUuidChange) {
-            // Format: nnn <label> <path> <uuid>
-            final String path = cooked[2];
-            final String uuid = (cooked.length > 3) ? cooked[3] : null;
+    }
 
-            final StorageVolume vol = mVolumesByPath.get(path);
-            if (vol != null) {
-                vol.setUuid(uuid);
+    private boolean onEventLocked(int code, String raw, String[] cooked) {
+        switch (code) {
+            case VoldResponseCode.DISK_CREATED: {
+                if (cooked.length != 3) break;
+                final String id = cooked[1];
+                final int flags = Integer.parseInt(cooked[2]);
+                mDisks.put(id, new Disk(id, flags));
+                break;
             }
-
-        } else if (code == VoldResponseCode.VolumeUserLabelChange) {
-            // Format: nnn <label> <path> <label>
-            final String path = cooked[2];
-            final String userLabel = (cooked.length > 3) ? cooked[3] : null;
-
-            final StorageVolume vol = mVolumesByPath.get(path);
-            if (vol != null) {
-                vol.setUserLabel(userLabel);
-            }
-
-        } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
-                   (code == VoldResponseCode.VolumeDiskRemoved) ||
-                   (code == VoldResponseCode.VolumeBadRemoval)) {
-            // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
-            // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
-            // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
-            String action = null;
-            final String label = cooked[2];
-            final String path = cooked[3];
-            int major = -1;
-            int minor = -1;
-
-            try {
-                String devComp = cooked[6].substring(1, cooked[6].length() -1);
-                String[] devTok = devComp.split(":");
-                major = Integer.parseInt(devTok[0]);
-                minor = Integer.parseInt(devTok[1]);
-            } catch (Exception ex) {
-                Slog.e(TAG, "Failed to parse major/minor", ex);
-            }
-
-            final StorageVolume volume;
-            final String state;
-            synchronized (mVolumesLock) {
-                volume = mVolumesByPath.get(path);
-                state = mVolumeStates.get(path);
-            }
-
-            if (code == VoldResponseCode.VolumeDiskInserted) {
-                new Thread("MountService#VolumeDiskInserted") {
-                    @Override
-                    public void run() {
-                        try {
-                            int rc;
-                            if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
-                                Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));
-                            }
-                        } catch (Exception ex) {
-                            Slog.w(TAG, "Failed to mount media on insertion", ex);
-                        }
-                    }
-                }.start();
-            } else if (code == VoldResponseCode.VolumeDiskRemoved) {
-                /*
-                 * This event gets trumped if we're already in BAD_REMOVAL state
-                 */
-                if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
-                    return true;
+            case VoldResponseCode.DISK_SIZE_CHANGED: {
+                if (cooked.length != 3) break;
+                final Disk disk = mDisks.get(cooked[1]);
+                if (disk != null) {
+                    disk.size = Long.parseLong(cooked[2]);
                 }
-                /* Send the media unmounted event first */
-                if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
-                updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
-                sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
+                break;
+            }
+            case VoldResponseCode.DISK_LABEL_CHANGED: {
+                if (cooked.length != 3) break;
+                final Disk disk = mDisks.get(cooked[1]);
+                if (disk != null) {
+                    disk.label = cooked[2];
+                }
+                break;
+            }
+            case VoldResponseCode.DISK_VOLUME_CREATED: {
+                if (cooked.length != 3) break;
+                final Disk disk = mDisks.get(cooked[1]);
+                final Volume vol = mVolumes.get(cooked[2]);
+                if (disk != null && vol != null) {
+                    disk.volumes.add(vol);
+                }
+                break;
+            }
+            case VoldResponseCode.DISK_DESTROYED: {
+                if (cooked.length != 2) break;
+                mDisks.remove(cooked[1]);
+                break;
+            }
 
-                if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
-                updatePublicVolumeState(volume, Environment.MEDIA_REMOVED);
-                action = Intent.ACTION_MEDIA_REMOVED;
-            } else if (code == VoldResponseCode.VolumeBadRemoval) {
-                if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
-                /* Send the media unmounted event first */
-                updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
-                sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
+            case VoldResponseCode.VOLUME_CREATED: {
+                if (cooked.length != 3) break;
+                final String id = cooked[1];
+                final int type = Integer.parseInt(cooked[2]);
+                final Volume vol = new Volume(id, type);
+                mVolumes.put(id, vol);
+                onVolumeCreatedLocked(vol);
+                break;
+            }
+            case VoldResponseCode.VOLUME_STATE_CHANGED: {
+                if (cooked.length != 3) break;
+                final Volume vol = mVolumes.get(cooked[1]);
+                if (vol != null) {
+                    final int oldState = vol.state;
+                    final int newState = Integer.parseInt(cooked[2]);
+                    vol.state = newState;
+                    onVolumeStateChangedLocked(vol, oldState, newState);
+                }
+                break;
+            }
+            case VoldResponseCode.VOLUME_FS_TYPE_CHANGED: {
+                if (cooked.length != 3) break;
+                final Volume vol = mVolumes.get(cooked[1]);
+                if (vol != null) {
+                    vol.fsType = cooked[2];
+                }
+                break;
+            }
+            case VoldResponseCode.VOLUME_FS_UUID_CHANGED: {
+                if (cooked.length != 3) break;
+                final Volume vol = mVolumes.get(cooked[1]);
+                if (vol != null) {
+                    vol.fsUuid = cooked[2];
+                }
+                break;
+            }
+            case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: {
+                if (cooked.length != 3) break;
+                final Volume vol = mVolumes.get(cooked[1]);
+                if (vol != null) {
+                    vol.fsLabel = cooked[2];
+                }
+                break;
+            }
+            case VoldResponseCode.VOLUME_PATH_CHANGED: {
+                if (cooked.length != 3) break;
+                final Volume vol = mVolumes.get(cooked[1]);
+                if (vol != null) {
+                    vol.path = cooked[2];
+                }
+                break;
+            }
+            case VoldResponseCode.VOLUME_DESTROYED: {
+                if (cooked.length != 2) break;
+                mVolumes.remove(cooked[1]);
+                break;
+            }
 
-                if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
-                updatePublicVolumeState(volume, Environment.MEDIA_BAD_REMOVAL);
-                action = Intent.ACTION_MEDIA_BAD_REMOVAL;
-            } else if (code == VoldResponseCode.FstrimCompleted) {
+            case VoldResponseCode.FstrimCompleted: {
                 EventLogTags.writeFstrimFinish(SystemClock.elapsedRealtime());
-            } else {
-                Slog.e(TAG, String.format("Unknown code {%d}", code));
+                break;
             }
-
-            if (action != null) {
-                sendStorageIntent(action, volume, UserHandle.ALL);
+            default: {
+                Slog.d(TAG, "Unhandled vold event " + code);
             }
-        } else {
-            return false;
         }
 
         return true;
     }
 
-    private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
-        final StorageVolume volume;
-        final String state;
-        synchronized (mVolumesLock) {
-            volume = mVolumesByPath.get(path);
-            state = getVolumeState(path);
-        }
+    private void onVolumeCreatedLocked(Volume vol) {
+        final boolean primaryPhysical = SystemProperties.getBoolean(PROP_PRIMARY_PHYSICAL, false);
+        if (vol.type == Volume.TYPE_EMULATED && !primaryPhysical) {
+            vol.flags |= Volume.FLAG_PRIMARY;
+            vol.flags |= Volume.FLAG_VISIBLE;
+            mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
 
-        if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChange::" + state);
-
-        String action = null;
-
-        if (oldState == VolumeState.Shared && newState != oldState) {
-            if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
-            sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED, volume, UserHandle.ALL);
-        }
-
-        if (newState == VolumeState.Init) {
-        } else if (newState == VolumeState.NoMedia) {
-            // NoMedia is handled via Disk Remove events
-        } else if (newState == VolumeState.Idle) {
-            /*
-             * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
-             * if we're in the process of enabling UMS
-             */
-            if (!state.equals(
-                    Environment.MEDIA_BAD_REMOVAL) && !state.equals(
-                            Environment.MEDIA_NOFS) && !state.equals(
-                                    Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
-                if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
-                updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
-                action = Intent.ACTION_MEDIA_UNMOUNTED;
+        } else if (vol.type == Volume.TYPE_PUBLIC) {
+            if (primaryPhysical) {
+                vol.flags |= Volume.FLAG_PRIMARY;
             }
-        } else if (newState == VolumeState.Pending) {
-        } else if (newState == VolumeState.Checking) {
-            if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
-            updatePublicVolumeState(volume, Environment.MEDIA_CHECKING);
-            action = Intent.ACTION_MEDIA_CHECKING;
-        } else if (newState == VolumeState.Mounted) {
-            if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
-            updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
-            action = Intent.ACTION_MEDIA_MOUNTED;
-        } else if (newState == VolumeState.Unmounting) {
-            action = Intent.ACTION_MEDIA_EJECT;
-        } else if (newState == VolumeState.Formatting) {
-        } else if (newState == VolumeState.Shared) {
-            if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
-            /* Send the media unmounted event first */
-            updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTED);
-            sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, volume, UserHandle.ALL);
+            vol.flags |= Volume.FLAG_VISIBLE;
+            vol.userId = UserHandle.USER_OWNER;
+            mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
 
-            if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
-            updatePublicVolumeState(volume, Environment.MEDIA_SHARED);
-            action = Intent.ACTION_MEDIA_SHARED;
-            if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
-        } else if (newState == VolumeState.SharedMnt) {
-            Slog.e(TAG, "Live shared mounts not supported yet!");
-            return;
         } else {
-            Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
-        }
-
-        if (action != null) {
-            sendStorageIntent(action, volume, UserHandle.ALL);
+            Slog.d(TAG, "Skipping automatic mounting of " + vol);
         }
     }
 
-    private int doMountVolume(String path) {
-        int rc = StorageResultCode.OperationSucceeded;
-
-        final StorageVolume volume;
-        synchronized (mVolumesLock) {
-            volume = mVolumesByPath.get(path);
+    private void onVolumeStateChangedLocked(Volume vol, int oldState, int newState) {
+        // Kick state changed event towards all started users. Any users
+        // started after this point will trigger additional
+        // user-specific broadcasts.
+        for (int userId : mStartedUsers) {
+            if (vol.isVisibleToUser(userId)) {
+                final StorageVolume userVol = vol.buildVolumeForUser(userId);
+                mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();
+            }
         }
 
-        if (!volume.isEmulated() && hasUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA)) {
-            Slog.w(TAG, "User has restriction DISALLOW_MOUNT_PHYSICAL_MEDIA; cannot mount volume.");
-            return StorageResultCode.OperationFailedInternalError;
-        }
+        // Tell PackageManager about changes to primary volume state, but only
+        // when not emulated.
+        if (vol.isPrimary() && vol.type == Volume.TYPE_PUBLIC) {
+            if (vol.state == Volume.STATE_MOUNTED) {
+                mPms.updateExternalMediaStatus(true, false);
 
-        if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);
-        try {
-            mConnector.execute("volume", "mount", path);
-        } catch (NativeDaemonConnectorException e) {
-            /*
-             * Mount failed for some reason
-             */
-            String action = null;
-            int code = e.getCode();
-            if (code == VoldResponseCode.OpFailedNoMedia) {
+            } else if (vol.state == Volume.STATE_UNMOUNTING) {
+                mPms.updateExternalMediaStatus(false, false);
+
+                // TODO: this should eventually be handled by new ObbVolume state changes
                 /*
-                 * Attempt to mount but no media inserted
+                 * Some OBBs might have been unmounted when this volume was
+                 * unmounted, so send a message to the handler to let it know to
+                 * remove those from the list of mounted OBBS.
                  */
-                rc = StorageResultCode.OperationFailedNoMedia;
-            } else if (code == VoldResponseCode.OpFailedMediaBlank) {
-                if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");
-                /*
-                 * Media is blank or does not contain a supported filesystem
-                 */
-                updatePublicVolumeState(volume, Environment.MEDIA_NOFS);
-                action = Intent.ACTION_MEDIA_NOFS;
-                rc = StorageResultCode.OperationFailedMediaBlank;
-            } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
-                if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");
-                /*
-                 * Volume consistency check failed
-                 */
-                updatePublicVolumeState(volume, Environment.MEDIA_UNMOUNTABLE);
-                action = Intent.ACTION_MEDIA_UNMOUNTABLE;
-                rc = StorageResultCode.OperationFailedMediaCorrupt;
-            } else {
-                rc = StorageResultCode.OperationFailedInternalError;
-            }
-
-            /*
-             * Send broadcast intent (if required for the failure)
-             */
-            if (action != null) {
-                sendStorageIntent(action, volume, UserHandle.ALL);
+                mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(
+                        OBB_FLUSH_MOUNT_STATE, vol.path));
             }
         }
 
-        return rc;
-    }
+        final String oldEnvState = sStateToEnvironment.get(oldState);
+        final String newEnvState = sStateToEnvironment.get(newState);
 
-    /*
-     * If force is not set, we do not unmount if there are
-     * processes holding references to the volume about to be unmounted.
-     * If force is set, all the processes holding references need to be
-     * killed via the ActivityManager before actually unmounting the volume.
-     * This might even take a while and might be retried after timed delays
-     * to make sure we dont end up in an instable state and kill some core
-     * processes.
-     * If removeEncryption is set, force is implied, and the system will remove any encryption
-     * mapping set on the volume when unmounting.
-     */
-    private int doUnmountVolume(String path, boolean force, boolean removeEncryption) {
-        if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
-            return VoldResponseCode.OpFailedVolNotMounted;
-        }
-
-        /*
-         * Force a GC to make sure AssetManagers in other threads of the
-         * system_server are cleaned up. We have to do this since AssetManager
-         * instances are kept as a WeakReference and it's possible we have files
-         * open on the external storage.
-         */
-        Runtime.getRuntime().gc();
-
-        // Redundant probably. But no harm in updating state again.
-        mPms.updateExternalMediaStatus(false, false);
-        try {
-            final Command cmd = new Command("volume", "unmount", path);
-            if (removeEncryption) {
-                cmd.appendArg("force_and_revert");
-            } else if (force) {
-                cmd.appendArg("force");
-            }
-            mConnector.execute(cmd);
-            // We unmounted the volume. None of the asec containers are available now.
-            synchronized (mAsecMountSet) {
-                mAsecMountSet.clear();
-            }
-            return StorageResultCode.OperationSucceeded;
-        } catch (NativeDaemonConnectorException e) {
-            // Don't worry about mismatch in PackageManager since the
-            // call back will handle the status changes any way.
-            int code = e.getCode();
-            if (code == VoldResponseCode.OpFailedVolNotMounted) {
-                return StorageResultCode.OperationFailedStorageNotMounted;
-            } else if (code == VoldResponseCode.OpFailedStorageBusy) {
-                return StorageResultCode.OperationFailedStorageBusy;
-            } else {
-                return StorageResultCode.OperationFailedInternalError;
-            }
-        }
-    }
-
-    private int doFormatVolume(String path) {
-        try {
-            mConnector.execute("volume", "format", path);
-            return StorageResultCode.OperationSucceeded;
-        } catch (NativeDaemonConnectorException e) {
-            int code = e.getCode();
-            if (code == VoldResponseCode.OpFailedNoMedia) {
-                return StorageResultCode.OperationFailedNoMedia;
-            } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
-                return StorageResultCode.OperationFailedMediaCorrupt;
-            } else {
-                return StorageResultCode.OperationFailedInternalError;
-            }
-        }
-    }
-
-    private boolean doGetVolumeShared(String path, String method) {
-        final NativeDaemonEvent event;
-        try {
-            event = mConnector.execute("volume", "shared", path, method);
-        } catch (NativeDaemonConnectorException ex) {
-            Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);
-            return false;
-        }
-
-        if (event.getCode() == VoldResponseCode.ShareEnabledResult) {
-            return event.getMessage().endsWith("enabled");
-        } else {
-            return false;
-        }
-    }
-
-    private void notifyShareAvailabilityChange(final boolean avail) {
         synchronized (mListeners) {
-            mUmsAvailable = avail;
             for (int i = mListeners.size() -1; i >= 0; i--) {
                 MountServiceBinderListener bl = mListeners.get(i);
                 try {
-                    bl.mListener.onUsbMassStorageConnectionChanged(avail);
+                    bl.mListener.onStorageStateChanged(vol.path, oldEnvState, newEnvState);
                 } catch (RemoteException rex) {
                     Slog.e(TAG, "Listener dead");
                     mListeners.remove(i);
@@ -1275,221 +1077,19 @@
                 }
             }
         }
-
-        if (mSystemReady == true) {
-            sendUmsIntent(avail);
-        } else {
-            mSendUmsConnectedOnBoot = avail;
-        }
-
-        final StorageVolume primary = getPrimaryPhysicalVolume();
-        if (avail == false && primary != null
-                && Environment.MEDIA_SHARED.equals(getVolumeState(primary.getPath()))) {
-            final String path = primary.getPath();
-            /*
-             * USB mass storage disconnected while enabled
-             */
-            new Thread("MountService#AvailabilityChange") {
-                @Override
-                public void run() {
-                    try {
-                        int rc;
-                        Slog.w(TAG, "Disabling UMS after cable disconnect");
-                        doShareUnshareVolume(path, "ums", false);
-                        if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
-                            Slog.e(TAG, String.format(
-                                    "Failed to remount {%s} on UMS enabled-disconnect (%d)",
-                                            path, rc));
-                        }
-                    } catch (Exception ex) {
-                        Slog.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex);
-                    }
-                }
-            }.start();
-        }
     }
 
-    private void sendStorageIntent(String action, StorageVolume volume, UserHandle user) {
-        final Intent intent = new Intent(action, Uri.parse("file://" + volume.getPath()));
-        intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, volume);
-        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-        Slog.d(TAG, "sendStorageIntent " + intent + " to " + user);
-        mContext.sendBroadcastAsUser(intent, user);
+    private void enforcePermission(String perm) {
+        mContext.enforceCallingOrSelfPermission(perm, perm);
     }
 
-    private void sendUmsIntent(boolean c) {
-        mContext.sendBroadcastAsUser(
-                new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)),
-                UserHandle.ALL);
-    }
-
-    private void validatePermission(String perm) {
-        if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException(String.format("Requires %s permission", perm));
-        }
-    }
-
-    private boolean hasUserRestriction(String restriction) {
+    private void enforceUserRestriction(String restriction) {
         UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        return um.hasUserRestriction(restriction, Binder.getCallingUserHandle());
-    }
-
-    private void validateUserRestriction(String restriction) {
-        if (hasUserRestriction(restriction)) {
+        if (um.hasUserRestriction(restriction, Binder.getCallingUserHandle())) {
             throw new SecurityException("User has restriction " + restriction);
         }
     }
 
-    // Storage list XML tags
-    private static final String TAG_STORAGE_LIST = "StorageList";
-    private static final String TAG_STORAGE = "storage";
-
-    private void readStorageListLocked() {
-        mVolumes.clear();
-        mVolumeStates.clear();
-
-        Resources resources = mContext.getResources();
-
-        int id = com.android.internal.R.xml.storage_list;
-        XmlResourceParser parser = resources.getXml(id);
-        AttributeSet attrs = Xml.asAttributeSet(parser);
-
-        try {
-            XmlUtils.beginDocument(parser, TAG_STORAGE_LIST);
-            while (true) {
-                XmlUtils.nextElement(parser);
-
-                String element = parser.getName();
-                if (element == null) break;
-
-                if (TAG_STORAGE.equals(element)) {
-                    TypedArray a = resources.obtainAttributes(attrs,
-                            com.android.internal.R.styleable.Storage);
-
-                    String path = a.getString(
-                            com.android.internal.R.styleable.Storage_mountPoint);
-                    int descriptionId = a.getResourceId(
-                            com.android.internal.R.styleable.Storage_storageDescription, -1);
-                    CharSequence description = a.getText(
-                            com.android.internal.R.styleable.Storage_storageDescription);
-                    boolean primary = a.getBoolean(
-                            com.android.internal.R.styleable.Storage_primary, false);
-                    boolean removable = a.getBoolean(
-                            com.android.internal.R.styleable.Storage_removable, false);
-                    boolean emulated = a.getBoolean(
-                            com.android.internal.R.styleable.Storage_emulated, false);
-                    int mtpReserve = a.getInt(
-                            com.android.internal.R.styleable.Storage_mtpReserve, 0);
-                    boolean allowMassStorage = a.getBoolean(
-                            com.android.internal.R.styleable.Storage_allowMassStorage, false);
-                    // resource parser does not support longs, so XML value is in megabytes
-                    long maxFileSize = a.getInt(
-                            com.android.internal.R.styleable.Storage_maxFileSize, 0) * 1024L * 1024L;
-
-                    Slog.d(TAG, "got storage path: " + path + " description: " + description +
-                            " primary: " + primary + " removable: " + removable +
-                            " emulated: " + emulated +  " mtpReserve: " + mtpReserve +
-                            " allowMassStorage: " + allowMassStorage +
-                            " maxFileSize: " + maxFileSize);
-
-                    if (emulated) {
-                        // For devices with emulated storage, we create separate
-                        // volumes for each known user.
-                        mEmulatedTemplate = new StorageVolume(null, descriptionId, true, false,
-                                true, mtpReserve, false, maxFileSize, null);
-
-                        final UserManagerService userManager = UserManagerService.getInstance();
-                        for (UserInfo user : userManager.getUsers(false)) {
-                            createEmulatedVolumeForUserLocked(user.getUserHandle());
-                        }
-
-                    } else {
-                        if (path == null || description == null) {
-                            Slog.e(TAG, "Missing storage path or description in readStorageList");
-                        } else {
-                            final StorageVolume volume = new StorageVolume(new File(path),
-                                    descriptionId, primary, removable, emulated, mtpReserve,
-                                    allowMassStorage, maxFileSize, null);
-                            addVolumeLocked(volume);
-
-                            // Until we hear otherwise, treat as unmounted
-                            mVolumeStates.put(volume.getPath(), Environment.MEDIA_UNMOUNTED);
-                            volume.setState(Environment.MEDIA_UNMOUNTED);
-                        }
-                    }
-
-                    a.recycle();
-                }
-            }
-        } catch (XmlPullParserException e) {
-            throw new RuntimeException(e);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        } finally {
-            // Compute storage ID for each physical volume; emulated storage is
-            // always 0 when defined.
-            int index = isExternalStorageEmulated() ? 1 : 0;
-            for (StorageVolume volume : mVolumes) {
-                if (!volume.isEmulated()) {
-                    volume.setStorageId(index++);
-                }
-            }
-            parser.close();
-        }
-    }
-
-    /**
-     * Create and add new {@link StorageVolume} for given {@link UserHandle}
-     * using {@link #mEmulatedTemplate} as template.
-     */
-    private void createEmulatedVolumeForUserLocked(UserHandle user) {
-        if (mEmulatedTemplate == null) {
-            throw new IllegalStateException("Missing emulated volume multi-user template");
-        }
-
-        final UserEnvironment userEnv = new UserEnvironment(user.getIdentifier());
-        final File path = userEnv.getExternalStorageDirectory();
-        final StorageVolume volume = StorageVolume.fromTemplate(mEmulatedTemplate, path, user);
-        volume.setStorageId(0);
-        addVolumeLocked(volume);
-
-        if (mSystemReady) {
-            updatePublicVolumeState(volume, Environment.MEDIA_MOUNTED);
-        } else {
-            // Place stub status for early callers to find
-            mVolumeStates.put(volume.getPath(), Environment.MEDIA_MOUNTED);
-            volume.setState(Environment.MEDIA_MOUNTED);
-        }
-    }
-
-    private void addVolumeLocked(StorageVolume volume) {
-        Slog.d(TAG, "addVolumeLocked() " + volume);
-        mVolumes.add(volume);
-        final StorageVolume existing = mVolumesByPath.put(volume.getPath(), volume);
-        if (existing != null) {
-            throw new IllegalStateException(
-                    "Volume at " + volume.getPath() + " already exists: " + existing);
-        }
-    }
-
-    private void removeVolumeLocked(StorageVolume volume) {
-        Slog.d(TAG, "removeVolumeLocked() " + volume);
-        mVolumes.remove(volume);
-        mVolumesByPath.remove(volume.getPath());
-        mVolumeStates.remove(volume.getPath());
-    }
-
-    private StorageVolume getPrimaryPhysicalVolume() {
-        synchronized (mVolumesLock) {
-            for (StorageVolume volume : mVolumes) {
-                if (volume.isPrimary() && !volume.isEmulated()) {
-                    return volume;
-                }
-            }
-        }
-        return null;
-    }
-
     /**
      * Constructs a new MountService instance
      *
@@ -1500,10 +1100,6 @@
 
         mContext = context;
 
-        synchronized (mVolumesLock) {
-            readStorageListLocked();
-        }
-
         // XXX: This will go away soon in favor of IMountServiceObserver
         mPms = (PackageManagerService) ServiceManager.getService("package");
 
@@ -1511,19 +1107,6 @@
         hthread.start();
         mHandler = new MountServiceHandler(hthread.getLooper());
 
-        // Watch for user changes
-        final IntentFilter userFilter = new IntentFilter();
-        userFilter.addAction(Intent.ACTION_USER_ADDED);
-        userFilter.addAction(Intent.ACTION_USER_REMOVED);
-        mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);
-
-        // Watch for USB changes on primary volume
-        final StorageVolume primary = getPrimaryPhysicalVolume();
-        if (primary != null && primary.allowMassStorage()) {
-            mContext.registerReceiver(
-                    mUsbReceiver, new IntentFilter(UsbManager.ACTION_USB_STATE), null, mHandler);
-        }
-
         // Add OBB Action Handler to MountService thread.
         mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());
 
@@ -1550,6 +1133,7 @@
          */
         mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,
                 null);
+        mConnector.setDebug(true);
 
         Thread thread = new Thread(mConnector, VOLD_TAG);
         thread.start();
@@ -1593,201 +1177,118 @@
         }
     }
 
+    @Override
     public void shutdown(final IMountShutdownObserver observer) {
-        validatePermission(android.Manifest.permission.SHUTDOWN);
+        enforcePermission(android.Manifest.permission.SHUTDOWN);
 
         Slog.i(TAG, "Shutting down");
-        synchronized (mVolumesLock) {
-            // Get all volumes to be unmounted.
-            MountShutdownLatch mountShutdownLatch = new MountShutdownLatch(observer,
-                                                            mVolumeStates.size());
-
-            for (String path : mVolumeStates.keySet()) {
-                String state = mVolumeStates.get(path);
-
-                if (state.equals(Environment.MEDIA_SHARED)) {
-                    /*
-                     * If the media is currently shared, unshare it.
-                     * XXX: This is still dangerous!. We should not
-                     * be rebooting at *all* if UMS is enabled, since
-                     * the UMS host could have dirty FAT cache entries
-                     * yet to flush.
-                     */
-                    setUsbMassStorageEnabled(false);
-                } else if (state.equals(Environment.MEDIA_CHECKING)) {
-                    /*
-                     * If the media is being checked, then we need to wait for
-                     * it to complete before being able to proceed.
-                     */
-                    // XXX: @hackbod - Should we disable the ANR timer here?
-                    int retries = 30;
-                    while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
-                        try {
-                            Thread.sleep(1000);
-                        } catch (InterruptedException iex) {
-                            Slog.e(TAG, "Interrupted while waiting for media", iex);
-                            break;
-                        }
-                        state = Environment.getExternalStorageState();
-                    }
-                    if (retries == 0) {
-                        Slog.e(TAG, "Timed out waiting for media to check");
-                    }
-                }
-
-                if (state.equals(Environment.MEDIA_MOUNTED)) {
-                    // Post a unmount message.
-                    ShutdownCallBack ucb = new ShutdownCallBack(path, mountShutdownLatch);
-                    mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
-                } else if (observer != null) {
-                    /*
-                     * Count down, since nothing will be done. The observer will be
-                     * notified when we are done so shutdown sequence can continue.
-                     */
-                    mountShutdownLatch.countDown();
-                    Slog.i(TAG, "Unmount completed: " + path +
-                        ", result code: " + StorageResultCode.OperationSucceeded);
-                }
-            }
-        }
+        mHandler.obtainMessage(H_SHUTDOWN, observer).sendToTarget();
     }
 
-    private boolean getUmsEnabling() {
-        synchronized (mListeners) {
-            return mUmsEnabling;
-        }
-    }
-
-    private void setUmsEnabling(boolean enable) {
-        synchronized (mListeners) {
-            mUmsEnabling = enable;
-        }
-    }
-
+    @Override
+    @Deprecated
     public boolean isUsbMassStorageConnected() {
-        waitForReady();
-
-        if (getUmsEnabling()) {
-            return true;
-        }
-        synchronized (mListeners) {
-            return mUmsAvailable;
-        }
+        return false;
     }
 
+    @Override
+    @Deprecated
     public void setUsbMassStorageEnabled(boolean enable) {
-        waitForReady();
-        validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
-        validateUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER);
-
-        final StorageVolume primary = getPrimaryPhysicalVolume();
-        if (primary == null) return;
-
-        // TODO: Add support for multiple share methods
-
-        /*
-         * If the volume is mounted and we're enabling then unmount it
-         */
-        String path = primary.getPath();
-        String vs = getVolumeState(path);
-        String method = "ums";
-        if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
-            // Override for isUsbMassStorageEnabled()
-            setUmsEnabling(enable);
-            UmsEnableCallBack umscb = new UmsEnableCallBack(path, method, true);
-            mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, umscb));
-            // Clear override
-            setUmsEnabling(false);
-        }
-        /*
-         * If we disabled UMS then mount the volume
-         */
-        if (!enable) {
-            doShareUnshareVolume(path, method, enable);
-            if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
-                Slog.e(TAG, "Failed to remount " + path +
-                        " after disabling share method " + method);
-                /*
-                 * Even though the mount failed, the unshare didn't so don't indicate an error.
-                 * The mountVolume() call will have set the storage state and sent the necessary
-                 * broadcasts.
-                 */
-            }
-        }
+        throw new UnsupportedOperationException();
     }
 
+    @Override
+    @Deprecated
     public boolean isUsbMassStorageEnabled() {
-        waitForReady();
-
-        final StorageVolume primary = getPrimaryPhysicalVolume();
-        if (primary != null) {
-            return doGetVolumeShared(primary.getPath(), "ums");
-        } else {
-            return false;
-        }
+        return false;
     }
 
     /**
      * @return state of the volume at the specified mount point
      */
+    @Override
+    @Deprecated
     public String getVolumeState(String mountPoint) {
-        synchronized (mVolumesLock) {
-            String state = mVolumeStates.get(mountPoint);
-            if (state == null) {
-                Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
-                if (SystemProperties.get("vold.encrypt_progress").length() != 0) {
-                    state = Environment.MEDIA_REMOVED;
-                } else {
-                    throw new IllegalArgumentException();
-                }
-            }
+        // TODO: pretend that we're unmounted when encrypting?
+        // SystemProperties.get("vold.encrypt_progress")
 
-            return state;
-        }
+        final Volume vol = findVolumeByLegacyPath(mountPoint);
+        return sStateToEnvironment.get(vol.state);
     }
 
     @Override
     public boolean isExternalStorageEmulated() {
-        return mEmulatedTemplate != null;
+        return Environment.isExternalStorageEmulated();
     }
 
+    @Override
     public int mountVolume(String path) {
-        validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
         waitForReady();
-        return doMountVolume(path);
+
+        final Volume vol = findVolumeByLegacyPath(path);
+        if (vol != null) {
+            if (vol.type == Volume.TYPE_PUBLIC || vol.type == Volume.TYPE_PRIVATE) {
+                enforceUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
+            }
+            try {
+                vol.mount();
+                return 0;
+            } catch (NativeDaemonConnectorException ignored) {
+            }
+        } else {
+            Slog.w(TAG, "Unknown volume for path " + path);
+        }
+        return -1;
     }
 
+    @Override
     public void unmountVolume(String path, boolean force, boolean removeEncryption) {
-        validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
         waitForReady();
 
-        String volState = getVolumeState(path);
-        if (DEBUG_UNMOUNT) {
-            Slog.i(TAG, "Unmounting " + path
-                    + " force = " + force
-                    + " removeEncryption = " + removeEncryption);
+        final Volume vol = findVolumeByLegacyPath(path);
+        if (vol != null) {
+            // TODO: expand PMS to know about multiple volumes
+            if (vol.isPrimary()) {
+                synchronized (mUnmountLock) {
+                    mUnmountSignal = new CountDownLatch(1);
+                    mPms.updateExternalMediaStatus(false, true);
+                    waitForLatch(mUnmountSignal, "mUnmountSignal");
+                    mUnmountSignal = null;
+                }
+            }
+
+            try {
+                vol.unmount();
+            } catch (NativeDaemonConnectorException ignored) {
+            }
+        } else {
+            Slog.w(TAG, "Unknown volume for path " + path);
         }
-        if (Environment.MEDIA_UNMOUNTED.equals(volState) ||
-                Environment.MEDIA_REMOVED.equals(volState) ||
-                Environment.MEDIA_SHARED.equals(volState) ||
-                Environment.MEDIA_UNMOUNTABLE.equals(volState)) {
-            // Media already unmounted or cannot be unmounted.
-            // TODO return valid return code when adding observer call back.
-            return;
-        }
-        UnmountCallBack ucb = new UnmountCallBack(path, force, removeEncryption);
-        mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
     }
 
+    @Override
     public int formatVolume(String path) {
-        validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
+        enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
         waitForReady();
 
-        return doFormatVolume(path);
+        final Volume vol = findVolumeByLegacyPath(path);
+        if (vol != null) {
+            try {
+                vol.format();
+                return 0;
+            } catch (NativeDaemonConnectorException ignored) {
+            }
+        } else {
+            Slog.w(TAG, "Unknown volume for path " + path);
+        }
+        return -1;
     }
 
+    @Override
     public int[] getStorageUsers(String path) {
-        validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
+        enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
         waitForReady();
         try {
             final String[] r = NativeDaemonEvent.filterMessageList(
@@ -1813,22 +1314,20 @@
     }
 
     private void warnOnNotMounted() {
-        final StorageVolume primary = getPrimaryPhysicalVolume();
-        if (primary != null) {
-            boolean mounted = false;
-            try {
-                mounted = Environment.MEDIA_MOUNTED.equals(getVolumeState(primary.getPath()));
-            } catch (IllegalArgumentException e) {
-            }
-
-            if (!mounted) {
-                Slog.w(TAG, "getSecureContainerList() called when storage not mounted");
+        synchronized (mLock) {
+            for (Volume vol : mVolumes.values()) {
+                if (vol.isPrimary() && vol.state == Volume.STATE_MOUNTED) {
+                    // Cool beans, we have a mounted primary volume
+                    return;
+                }
             }
         }
+
+        Slog.w(TAG, "No primary storage mounted!");
     }
 
     public String[] getSecureContainerList() {
-        validatePermission(android.Manifest.permission.ASEC_ACCESS);
+        enforcePermission(android.Manifest.permission.ASEC_ACCESS);
         waitForReady();
         warnOnNotMounted();
 
@@ -1842,7 +1341,7 @@
 
     public int createSecureContainer(String id, int sizeMb, String fstype, String key,
             int ownerUid, boolean external) {
-        validatePermission(android.Manifest.permission.ASEC_CREATE);
+        enforcePermission(android.Manifest.permission.ASEC_CREATE);
         waitForReady();
         warnOnNotMounted();
 
@@ -1864,7 +1363,7 @@
 
     @Override
     public int resizeSecureContainer(String id, int sizeMb, String key) {
-        validatePermission(android.Manifest.permission.ASEC_CREATE);
+        enforcePermission(android.Manifest.permission.ASEC_CREATE);
         waitForReady();
         warnOnNotMounted();
 
@@ -1878,7 +1377,7 @@
     }
 
     public int finalizeSecureContainer(String id) {
-        validatePermission(android.Manifest.permission.ASEC_CREATE);
+        enforcePermission(android.Manifest.permission.ASEC_CREATE);
         warnOnNotMounted();
 
         int rc = StorageResultCode.OperationSucceeded;
@@ -1895,7 +1394,7 @@
     }
 
     public int fixPermissionsSecureContainer(String id, int gid, String filename) {
-        validatePermission(android.Manifest.permission.ASEC_CREATE);
+        enforcePermission(android.Manifest.permission.ASEC_CREATE);
         warnOnNotMounted();
 
         int rc = StorageResultCode.OperationSucceeded;
@@ -1912,7 +1411,7 @@
     }
 
     public int destroySecureContainer(String id, boolean force) {
-        validatePermission(android.Manifest.permission.ASEC_DESTROY);
+        enforcePermission(android.Manifest.permission.ASEC_DESTROY);
         waitForReady();
         warnOnNotMounted();
 
@@ -1952,7 +1451,7 @@
     }
 
     public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) {
-        validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
+        enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
         waitForReady();
         warnOnNotMounted();
 
@@ -1982,7 +1481,7 @@
     }
 
     public int unmountSecureContainer(String id, boolean force) {
-        validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
+        enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
         waitForReady();
         warnOnNotMounted();
 
@@ -2025,7 +1524,7 @@
     }
 
     public boolean isSecureContainerMounted(String id) {
-        validatePermission(android.Manifest.permission.ASEC_ACCESS);
+        enforcePermission(android.Manifest.permission.ASEC_ACCESS);
         waitForReady();
         warnOnNotMounted();
 
@@ -2035,7 +1534,7 @@
     }
 
     public int renameSecureContainer(String oldId, String newId) {
-        validatePermission(android.Manifest.permission.ASEC_RENAME);
+        enforcePermission(android.Manifest.permission.ASEC_RENAME);
         waitForReady();
         warnOnNotMounted();
 
@@ -2060,7 +1559,7 @@
     }
 
     public String getSecureContainerPath(String id) {
-        validatePermission(android.Manifest.permission.ASEC_ACCESS);
+        enforcePermission(android.Manifest.permission.ASEC_ACCESS);
         waitForReady();
         warnOnNotMounted();
 
@@ -2081,7 +1580,7 @@
     }
 
     public String getSecureContainerFilesystemPath(String id) {
-        validatePermission(android.Manifest.permission.ASEC_ACCESS);
+        enforcePermission(android.Manifest.permission.ASEC_ACCESS);
         waitForReady();
         warnOnNotMounted();
 
@@ -2101,8 +1600,13 @@
         }
     }
 
+    @Override
     public void finishMediaUpdate() {
-        mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
+        if (mUnmountSignal != null) {
+            mUnmountSignal.countDown();
+        } else {
+            Slog.w(TAG, "Odd, nobody asked to unmount?");
+        }
     }
 
     private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
@@ -2477,109 +1981,84 @@
                 Context.APP_OPS_SERVICE);
         appOps.checkPackage(Binder.getCallingUid(), callingPkg);
 
+        File appFile = null;
         try {
-            appPath = new File(appPath).getCanonicalPath();
+            appFile = new File(appPath).getCanonicalFile();
         } catch (IOException e) {
             Slog.e(TAG, "Failed to resolve " + appPath + ": " + e);
             return -1;
         }
 
-        if (!appPath.endsWith("/")) {
-            appPath = appPath + "/";
-        }
-
         // Try translating the app path into a vold path, but require that it
         // belong to the calling package.
-        String voldPath = maybeTranslatePathForVold(appPath,
-                userEnv.buildExternalStorageAppDataDirs(callingPkg),
-                userEnv.buildExternalStorageAppDataDirsForVold(callingPkg));
-        if (voldPath != null) {
+        if (FileUtils.contains(userEnv.buildExternalStorageAppDataDirs(callingPkg), appFile) ||
+                FileUtils.contains(userEnv.buildExternalStorageAppObbDirs(callingPkg), appFile) ||
+                FileUtils.contains(userEnv.buildExternalStorageAppMediaDirs(callingPkg), appFile)) {
+            appPath = appFile.getAbsolutePath();
+            if (!appPath.endsWith("/")) {
+                appPath = appPath + "/";
+            }
+
             try {
-                mConnector.execute("volume", "mkdirs", voldPath);
+                mConnector.execute("volume", "mkdirs", appPath);
                 return 0;
             } catch (NativeDaemonConnectorException e) {
                 return e.getCode();
             }
         }
 
-        voldPath = maybeTranslatePathForVold(appPath,
-                userEnv.buildExternalStorageAppObbDirs(callingPkg),
-                userEnv.buildExternalStorageAppObbDirsForVold(callingPkg));
-        if (voldPath != null) {
-            try {
-                mConnector.execute("volume", "mkdirs", voldPath);
-                return 0;
-            } catch (NativeDaemonConnectorException e) {
-                return e.getCode();
-            }
-        }
-
-        voldPath = maybeTranslatePathForVold(appPath,
-                userEnv.buildExternalStorageAppMediaDirs(callingPkg),
-                userEnv.buildExternalStorageAppMediaDirsForVold(callingPkg));
-        if (voldPath != null) {
-            try {
-                mConnector.execute("volume", "mkdirs", voldPath);
-                return 0;
-            } catch (NativeDaemonConnectorException e) {
-                return e.getCode();
-            }
-        }
-
-        throw new SecurityException("Invalid mkdirs path: " + appPath);
-    }
-
-    /**
-     * Translate the given path from an app-visible path to a vold-visible path,
-     * but only if it's under the given whitelisted paths.
-     *
-     * @param path a canonicalized app-visible path.
-     * @param appPaths list of app-visible paths that are allowed.
-     * @param voldPaths list of vold-visible paths directly corresponding to the
-     *            allowed app-visible paths argument.
-     * @return a vold-visible path representing the original path, or
-     *         {@code null} if the given path didn't have an app-to-vold
-     *         mapping.
-     */
-    @VisibleForTesting
-    public static String maybeTranslatePathForVold(
-            String path, File[] appPaths, File[] voldPaths) {
-        if (appPaths.length != voldPaths.length) {
-            throw new IllegalStateException("Paths must be 1:1 mapping");
-        }
-
-        for (int i = 0; i < appPaths.length; i++) {
-            final String appPath = appPaths[i].getAbsolutePath() + "/";
-            if (path.startsWith(appPath)) {
-                path = new File(voldPaths[i], path.substring(appPath.length()))
-                        .getAbsolutePath();
-                if (!path.endsWith("/")) {
-                    path = path + "/";
-                }
-                return path;
-            }
-        }
-        return null;
+        throw new SecurityException("Invalid mkdirs path: " + appFile);
     }
 
     @Override
-    public StorageVolume[] getVolumeList() {
-        final int callingUserId = UserHandle.getCallingUserId();
-        final boolean accessAll = (mContext.checkPermission(
-                android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE,
-                Binder.getCallingPid(), Binder.getCallingUid()) == PERMISSION_GRANTED);
+    public StorageVolume[] getVolumeList(int userId) {
+        if (UserHandle.getCallingUserId() != userId) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE, "getVolumeList");
+        }
 
-        synchronized (mVolumesLock) {
-            final ArrayList<StorageVolume> filtered = Lists.newArrayList();
-            for (StorageVolume volume : mVolumes) {
-                final UserHandle owner = volume.getOwner();
-                final boolean ownerMatch = owner == null || owner.getIdentifier() == callingUserId;
-                if (accessAll || ownerMatch) {
-                    filtered.add(volume);
+        final ArrayList<StorageVolume> res = Lists.newArrayList();
+        boolean foundPrimary = false;
+        synchronized (mLock) {
+            for (Volume vol : mVolumes.values()) {
+                if (vol.isVisibleToUser(userId)) {
+                    final StorageVolume userVol = vol.buildVolumeForUser(userId);
+                    if (vol.isPrimary()) {
+                        res.add(0, userVol);
+                        foundPrimary = true;
+                    } else {
+                        res.add(userVol);
+                    }
                 }
             }
-            return filtered.toArray(new StorageVolume[filtered.size()]);
         }
+
+        if (!foundPrimary) {
+            Slog.w(TAG, "No primary storage defined yet; hacking together a stub");
+
+            final boolean primaryPhysical = SystemProperties.getBoolean(
+                    PROP_PRIMARY_PHYSICAL, false);
+
+            final String id = "stub_primary";
+            final File path = Environment.getLegacyExternalStorageDirectory();
+            final int descriptionId = android.R.string.unknownName;
+            final boolean primary = true;
+            final boolean removable = primaryPhysical;
+            final boolean emulated = !primaryPhysical;
+            final long mtpReserveSize = 0L;
+            final boolean allowMassStorage = false;
+            final long maxFileSize = 0L;
+            final UserHandle owner = new UserHandle(userId);
+            final String uuid = null;
+            final String userLabel = null;
+            final String state = Environment.MEDIA_REMOVED;
+
+            res.add(0, new StorageVolume(id, MtpStorage.getStorageIdForIndex(0), path,
+                    descriptionId, primary, removable, emulated, mtpReserveSize,
+                    allowMassStorage, maxFileSize, owner, uuid, userLabel, state));
+        }
+
+        return res.toArray(new StorageVolume[res.size()]);
     }
 
     private void addObbStateLocked(ObbState obbState) throws RemoteException {
@@ -3073,21 +2552,13 @@
         if (path.startsWith(obbPath)) {
             path = path.substring(obbPath.length() + 1);
 
-            if (forVold) {
-                return new File(Environment.getEmulatedStorageObbSource(), path).getAbsolutePath();
-            } else {
-                final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
-                return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
-                        .getAbsolutePath();
-            }
+            final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
+            return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path)
+                    .getAbsolutePath();
         }
 
         // Handle normal external storage paths
-        if (forVold) {
-            return new File(Environment.getEmulatedStorageSource(userId), path).getAbsolutePath();
-        } else {
-            return new File(userEnv.getExternalDirsForApp()[0], path).getAbsolutePath();
-        }
+        return new File(userEnv.getExternalStorageDirectory(), path).getAbsolutePath();
     }
 
     @Override
@@ -3126,15 +2597,20 @@
             pw.decreaseIndent();
         }
 
-        synchronized (mVolumesLock) {
+        synchronized (mLock) {
             pw.println();
-            pw.println("mVolumes:");
+            pw.println("Disks:");
             pw.increaseIndent();
-            for (StorageVolume volume : mVolumes) {
-                pw.println(volume);
-                pw.increaseIndent();
-                pw.println("Current state: " + mVolumeStates.get(volume.getPath()));
-                pw.decreaseIndent();
+            for (Disk disk : mDisks.values()) {
+                disk.dump(pw);
+            }
+            pw.decreaseIndent();
+
+            pw.println();
+            pw.println("Volumes:");
+            pw.increaseIndent();
+            for (Volume vol : mVolumes.values()) {
+                vol.dump(pw);
             }
             pw.decreaseIndent();
         }
@@ -3153,6 +2629,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public void monitor() {
         if (mConnector != null) {
             mConnector.monitor();
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java
index d2dfc7b..78c7f38 100644
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ b/services/core/java/com/android/server/NativeDaemonConnector.java
@@ -48,8 +48,6 @@
  * {@code libsysutils} FrameworkListener protocol.
  */
 final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
-    private static final boolean LOGD = false;
-
     private final static boolean VDBG = false;
 
     private final String TAG;
@@ -58,6 +56,8 @@
     private OutputStream mOutputStream;
     private LocalLog mLocalLog;
 
+    private volatile boolean mDebug = false;
+
     private final ResponseQueue mResponseQueue;
 
     private final PowerManager.WakeLock mWakeLock;
@@ -99,6 +99,14 @@
         mLocalLog = new LocalLog(maxLogSize);
     }
 
+    /**
+     * Enable Set debugging mode, which causes messages to also be written to both
+     * {@link Slog} in addition to internal log.
+     */
+    public void setDebug(boolean debug) {
+        mDebug = debug;
+    }
+
     @Override
     public void run() {
         mCallbackHandler = new Handler(mLooper, this);
@@ -513,7 +521,7 @@
     }
 
     private void log(String logstring) {
-        if (LOGD) Slog.d(TAG, logstring);
+        if (mDebug) Slog.d(TAG, logstring);
         mLocalLog.log(logstring);
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b5e1de9..dbf7def 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3041,24 +3041,13 @@
             int uid = app.uid;
 
             int[] gids = null;
-            int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
+            int mountExternal = Zygote.MOUNT_EXTERNAL_DEFAULT;
             if (!app.isolated) {
                 int[] permGids = null;
                 try {
                     checkTime(startTime, "startProcess: getting gids from package manager");
                     permGids = AppGlobals.getPackageManager().getPackageGids(app.info.packageName,
                             app.userId);
-
-                    if (Environment.isExternalStorageEmulated()) {
-                        checkTime(startTime, "startProcess: checking external storage perm");
-                        if (mContext.getPackageManager().checkPermission(
-                                android.Manifest.permission.ACCESS_ALL_EXTERNAL_STORAGE,
-                                app.info.packageName) == PERMISSION_GRANTED) {
-                            mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL;
-                        } else {
-                            mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
-                        }
-                    }
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Unable to retrieve gids", e);
                 }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 777d8dc..0eb914b 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -3919,9 +3919,8 @@
         mStackSupervisor.removeChildActivityContainers(r);
 
         try {
-            if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG,
-                    (andResume ? "Relaunching to RESUMED " : "Relaunching to PAUSED ")
-                    + r);
+            if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG, "Moving to " +
+                    (andResume ? "RESUMED" : "PAUSED") + " Relaunching " + r);
             r.forceNewConfig = false;
             r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes,
                     !andResume, new Configuration(mService.mConfiguration),
diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java
index cbbf91b..75a79cb 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java
@@ -58,14 +58,16 @@
             // received without <Request ARC Initiation> or <Request ARC Termination>.
             case Constants.MESSAGE_FEATURE_ABORT:
                 int originalOpcode = cmd.getParams()[0] & 0xFF;
-                if (originalOpcode == Constants.MESSAGE_REQUEST_ARC_INITIATION
-                        || originalOpcode == Constants.MESSAGE_REQUEST_ARC_TERMINATION) {
+                if (originalOpcode == Constants.MESSAGE_REQUEST_ARC_TERMINATION) {
                     disableArcTransmission();
                     finish();
                     return true;
-                } else {
-                    return false;
+                } else if (originalOpcode == Constants.MESSAGE_REQUEST_ARC_INITIATION) {
+                    tv().setArcStatus(false);
+                    finish();
+                    return true;
                 }
+                return false;
         }
         return false;
     }
@@ -82,7 +84,7 @@
         if (mState != state || state != STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE) {
             return;
         }
-        HdmiLogger.debug("[T]RequestArcAction.");
+        HdmiLogger.debug("[T] RequestArcAction.");
         disableArcTransmission();
         finish();
     }
diff --git a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
index d9e1f24..f69f975 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcInitiationAction.java
@@ -35,6 +35,7 @@
 
     @Override
     boolean start() {
+        // Seq #38
         mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
         addTimer(mState, HdmiConfig.TIMEOUT_MS);
 
@@ -44,9 +45,8 @@
             @Override
             public void onSendCompleted(int error) {
                 if (error != Constants.SEND_RESULT_SUCCESS) {
-                    // If failed to send <Request ARC Initiation>, start "Disabled"
-                    // ARC transmission action.
-                    disableArcTransmission();
+                    // Turn off ARC status if <Request ARC Initiation> fails.
+                    tv().setArcStatus(false);
                     finish();
                 }
             }
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index bffa854..d200d35 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -52,6 +52,7 @@
 
     @Override
     boolean start() {
+        // Seq #37.
         if (mEnabled) {
             // Enable ARC status immediately after sending <Report Arc Initiated>.
             // If AVR responds with <Feature Abort>, disable ARC status again.
diff --git a/services/core/java/com/android/server/pm/IntentFilterVerificationKey.java b/services/core/java/com/android/server/pm/IntentFilterVerificationKey.java
new file mode 100644
index 0000000..399b03c
--- /dev/null
+++ b/services/core/java/com/android/server/pm/IntentFilterVerificationKey.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import java.util.Arrays;
+
+/**
+ * This is the key for the map of {@link android.content.pm.IntentFilterVerificationInfo}s
+ * maintained by the  {@link com.android.server.pm.PackageManagerService}
+ */
+class IntentFilterVerificationKey {
+    public String domains;
+    public String packageName;
+    public String className;
+
+    public IntentFilterVerificationKey(String[] domains, String packageName, String className) {
+        StringBuilder sb = new StringBuilder();
+        for (String host : domains) {
+            sb.append(host);
+        }
+        this.domains = sb.toString();
+        this.packageName = packageName;
+        this.className = className;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        IntentFilterVerificationKey that = (IntentFilterVerificationKey) o;
+
+        if (domains != null ? !domains.equals(that.domains) : that.domains != null) return false;
+        if (className != null ? !className.equals(that.className) : that.className != null)
+            return false;
+        if (packageName != null ? !packageName.equals(that.packageName) : that.packageName != null)
+            return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = domains != null ? domains.hashCode() : 0;
+        result = 31 * result + (packageName != null ? packageName.hashCode() : 0);
+        result = 31 * result + (className != null ? className.hashCode() : 0);
+        return result;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/IntentFilterVerificationResponse.java b/services/core/java/com/android/server/pm/IntentFilterVerificationResponse.java
new file mode 100644
index 0000000..ead399b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/IntentFilterVerificationResponse.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+
+import java.util.List;
+
+/* package private */ class IntentFilterVerificationResponse {
+    public final int callerUid;
+    public final int code;
+    public final List<String> failedDomains;
+
+    public IntentFilterVerificationResponse(int callerUid, int code, List<String> failedDomains) {
+        this.callerUid = callerUid;
+        this.code = code;
+        this.failedDomains = failedDomains;
+    }
+
+    public String getFailedDomainsString() {
+        StringBuilder sb = new StringBuilder();
+        for (String domain : failedDomains) {
+            if (sb.length() > 0) {
+                sb.append(" ");
+            }
+            sb.append(domain);
+        }
+        return sb.toString();
+    }
+}
diff --git a/services/core/java/com/android/server/pm/IntentFilterVerificationState.java b/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
new file mode 100644
index 0000000..c09d6ae
--- /dev/null
+++ b/services/core/java/com/android/server/pm/IntentFilterVerificationState.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+public class IntentFilterVerificationState {
+    static final String TAG = IntentFilterVerificationState.class.getName();
+
+    public final static int STATE_UNDEFINED = 0;
+    public final static int STATE_VERIFICATION_PENDING = 1;
+    public final static int STATE_VERIFICATION_SUCCESS = 2;
+    public final static int STATE_VERIFICATION_FAILURE = 3;
+
+    private int mRequiredVerifierUid = 0;
+
+    private int mState;
+
+    private ArrayList<PackageParser.ActivityIntentInfo> mFilters = new ArrayList<>();
+    private ArraySet<String> mHosts = new ArraySet<>();
+    private int mUserId;
+
+    private String mPackageName;
+    private boolean mVerificationComplete;
+
+    public IntentFilterVerificationState(int verifierUid, int userId, String packageName) {
+        mRequiredVerifierUid = verifierUid;
+        mUserId = userId;
+        mPackageName = packageName;
+        mState = STATE_UNDEFINED;
+        mVerificationComplete = false;
+    }
+
+    public void setState(int state) {
+        if (state > STATE_VERIFICATION_FAILURE || state < STATE_UNDEFINED) {
+            mState = STATE_UNDEFINED;
+        } else {
+            mState = state;
+        }
+    }
+
+    public int getState() {
+        return mState;
+    }
+
+    public void setPendingState() {
+        setState(STATE_VERIFICATION_PENDING);
+    }
+
+    public ArrayList<PackageParser.ActivityIntentInfo> getFilters() {
+        return mFilters;
+    }
+
+    public boolean isVerificationComplete() {
+        return mVerificationComplete;
+    }
+
+    public boolean isVerified() {
+        if (mVerificationComplete) {
+            return (mState == STATE_VERIFICATION_SUCCESS);
+        }
+        return false;
+    }
+
+    public int getUserId() {
+        return mUserId;
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public String getHostsString() {
+        StringBuilder sb = new StringBuilder();
+        final int count = mHosts.size();
+        for (int i=0; i<count; i++) {
+            if (i > 0) {
+                sb.append(" ");
+            }
+            sb.append(mHosts.valueAt(i));
+        }
+        return sb.toString();
+    }
+
+    public boolean setVerifierResponse(int callerUid, int code) {
+        if (mRequiredVerifierUid == callerUid) {
+            int state = STATE_UNDEFINED;
+            if (code == PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS) {
+                state = STATE_VERIFICATION_SUCCESS;
+            } else if (code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) {
+                state = STATE_VERIFICATION_FAILURE;
+            }
+            mVerificationComplete = true;
+            setState(state);
+            return true;
+        }
+        Log.d(TAG, "Cannot set verifier response with callerUid:" + callerUid + " and code:" +
+                code + " as required verifierUid is:" + mRequiredVerifierUid);
+        return false;
+    }
+
+    public void addFilter(PackageParser.ActivityIntentInfo filter) {
+        mFilters.add(filter);
+        mHosts.addAll(filter.getHostsList());
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c7dc74f..b9dfc21 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -44,6 +44,10 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
 import static android.content.pm.PackageManager.INSTALL_FORWARD_LOCK;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
 import static android.content.pm.PackageParser.isApkFile;
 import static android.os.Process.PACKAGE_INFO_GID;
 import static android.os.Process.SYSTEM_UID;
@@ -59,6 +63,8 @@
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
 import static com.android.server.pm.InstructionSets.getPreferredInstructionSet;
 
+import android.Manifest;
+import android.content.pm.IntentFilterVerificationInfo;
 import android.util.ArrayMap;
 
 import com.android.internal.R;
@@ -504,6 +510,231 @@
 
     boolean mResolverReplaced = false;
 
+    private final ComponentName mIntentFilterVerifierComponent;
+    private int mIntentFilterVerificationToken = 0;
+
+    final SparseArray<IntentFilterVerificationState> mIntentFilterVerificationStates
+            = new SparseArray<IntentFilterVerificationState>();
+
+    private interface IntentFilterVerifier<T extends IntentFilter> {
+        boolean addOneIntentFilterVerification(int verifierId, int userId, int verificationId,
+                                               T filter, String packageName);
+        void startVerifications(int userId);
+        void receiveVerificationResponse(int verificationId);
+    }
+
+    private class IntentVerifierProxy implements IntentFilterVerifier<ActivityIntentInfo> {
+        private Context mContext;
+        private ComponentName mIntentFilterVerifierComponent;
+        private ArrayList<Integer> mCurrentIntentFilterVerifications = new ArrayList<Integer>();
+
+        public IntentVerifierProxy(Context context, ComponentName verifierComponent) {
+            mContext = context;
+            mIntentFilterVerifierComponent = verifierComponent;
+        }
+
+        private String getDefaultScheme() {
+            // TODO: replace SCHEME_HTTP with SCHEME_HTTPS
+            return IntentFilter.SCHEME_HTTP;
+        }
+
+        @Override
+        public void startVerifications(int userId) {
+            // Launch verifications requests
+            int count = mCurrentIntentFilterVerifications.size();
+            for (int n=0; n<count; n++) {
+                int verificationId = mCurrentIntentFilterVerifications.get(n);
+                final IntentFilterVerificationState ivs =
+                        mIntentFilterVerificationStates.get(verificationId);
+
+                String packageName = ivs.getPackageName();
+                boolean modified = false;
+
+                ArrayList<PackageParser.ActivityIntentInfo> filters = ivs.getFilters();
+                final int filterCount = filters.size();
+                for (int m=0; m<filterCount; m++) {
+                    PackageParser.ActivityIntentInfo filter = filters.get(m);
+                    synchronized (mPackages) {
+                        modified = mSettings.createIntentFilterVerificationIfNeededLPw(
+                                packageName, filter.getHosts());
+                    }
+                }
+                synchronized (mPackages) {
+                    if (modified) {
+                        scheduleWriteSettingsLocked();
+                    }
+                }
+                sendVerificationRequest(userId, verificationId, ivs);
+            }
+            mCurrentIntentFilterVerifications.clear();
+        }
+
+        private void sendVerificationRequest(int userId, int verificationId,
+                                             IntentFilterVerificationState ivs) {
+
+            Intent verificationIntent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
+            verificationIntent.putExtra(
+                    PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID,
+                    verificationId);
+            verificationIntent.putExtra(
+                    PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME,
+                    getDefaultScheme());
+            verificationIntent.putExtra(
+                    PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS,
+                    ivs.getHostsString());
+            verificationIntent.putExtra(
+                    PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME,
+                    ivs.getPackageName());
+            verificationIntent.setComponent(mIntentFilterVerifierComponent);
+            verificationIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+
+            UserHandle user = new UserHandle(userId);
+            mContext.sendBroadcastAsUser(verificationIntent, user);
+            Slog.d(TAG, "Sending IntenFilter verification broadcast");
+        }
+
+        public void receiveVerificationResponse(int verificationId) {
+            IntentFilterVerificationState ivs = mIntentFilterVerificationStates.get(verificationId);
+
+            final boolean verified = ivs.isVerified();
+
+            ArrayList<PackageParser.ActivityIntentInfo> filters = ivs.getFilters();
+            final int count = filters.size();
+            for (int n=0; n<count; n++) {
+                PackageParser.ActivityIntentInfo filter = filters.get(n);
+                filter.setVerified(verified);
+
+                Slog.d(TAG, "IntentFilter " + filter.toString() + " verified with result:"
+                        + verified + " and hosts:" + ivs.getHostsString());
+            }
+
+            mIntentFilterVerificationStates.remove(verificationId);
+
+            final String packageName = ivs.getPackageName();
+            IntentFilterVerificationInfo ivi = null;
+
+            synchronized (mPackages) {
+                ivi = mSettings.getIntentFilterVerificationLPr(packageName);
+            }
+            if (ivi == null) {
+                Slog.w(TAG, "IntentFilterVerificationInfo not found for verificationId:"
+                        + verificationId + " packageName:" + packageName);
+                return;
+            }
+            Slog.d(TAG, "Updating IntentFilterVerificationInfo for verificationId: "
+                    + verificationId);
+
+            synchronized (mPackages) {
+                if (verified) {
+                    ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS);
+                } else {
+                    ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK);
+                }
+                scheduleWriteSettingsLocked();
+
+                final int userId = ivs.getUserId();
+                if (userId != UserHandle.USER_ALL) {
+                    final int userStatus =
+                            mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
+
+                    int updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+                    boolean needUpdate = false;
+
+                    // We cannot override the STATUS_ALWAYS / STATUS_NEVER states if they have
+                    // already been set by the User thru the Disambiguation dialog
+                    switch (userStatus) {
+                        case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
+                            if (verified) {
+                                updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+                            } else {
+                                updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+                            }
+                            needUpdate = true;
+                            break;
+
+                        case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
+                            if (verified) {
+                                updatedStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+                                needUpdate = true;
+                            }
+                            break;
+
+                        default:
+                            // Nothing to do
+                    }
+
+                    if (needUpdate) {
+                        mSettings.updateIntentFilterVerificationStatusLPw(
+                                packageName, updatedStatus, userId);
+                        scheduleWritePackageRestrictionsLocked(userId);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public boolean addOneIntentFilterVerification(int verifierId, int userId, int verificationId,
+                    ActivityIntentInfo filter, String packageName) {
+            if (!(filter.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
+                    filter.hasDataScheme(IntentFilter.SCHEME_HTTPS))) {
+                Slog.d(TAG, "IntentFilter does not contain HTTP nor HTTPS data scheme");
+                return false;
+            }
+            IntentFilterVerificationState ivs = mIntentFilterVerificationStates.get(verificationId);
+            if (ivs == null) {
+                ivs = createDomainVerificationState(verifierId, userId, verificationId,
+                        packageName);
+            }
+            ArrayList<String> hosts = filter.getHostsList();
+            if (!hasValidHosts(hosts)) {
+                return false;
+            }
+            ivs.addFilter(filter);
+            return true;
+        }
+
+        private IntentFilterVerificationState createDomainVerificationState(int verifierId,
+                int userId, int verificationId, String packageName) {
+            IntentFilterVerificationState ivs = new IntentFilterVerificationState(
+                    verifierId, userId, packageName);
+            ivs.setPendingState();
+            synchronized (mPackages) {
+                mIntentFilterVerificationStates.append(verificationId, ivs);
+                mCurrentIntentFilterVerifications.add(verificationId);
+            }
+            return ivs;
+        }
+
+        private boolean hasValidHosts(ArrayList<String> hosts) {
+            if (hosts.size() == 0) {
+                Slog.d(TAG, "IntentFilter does not contain any data hosts");
+                return false;
+            }
+            String hostEndBase = null;
+            for (String host : hosts) {
+                String[] hostParts = host.split("\\.");
+                // Should be at minimum a host like "example.com"
+                if (hostParts.length < 2) {
+                    Slog.d(TAG, "IntentFilter does not contain a valid data host name: " + host);
+                    return false;
+                }
+                // Verify that we have the same ending domain
+                int length = hostParts.length;
+                String hostEnd = hostParts[length - 1] + hostParts[length - 2];
+                if (hostEndBase == null) {
+                    hostEndBase = hostEnd;
+                }
+                if (!hostEnd.equalsIgnoreCase(hostEndBase)) {
+                    Slog.d(TAG, "IntentFilter does not contain the same data domains");
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    private IntentFilterVerifier mIntentFilterVerifier;
+
     // Set of pending broadcasts for aggregating enable/disable of components.
     static class PendingPackageBroadcasts {
         // for each user id, a map of <package name -> components within that package>
@@ -590,6 +821,8 @@
     static final int WRITE_PACKAGE_RESTRICTIONS = 14;
     static final int PACKAGE_VERIFIED = 15;
     static final int CHECK_PENDING_VERIFICATION = 16;
+    static final int START_INTENT_FILTER_VERIFICATIONS = 17;
+    static final int INTENT_FILTER_VERIFIED = 18;
 
     static final int WRITE_SETTINGS_DELAY = 10*1000;  // 10 seconds
 
@@ -1240,6 +1473,54 @@
 
                     break;
                 }
+                case START_INTENT_FILTER_VERIFICATIONS: {
+                    int userId = msg.arg1;
+                    int verifierUid = msg.arg2;
+                    PackageParser.Package pkg = (PackageParser.Package)msg.obj;
+
+                    verifyIntentFiltersIfNeeded(userId, verifierUid, pkg);
+                    break;
+                }
+                case INTENT_FILTER_VERIFIED: {
+                    final int verificationId = msg.arg1;
+
+                    final IntentFilterVerificationState state = mIntentFilterVerificationStates.get(
+                            verificationId);
+                    if (state == null) {
+                        Slog.w(TAG, "Invalid IntentFilter verification token "
+                                + verificationId + " received");
+                        break;
+                    }
+
+                    final int userId = state.getUserId();
+
+                    Slog.d(TAG, "Processing IntentFilter verification with token:"
+                            + verificationId + " and userId:" + userId);
+
+                    final IntentFilterVerificationResponse response =
+                            (IntentFilterVerificationResponse) msg.obj;
+
+                    state.setVerifierResponse(response.callerUid, response.code);
+
+                    Slog.d(TAG, "IntentFilter verification with token:" + verificationId
+                            + " and userId:" + userId
+                            + " is settings verifier response with response code:"
+                            + response.code);
+
+                    if (response.code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) {
+                        Slog.d(TAG, "Domains failing verification: "
+                                + response.getFailedDomainsString());
+                    }
+
+                    if (state.isVerificationComplete()) {
+                        mIntentFilterVerifier.receiveVerificationResponse(verificationId);
+                    } else {
+                        Slog.d(TAG, "IntentFilter verification with token:" + verificationId
+                                + " was not said to be complete");
+                    }
+
+                    break;
+                }
             }
         }
     }
@@ -1851,11 +2132,16 @@
                     SystemClock.uptimeMillis());
 
             mRequiredVerifierPackage = getRequiredVerifierLPr();
+
+            mInstallerService = new PackageInstallerService(context, this, mAppInstallDir);
+
+            mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
+            mIntentFilterVerifier = new IntentVerifierProxy(mContext,
+                    mIntentFilterVerifierComponent);
+
         } // synchronized (mPackages)
         } // synchronized (mInstallLock)
 
-        mInstallerService = new PackageInstallerService(context, this, mAppInstallDir);
-
         // Now after opening every single application zip, make sure they
         // are all flushed.  Not really needed, but keeps things nice and
         // tidy.
@@ -1909,6 +2195,46 @@
         return requiredVerifier;
     }
 
+    private ComponentName getIntentFilterVerifierComponentNameLPr() {
+        final Intent verification = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);
+        final List<ResolveInfo> receivers = queryIntentReceivers(verification, PACKAGE_MIME_TYPE,
+                PackageManager.GET_DISABLED_COMPONENTS, 0 /* userId */);
+
+        ComponentName verifierComponentName = null;
+
+        int priority = -1000;
+        final int N = receivers.size();
+        for (int i = 0; i < N; i++) {
+            final ResolveInfo info = receivers.get(i);
+
+            if (info.activityInfo == null) {
+                continue;
+            }
+
+            final String packageName = info.activityInfo.packageName;
+
+            final PackageSetting ps = mSettings.mPackages.get(packageName);
+            if (ps == null) {
+                continue;
+            }
+
+            if (checkPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
+                    packageName, UserHandle.USER_OWNER) != PackageManager.PERMISSION_GRANTED) {
+                continue;
+            }
+
+            // Select the IntentFilterVerifier with the highest priority
+            if (priority < info.priority) {
+                priority = info.priority;
+                verifierComponentName = new ComponentName(packageName, info.activityInfo.name);
+                Slog.d(TAG, "Selecting IntentFilterVerifier: " + verifierComponentName +
+                        " with priority: " + info.priority);
+            }
+        }
+
+        return verifierComponentName;
+    }
+
     @Override
     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
             throws RemoteException {
@@ -3532,14 +3858,20 @@
                 resolveInfo = queryCrossProfileIntents(
                         matchingFilters, intent, resolvedType, flags, userId);
 
-                // Check for results in the current profile.
+                // Check for results in the current profile. Adding GET_RESOLVED_FILTER flags
+                // as we need it later
                 List<ResolveInfo> result = mActivities.queryIntent(
                         intent, resolvedType, flags, userId);
                 if (resolveInfo != null) {
                     result.add(resolveInfo);
                     Collections.sort(result, mResolvePrioritySorter);
                 }
-                return filterIfNotPrimaryUser(result, userId);
+                result = filterIfNotPrimaryUser(result, userId);
+                if (result.size() > 1) {
+                    return filterCandidatesWithDomainPreferedActivitiesLPw(result);
+                }
+
+                return result;
             }
             final PackageParser.Package pkg = mPackages.get(pkgName);
             if (pkg != null) {
@@ -3570,6 +3902,49 @@
         return resolveInfos;
     }
 
+    private List<ResolveInfo> filterCandidatesWithDomainPreferedActivitiesLPw(
+            List<ResolveInfo> candidates) {
+        if (DEBUG_PREFERRED) {
+            Slog.v("TAG", "Filtering results with prefered activities. Candidates count: " +
+                    candidates.size());
+        }
+        final int userId = UserHandle.getCallingUserId();
+        ArrayList<ResolveInfo> result = new ArrayList<ResolveInfo>(candidates);
+        synchronized (mPackages) {
+            final int count = result.size();
+            for (int n = count-1; n >= 0; n--) {
+                ResolveInfo info = result.get(n);
+                if (!info.filterNeedsVerification) {
+                    continue;
+                }
+                String packageName = info.activityInfo.packageName;
+                PackageSetting ps = mSettings.mPackages.get(packageName);
+                if (ps != null) {
+                    // Try to get the status from User settings first
+                    int status = ps.getDomainVerificationStatusForUser(userId);
+                    // if none available, get the master status
+                    if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+                        if (ps.getIntentFilterVerificationInfo() != null) {
+                            status = ps.getIntentFilterVerificationInfo().getStatus();
+                        }
+                    }
+                    if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
+                        result.clear();
+                        result.add(info);
+                        // We break the for loop as we are good to go
+                        break;
+                    } else if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
+                        result.remove(n);
+                    }
+                }
+            }
+        }
+        if (DEBUG_PREFERRED) {
+            Slog.v("TAG", "Filtered results with prefered activities. New candidates count: " +
+                    result.size());
+        }
+        return result;
+    }
 
     private ResolveInfo querySkipCurrentProfileIntents(
             List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
@@ -7444,6 +7819,9 @@
             if ((mFlags&PackageManager.GET_RESOLVED_FILTER) != 0) {
                 res.filter = info;
             }
+            if (info != null) {
+                res.filterNeedsVerification = info.needsVerification();
+            }
             res.priority = info.getPriority();
             res.preferredOrder = activity.owner.mPreferredOrder;
             //System.out.println("Result: " + res.activityInfo.className +
@@ -7666,8 +8044,6 @@
             }
             res.priority = info.getPriority();
             res.preferredOrder = service.owner.mPreferredOrder;
-            //System.out.println("Result: " + res.activityInfo.className +
-            //                   " = " + res.priority);
             res.match = match;
             res.isDefault = info.hasDefault;
             res.labelRes = info.labelRes;
@@ -8544,6 +8920,45 @@
                 android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) == 1;
     }
 
+    @Override
+    public void verifyIntentFilter(int id, int verificationCode, List<String> outFailedDomains)
+            throws RemoteException {
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT,
+                "Only intentfilter verification agents can verify applications");
+
+        final Message msg = mHandler.obtainMessage(INTENT_FILTER_VERIFIED);
+        final IntentFilterVerificationResponse response = new IntentFilterVerificationResponse(
+                Binder.getCallingUid(), verificationCode, outFailedDomains);
+        msg.arg1 = id;
+        msg.obj = response;
+        mHandler.sendMessage(msg);
+    }
+
+    @Override
+    public int getIntentVerificationStatus(String packageName, int userId) {
+        synchronized (mPackages) {
+            return mSettings.getIntentFilterVerificationStatusLPr(packageName, userId);
+        }
+    }
+
+    @Override
+    public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
+        boolean result = false;
+        synchronized (mPackages) {
+            result = mSettings.updateIntentFilterVerificationStatusLPw(packageName, status, userId);
+        }
+        scheduleWritePackageRestrictionsLocked(userId);
+        return result;
+    }
+
+    @Override
+    public List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) {
+        synchronized (mPackages) {
+            return mSettings.getIntentFilterVerificationsLPr(packageName);
+        }
+    }
+
     /**
      * Get the "allow unknown sources" setting.
      *
@@ -10708,6 +11123,8 @@
             return;
         }
 
+        startIntentFilterVerifications(args.user.getIdentifier(), pkg);
+
         if (replace) {
             replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                     installerPackageName, res);
@@ -10723,6 +11140,86 @@
         }
     }
 
+    private void startIntentFilterVerifications(int userId, PackageParser.Package pkg) {
+        if (mIntentFilterVerifierComponent == null) {
+            Slog.d(TAG, "No IntentFilter verification will not be done as "
+                    + "there is no IntentFilterVerifier available!");
+            return;
+        }
+
+        final int verifierUid = getPackageUid(
+                mIntentFilterVerifierComponent.getPackageName(),
+                (userId == UserHandle.USER_ALL) ? UserHandle.USER_OWNER : userId);
+
+        mHandler.removeMessages(START_INTENT_FILTER_VERIFICATIONS);
+        final Message msg = mHandler.obtainMessage(START_INTENT_FILTER_VERIFICATIONS);
+        msg.obj = pkg;
+        msg.arg1 = userId;
+        msg.arg2 = verifierUid;
+
+        mHandler.sendMessage(msg);
+    }
+
+    private void verifyIntentFiltersIfNeeded(int userId, int verifierUid,
+                                             PackageParser.Package pkg) {
+        int size = pkg.activities.size();
+        if (size == 0) {
+            Slog.d(TAG, "No activity, so no need to verify any IntentFilter!");
+            return;
+        }
+
+        Slog.d(TAG, "Checking for userId:" + userId + " if any IntentFilter from the " + size
+                + " Activities needs verification ...");
+
+        final int verificationId = mIntentFilterVerificationToken++;
+        int count = 0;
+        synchronized (mPackages) {
+            for (PackageParser.Activity a : pkg.activities) {
+                for (ActivityIntentInfo filter : a.intents) {
+                    boolean needFilterVerification = filter.needsVerification() &&
+                            !filter.isVerified();
+                    if (needFilterVerification && needNetworkVerificationLPr(filter)) {
+                        Slog.d(TAG, "Verification needed for IntentFilter:" + filter.toString());
+                        mIntentFilterVerifier.addOneIntentFilterVerification(
+                                verifierUid, userId, verificationId, filter, pkg.packageName);
+                        count++;
+                    } else {
+                        Slog.d(TAG, "No verification needed for IntentFilter:" + filter.toString());
+                    }
+                }
+            }
+        }
+
+        if (count > 0) {
+            mIntentFilterVerifier.startVerifications(userId);
+            Slog.d(TAG, "Started " + count + " IntentFilter verification"
+                    + (count > 1 ? "s" : "") +  " for userId:" + userId + "!");
+        } else {
+            Slog.d(TAG, "No need to start any IntentFilter verification!");
+        }
+    }
+
+    private boolean needNetworkVerificationLPr(ActivityIntentInfo filter) {
+        final ComponentName cn  = filter.activity.getComponentName();
+        final String packageName = cn.getPackageName();
+
+        IntentFilterVerificationInfo ivi = mSettings.getIntentFilterVerificationLPr(
+                packageName);
+        if (ivi == null) {
+            return true;
+        }
+        int status = ivi.getStatus();
+        switch (status) {
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
+            case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
+                return true;
+
+            default:
+                // Nothing to do
+                return false;
+        }
+    }
+
     private static boolean isMultiArch(PackageSetting ps) {
         return (ps.pkgFlags & ApplicationInfo.FLAG_MULTIARCH) != 0;
     }
@@ -11066,6 +11563,7 @@
                         }
                     }
                     clearPackagePreferredActivitiesLPw(deletedPs.name, UserHandle.USER_ALL);
+                    clearIntentFilterVerificationsLPw(deletedPs.name, UserHandle.USER_ALL);
                 }
                 // make sure to preserve per-user disabled state if this removal was just
                 // a downgrade of a system app to the factory package
@@ -11294,8 +11792,8 @@
                         true,  //notLaunched
                         false, //hidden
                         null, null, null,
-                        false // blockUninstall
-                        );
+                        false, // blockUninstall
+                        INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
                 if (!isSystemApp(ps)) {
                     if (ps.isAnyInstalled(sUserManager.getUserIds())) {
                         // Other user still have this package installed, so all
@@ -11918,6 +12416,19 @@
         return changed;
     }
 
+    /** This method takes a specific user id as well as UserHandle.USER_ALL. */
+    void clearIntentFilterVerificationsLPw(String packageName, int userId) {
+        if (userId == UserHandle.USER_ALL) {
+            mSettings.removeIntentFilterVerificationLPw(packageName, sUserManager.getUserIds());
+            for (int oneUserId : sUserManager.getUserIds()) {
+                scheduleWritePackageRestrictionsLocked(oneUserId);
+            }
+        } else {
+            mSettings.removeIntentFilterVerificationLPw(packageName, userId);
+            scheduleWritePackageRestrictionsLocked(userId);
+        }
+    }
+
     @Override
     public void resetPreferredActivities(int userId) {
         /* TODO: Actually use userId. Why is it being passed in? */
@@ -12428,6 +12939,8 @@
         public static final int DUMP_KEYSETS = 1 << 11;
         public static final int DUMP_VERSION = 1 << 12;
         public static final int DUMP_INSTALLS = 1 << 13;
+        public static final int DUMP_INTENT_FILTER_VERIFIERS = 1 << 14;
+        public static final int DUMP_DOMAIN_PREFERRED = 1 << 15;
 
         public static final int OPTION_SHOW_FILTERS = 1 << 0;
 
@@ -12533,6 +13046,8 @@
                 pw.println("    write: write current settings now");
                 pw.println("    <package.name>: info about given package");
                 pw.println("    installs: details about install sessions");
+                pw.println("    d[omain-preferred-apps]: print domains preferred apps");
+                pw.println("    i[ntent-filter-verifiers]|ifv: print intent filter verifier info");
                 return;
             } else if ("--checkin".equals(opt)) {
                 checkin = true;
@@ -12569,6 +13084,8 @@
                     fullPreferred = true;
                     opti++;
                 }
+            } else if ("d".equals(cmd) || "domain-preferred-apps".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_DOMAIN_PREFERRED);
             } else if ("p".equals(cmd) || "packages".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_PACKAGES);
             } else if ("s".equals(cmd) || "shared-users".equals(cmd)) {
@@ -12579,6 +13096,9 @@
                 dumpState.setDump(DumpState.DUMP_MESSAGES);
             } else if ("v".equals(cmd) || "verifiers".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_VERIFIERS);
+            } else if ("i".equals(cmd) || "ifv".equals(cmd)
+                    || "intent-filter-verifiers".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_INTENT_FILTER_VERIFIERS);
             } else if ("version".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_VERSION);
             } else if ("k".equals(cmd) || "keysets".equals(cmd)) {
@@ -12634,6 +13154,29 @@
                 }
             }
 
+            if (dumpState.isDumping(DumpState.DUMP_INTENT_FILTER_VERIFIERS) &&
+                    packageName == null) {
+                if (mIntentFilterVerifierComponent != null) {
+                    String verifierPackageName = mIntentFilterVerifierComponent.getPackageName();
+                    if (!checkin) {
+                        if (dumpState.onTitlePrinted())
+                            pw.println();
+                        pw.println("Intent Filter Verifier:");
+                        pw.print("  Using: ");
+                        pw.print(verifierPackageName);
+                        pw.print(" (uid=");
+                        pw.print(getPackageUid(verifierPackageName, 0));
+                        pw.println(")");
+                    } else if (verifierPackageName != null) {
+                        pw.print("ifv,"); pw.print(verifierPackageName);
+                        pw.print(","); pw.println(getPackageUid(verifierPackageName, 0));
+                    }
+                } else {
+                    pw.println();
+                    pw.println("No Intent Filter Verifier available!");
+                }
+            }
+
             if (dumpState.isDumping(DumpState.DUMP_LIBS) && packageName == null) {
                 boolean printedHeader = false;
                 final Iterator<String> it = mSharedLibraries.keySet().iterator();
@@ -12753,6 +13296,65 @@
                 }
             }
 
+            if (!checkin && dumpState.isDumping(DumpState.DUMP_DOMAIN_PREFERRED)) {
+                pw.println();
+                int count = mSettings.mPackages.size();
+                if (count == 0) {
+                    pw.println("No domain preferred apps!");
+                    pw.println();
+                } else {
+                    final String prefix = "  ";
+                    Collection<PackageSetting> allPackageSettings = mSettings.mPackages.values();
+                    if (allPackageSettings.size() == 0) {
+                        pw.println("No domain preferred apps!");
+                        pw.println();
+                    } else {
+                        pw.println("Domain preferred apps status:");
+                        pw.println();
+                        count = 0;
+                        for (PackageSetting ps : allPackageSettings) {
+                            IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
+                            if (ivi == null || ivi.getPackageName() == null) continue;
+                            pw.println(prefix + "Package Name: " + ivi.getPackageName());
+                            pw.println(prefix + "Domains: " + ivi.getDomainsString());
+                            pw.println(prefix + "Status: " + ivi.getStatusString());
+                            pw.println();
+                            count++;
+                        }
+                        if (count == 0) {
+                            pw.println(prefix + "No domain preferred app status!");
+                            pw.println();
+                        }
+                        for (int userId : sUserManager.getUserIds()) {
+                            pw.println("Domain preferred apps for User " + userId + ":");
+                            pw.println();
+                            count = 0;
+                            for (PackageSetting ps : allPackageSettings) {
+                                IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
+                                if (ivi == null || ivi.getPackageName() == null) {
+                                    continue;
+                                }
+                                final int status = ps.getDomainVerificationStatusForUser(userId);
+                                if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+                                    continue;
+                                }
+                                pw.println(prefix + "Package Name: " + ivi.getPackageName());
+                                pw.println(prefix + "Domains: " + ivi.getDomainsString());
+                                String statusStr = IntentFilterVerificationInfo.
+                                        getStatusStringFromValue(status);
+                                pw.println(prefix + "Status: " + statusStr);
+                                pw.println();
+                                count++;
+                            }
+                            if (count == 0) {
+                                pw.println(prefix + "No domain preferred apps!");
+                                pw.println();
+                            }
+                        }
+                    }
+                }
+            }
+
             if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
                 mSettings.dumpPermissionsLPr(pw, packageName, dumpState);
                 if (packageName == null) {
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 35df33b..20120de 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -20,6 +20,8 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 
+import android.content.pm.IntentFilterVerificationInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageUserState;
 import android.util.ArraySet;
 import android.util.SparseArray;
@@ -108,6 +110,9 @@
 
     /* package name of the app that installed this package */
     String installerPackageName;
+
+    IntentFilterVerificationInfo verificationInfo;
+
     PackageSettingBase(String name, String realName, File codePath, File resourcePath,
             String legacyNativeLibraryPathString, String primaryCpuAbiString,
             String secondaryCpuAbiString, String cpuAbiOverrideString,
@@ -214,6 +219,7 @@
         }
         installStatus = base.installStatus;
         keySetData = base.keySetData;
+        verificationInfo = base.verificationInfo;
     }
 
     private PackageUserState modifyUserState(int userId) {
@@ -317,7 +323,7 @@
     void setUserState(int userId, int enabled, boolean installed, boolean stopped,
             boolean notLaunched, boolean hidden,
             String lastDisableAppCaller, ArraySet<String> enabledComponents,
-            ArraySet<String> disabledComponents, boolean blockUninstall) {
+            ArraySet<String> disabledComponents, boolean blockUninstall, int domainVerifState) {
         PackageUserState state = modifyUserState(userId);
         state.enabled = enabled;
         state.installed = installed;
@@ -328,6 +334,7 @@
         state.enabledComponents = enabledComponents;
         state.disabledComponents = disabledComponents;
         state.blockUninstall = blockUninstall;
+        state.domainVerificationStatus = domainVerifState;
     }
 
     ArraySet<String> getEnabledComponents(int userId) {
@@ -415,4 +422,25 @@
     void removeUser(int userId) {
         userState.delete(userId);
     }
+
+    public IntentFilterVerificationInfo getIntentFilterVerificationInfo() {
+        return verificationInfo;
+    }
+
+    public void setIntentFilterVerificationInfo(IntentFilterVerificationInfo info) {
+        verificationInfo = info;
+    }
+
+    public int getDomainVerificationStatusForUser(int userId) {
+        return readUserState(userId).domainVerificationStatus;
+    }
+
+    public void setDomainVerificationStatusForUser(int status, int userId) {
+        modifyUserState(userId).domainVerificationStatus = status;
+    }
+
+    public void clearDomainVerificationStatusForUser(int userId) {
+        modifyUserState(userId).domainVerificationStatus =
+                PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 8f185ec..dd58813 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -22,11 +22,13 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.PACKAGE_INFO_GID;
 
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
+import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Binder;
@@ -41,6 +43,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.AtomicFile;
+import android.text.TextUtils;
 import android.util.LogPrinter;
 
 import android.util.SparseBooleanArray;
@@ -88,6 +91,7 @@
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
@@ -160,6 +164,7 @@
             "persistent-preferred-activities";
     static final String TAG_CROSS_PROFILE_INTENT_FILTERS =
             "crossProfile-intent-filters";
+    public static final String TAG_DOMAIN_VERIFICATION = "domain-verification";
 
     private static final String ATTR_NAME = "name";
     private static final String ATTR_USER = "user";
@@ -174,6 +179,7 @@
     private static final String ATTR_HIDDEN = "hidden";
     private static final String ATTR_INSTALLED = "inst";
     private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
+    private static final String ATTR_DOMAIN_VERIFICATON_STATE = "domainVerificationStatus";
 
     private final Object mLock;
 
@@ -590,8 +596,8 @@
                                     true, // notLaunched
                                     false, // hidden
                                     null, null, null,
-                                    false // blockUninstall
-                                    );
+                                    false, // blockUninstall
+                                    INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
                             writePackageRestrictionsLPr(user.id);
                         }
                     }
@@ -865,7 +871,7 @@
             if (mOtherUserIds.get(uid) != null) {
                 PackageManagerService.reportSettingsProblem(Log.ERROR,
                         "Adding duplicate shared id: " + uid
-                        + " name=" + name);
+                                + " name=" + name);
                 return false;
             }
             mOtherUserIds.put(uid, obj);
@@ -931,6 +937,96 @@
         return cpir;
     }
 
+    /**
+     * The following functions suppose that you have a lock for managing access to the
+     * mIntentFiltersVerifications map.
+     */
+
+    /* package protected */
+    IntentFilterVerificationInfo getIntentFilterVerificationLPr(String packageName) {
+        PackageSetting ps = mPackages.get(packageName);
+        if (ps == null) {
+            Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+            return null;
+        }
+        return ps.getIntentFilterVerificationInfo();
+    }
+
+    /* package protected */
+    boolean createIntentFilterVerificationIfNeededLPw(String packageName, String[] domains) {
+        PackageSetting ps = mPackages.get(packageName);
+        if (ps == null) {
+            Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+            return false;
+        }
+        if (ps.getIntentFilterVerificationInfo() == null) {
+            IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(packageName, domains);
+            ps.setIntentFilterVerificationInfo(ivi);
+            return false;
+        }
+        return true;
+    }
+
+    int getIntentFilterVerificationStatusLPr(String packageName, int userId) {
+        PackageSetting ps = mPackages.get(packageName);
+        if (ps == null) {
+            Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+            return INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+        }
+        int status = ps.getDomainVerificationStatusForUser(userId);
+        if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+            if (ps.getIntentFilterVerificationInfo() != null) {
+                status = ps.getIntentFilterVerificationInfo().getStatus();
+            }
+        }
+        return status;
+    }
+
+    boolean updateIntentFilterVerificationStatusLPw(String packageName, int status, int userId) {
+        PackageSetting ps = mPackages.get(packageName);
+        if (ps == null) {
+            Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+            return false;
+        }
+        ps.setDomainVerificationStatusForUser(status, userId);
+        return true;
+    }
+
+    /**
+     * Used for dump. Should be read only.
+     */
+    List<IntentFilterVerificationInfo> getIntentFilterVerificationsLPr(
+            String packageName) {
+        if (packageName == null) {
+            return Collections.<IntentFilterVerificationInfo>emptyList();
+        }
+        ArrayList<IntentFilterVerificationInfo> result = new ArrayList<>();
+        for (PackageSetting ps : mPackages.values()) {
+            IntentFilterVerificationInfo ivi = ps.getIntentFilterVerificationInfo();
+            if (ivi == null || TextUtils.isEmpty(ivi.getPackageName()) ||
+                    !ivi.getPackageName().equalsIgnoreCase(packageName)) {
+                continue;
+            }
+            result.add(ivi);
+        }
+        return result;
+    }
+
+    void removeIntentFilterVerificationLPw(String packageName, int userId) {
+        PackageSetting ps = mPackages.get(packageName);
+        if (ps == null) {
+            Slog.w(PackageManagerService.TAG, "No package known for name: " + packageName);
+            return;
+        }
+        ps.clearDomainVerificationStatusForUser(userId);
+    }
+
+    void removeIntentFilterVerificationLPw(String packageName, int[] userIds) {
+        for (int userId : userIds) {
+            removeIntentFilterVerificationLPw(packageName, userId);
+        }
+    }
+
     private File getUserPackagesStateFile(int userId) {
         // TODO: Implement a cleaner solution when adding tests.
         // This instead of Environment.getUserSystemDirectory(userId) to support testing.
@@ -1083,6 +1179,25 @@
         }
     }
 
+    private void readDomainVerificationLPw(XmlPullParser parser, PackageSettingBase packageSetting)
+            throws XmlPullParserException, IOException {
+        IntentFilterVerificationInfo ivi = new IntentFilterVerificationInfo(parser);
+        packageSetting.setIntentFilterVerificationInfo(ivi);
+        Log.d(TAG, "Read domain verification for package:" + ivi.getPackageName());
+    }
+
+    void writeDomainVerificationsLPr(XmlSerializer serializer, String packageName,
+                                     IntentFilterVerificationInfo verificationInfo)
+            throws IllegalArgumentException, IllegalStateException, IOException {
+        if (verificationInfo != null && verificationInfo.getPackageName() != null) {
+            serializer.startTag(null, TAG_DOMAIN_VERIFICATION);
+            verificationInfo.writeToXml(serializer);
+            Log.d(TAG, "Wrote domain verification for package: "
+                    + verificationInfo.getPackageName());
+            serializer.endTag(null, TAG_DOMAIN_VERIFICATION);
+        }
+    }
+
     void readPackageRestrictionsLPr(int userId) {
         if (DEBUG_MU) {
             Log.i(TAG, "Reading package restrictions for user=" + userId);
@@ -1127,8 +1242,8 @@
                                 false,  // notLaunched
                                 false,  // hidden
                                 null, null, null,
-                                false // blockUninstall
-                                );
+                                false, // blockUninstall
+                                INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
                     }
                     return;
                 }
@@ -1197,6 +1312,12 @@
                     final boolean blockUninstall = blockUninstallStr == null
                             ? false : Boolean.parseBoolean(blockUninstallStr);
 
+                    final String verifStateStr =
+                            parser.getAttributeValue(null, ATTR_DOMAIN_VERIFICATON_STATE);
+                    final int verifState = (verifStateStr == null) ?
+                            PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED :
+                            Integer.parseInt(verifStateStr);
+
                     ArraySet<String> enabledComponents = null;
                     ArraySet<String> disabledComponents = null;
 
@@ -1217,7 +1338,8 @@
                     }
 
                     ps.setUserState(userId, enabled, installed, stopped, notLaunched, hidden,
-                            enabledCaller, enabledComponents, disabledComponents, blockUninstall);
+                            enabledCaller, enabledComponents, disabledComponents, blockUninstall,
+                            verifState);
                 } else if (tagName.equals("preferred-activities")) {
                     readPreferredActivitiesLPw(parser, userId);
                 } else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
@@ -1364,7 +1486,9 @@
                                 && ustate.enabledComponents.size() > 0)
                         || (ustate.disabledComponents != null
                                 && ustate.disabledComponents.size() > 0)
-                        || ustate.blockUninstall) {
+                        || ustate.blockUninstall
+                        || (ustate.domainVerificationStatus !=
+                            PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED)) {
                     serializer.startTag(null, TAG_PACKAGE);
                     serializer.attribute(null, ATTR_NAME, pkg.name);
                     if (DEBUG_MU) Log.i(TAG, "  pkg=" + pkg.name + ", state=" + ustate.enabled);
@@ -1392,6 +1516,11 @@
                                     ustate.lastDisableAppCaller);
                         }
                     }
+                    if (ustate.domainVerificationStatus !=
+                            PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED) {
+                        serializer.attribute(null, ATTR_DOMAIN_VERIFICATON_STATE,
+                                Integer.toString(ustate.domainVerificationStatus));
+                    }
                     if (ustate.enabledComponents != null
                             && ustate.enabledComponents.size() > 0) {
                         serializer.startTag(null, TAG_ENABLED_COMPONENTS);
@@ -1418,9 +1547,7 @@
             }
 
             writePreferredActivitiesLPr(serializer, userId, true);
-
             writePersistentPreferredActivitiesLPr(serializer, userId);
-
             writeCrossProfileIntentFiltersLPr(serializer, userId);
 
             serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS);
@@ -1933,6 +2060,7 @@
         writeSigningKeySetsLPr(serializer, pkg.keySetData);
         writeUpgradeKeySetsLPr(serializer, pkg.keySetData);
         writeKeySetAliasesLPr(serializer, pkg.keySetData);
+        writeDomainVerificationsLPr(serializer, pkg.name, pkg.verificationInfo);
 
         serializer.endTag(null, "package");
     }
@@ -2109,7 +2237,8 @@
                     // TODO: check whether this is okay! as it is very
                     // similar to how preferred-activities are treated
                     readCrossProfileIntentFiltersLPw(parser, 0);
-                } else if (tagName.equals("updated-package")) {
+                }
+                else if (tagName.equals("updated-package")) {
                     readDisabledSysPackageLPw(parser);
                 } else if (tagName.equals("cleaning-package")) {
                     String name = parser.getAttributeValue(null, ATTR_NAME);
@@ -3024,6 +3153,8 @@
                     long id = Long.parseLong(parser.getAttributeValue(null, "identifier"));
                     String alias = parser.getAttributeValue(null, "alias");
                     packageSetting.keySetData.addDefinedKeySet(id, alias);
+                } else if (tagName.equals(TAG_DOMAIN_VERIFICATION)) {
+                    readDomainVerificationLPw(parser, packageSetting);
                 } else {
                     PackageManagerService.reportSettingsProblem(Log.WARN,
                             "Unknown element under <package>: " + parser.getName());
@@ -3388,7 +3519,7 @@
         return false;
     }
 
-    private List<UserInfo> getAllUsers() {
+    List<UserInfo> getAllUsers() {
         long id = Binder.clearCallingIdentity();
         try {
             return UserManagerService.getInstance().getUsers(false);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index be6550c..87cf06e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1398,6 +1398,14 @@
                     public void onDebug() {
                         // no-op
                     }
+                    @Override
+                    public void onDown() {
+                        mOrientationListener.onTouchStart();
+                    }
+                    @Override
+                    public void onUpOrCancel() {
+                        mOrientationListener.onTouchEnd();
+                    }
                 });
         mImmersiveModeConfirmation = new ImmersiveModeConfirmation(mContext);
         mWindowManagerFuncs.registerPointerEventListener(mSystemGestures);
diff --git a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
index cfa631f..627b328 100644
--- a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
@@ -75,6 +75,7 @@
                 mDebugFireable = true;
                 mDownPointers = 0;
                 captureDown(event, 0);
+                mCallbacks.onDown();
                 break;
             case MotionEvent.ACTION_POINTER_DOWN:
                 captureDown(event, event.getActionIndex());
@@ -106,6 +107,7 @@
             case MotionEvent.ACTION_CANCEL:
                 mSwipeFireable = false;
                 mDebugFireable = false;
+                mCallbacks.onUpOrCancel();
                 break;
             default:
                 if (DEBUG) Slog.d(TAG, "Ignoring " + event);
@@ -192,6 +194,8 @@
         void onSwipeFromTop();
         void onSwipeFromBottom();
         void onSwipeFromRight();
+        void onDown();
+        void onUpOrCancel();
         void onDebug();
     }
 }
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java
index 0118127..a33ee4c 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java
@@ -22,6 +22,7 @@
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.util.Log;
 import android.util.Slog;
@@ -133,6 +134,20 @@
         }
     }
 
+    public void onTouchStart() {
+        synchronized (mLock) {
+            mSensorEventListener.onTouchStartLocked();
+        }
+    }
+
+    public void onTouchEnd() {
+        long whenElapsedNanos = SystemClock.elapsedRealtimeNanos();
+
+        synchronized (mLock) {
+            mSensorEventListener.onTouchEndLocked(whenElapsedNanos);
+        }
+    }
+
     /**
      * Sets the current rotation.
      *
@@ -269,6 +284,11 @@
         private static final long PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS =
                 500 * NANOS_PER_MS;
 
+        // The minimum amount of time that must have elapsed since the screen was last touched
+        // before the proposed rotation can change.
+        private static final long PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS =
+                500 * NANOS_PER_MS;
+
         // If the tilt angle remains greater than the specified angle for a minimum of
         // the specified time, then the device is deemed to be lying flat
         // (just chillin' on a table).
@@ -398,6 +418,10 @@
         private long mAccelerationTimestampNanos;
         private boolean mAccelerating;
 
+        // Timestamp when the last touch to the touch screen ended
+        private long mTouchEndedTimestampNanos = Long.MIN_VALUE;
+        private boolean mTouched;
+
         // Whether we are locked into an overhead usage mode.
         private boolean mOverhead;
 
@@ -422,6 +446,7 @@
             pw.println(prefix + "mSwinging=" + mSwinging);
             pw.println(prefix + "mAccelerating=" + mAccelerating);
             pw.println(prefix + "mOverhead=" + mOverhead);
+            pw.println(prefix + "mTouched=" + mTouched);
         }
 
         @Override
@@ -601,6 +626,7 @@
                             + ", isFlat=" + isFlat
                             + ", isSwinging=" + isSwinging
                             + ", isOverhead=" + mOverhead
+                            + ", isTouched=" + mTouched
                             + ", timeUntilSettledMS=" + remainingMS(now,
                                     mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS)
                             + ", timeUntilAccelerationDelayExpiredMS=" + remainingMS(now,
@@ -608,7 +634,9 @@
                             + ", timeUntilFlatDelayExpiredMS=" + remainingMS(now,
                                     mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS)
                             + ", timeUntilSwingDelayExpiredMS=" + remainingMS(now,
-                                    mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS));
+                                    mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS)
+                            + ", timeUntilTouchDelayExpiredMS=" + remainingMS(now,
+                                    mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS));
                 }
             }
 
@@ -710,6 +738,12 @@
                 return false;
             }
 
+            // The last touch must have ended sufficiently long ago.
+            if (mTouched || now < mTouchEndedTimestampNanos
+                    + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS) {
+                return false;
+            }
+
             // Looks good!
             return true;
         }
@@ -796,5 +830,14 @@
         private float remainingMS(long now, long until) {
             return now >= until ? 0 : (until - now) * 0.000001f;
         }
+
+        private void onTouchStartLocked() {
+            mTouched = true;
+        }
+
+        private void onTouchEndLocked(long whenElapsedNanos) {
+            mTouched = false;
+            mTouchEndedTimestampNanos = whenElapsedNanos;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 957eb9e..32bb78e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -4608,8 +4608,7 @@
             if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) Slog.v(TAG, "setAppVisibility(" +
                     token + ", visible=" + visible + "): " + mAppTransition +
                     " hidden=" + wtoken.hidden + " hiddenRequested=" +
-                    wtoken.hiddenRequested, HIDE_STACK_CRAWLS ?
-                            null : new RuntimeException("here").fillInStackTrace());
+                    wtoken.hiddenRequested + " Callers=" + Debug.getCallers(6));
 
             // If we are preparing an app transition, then delay changing
             // the visibility of this token until we execute that transition.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 73b5de1..0c58aef 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -78,6 +78,8 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.ContactsContract.QuickContact;
+import android.provider.ContactsInternal;
 import android.provider.Settings;
 import android.security.Credentials;
 import android.security.IKeyChainAliasCallback;
@@ -146,6 +148,8 @@
 
     private static final String LOG_TAG = "DevicePolicyManagerService";
 
+    private static final boolean VERBOSE_LOG = false; // DO NOT SUBMIT WITH TRUE
+
     private static final String DEVICE_POLICIES_XML = "device_policies.xml";
 
     private static final String LOCK_TASK_COMPONENTS_XML = "lock-task-component";
@@ -5435,6 +5439,59 @@
         }
     }
 
+    @Override
+    public void startManagedQuickContact(String actualLookupKey, long actualContactId,
+            Intent originalIntent) {
+        final Intent intent = QuickContact.rebuildManagedQuickContactsIntent(
+                actualLookupKey, actualContactId, originalIntent);
+        final int callingUserId = UserHandle.getCallingUserId();
+
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                final int managedUserId = getManagedUserId(callingUserId);
+                if (managedUserId < 0) {
+                    return;
+                }
+                if (getCrossProfileCallerIdDisabledForUser(managedUserId)) {
+                    if (VERBOSE_LOG) {
+                        Log.v(LOG_TAG,
+                                "Cross-profile contacts access disabled for user " + managedUserId);
+                    }
+                    return;
+                }
+                ContactsInternal.startQuickContactWithErrorToastForUser(
+                        mContext, intent, new UserHandle(managedUserId));
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    /**
+     * @return the user ID of the managed user that is linked to the current user, if any.
+     * Otherwise -1.
+     */
+    public int getManagedUserId(int callingUserId) {
+        if (VERBOSE_LOG) {
+            Log.v(LOG_TAG, "getManagedUserId: callingUserId=" + callingUserId);
+        }
+
+        for (UserInfo ui : mUserManager.getProfiles(callingUserId)) {
+            if (ui.id == callingUserId || !ui.isManagedProfile()) {
+                continue; // Caller user self, or not a managed profile.  Skip.
+            }
+            if (VERBOSE_LOG) {
+                Log.v(LOG_TAG, "Managed user=" + ui.id);
+            }
+            return ui.id;
+        }
+        if (VERBOSE_LOG) {
+            Log.v(LOG_TAG, "Managed user not found.");
+        }
+        return -1;
+    }
+
     /**
      * Sets which packages may enter lock task mode.
      *
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8c653e2..090c0f8 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -912,6 +912,11 @@
                 }
             }
 
+            if (!disableNonCoreServices) {
+                ServiceManager.addService(GraphicsStatsService.GRAPHICS_STATS_SERVICE,
+                        new GraphicsStatsService(context));
+            }
+
             if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
                 mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
             }
diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
index 3b65709..7c101a40f 100644
--- a/services/usb/java/com/android/server/usb/UsbMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.media.midi.MidiDeviceInfo;
 import android.media.midi.MidiDeviceServer;
-import android.media.midi.MidiDispatcher;
 import android.media.midi.MidiManager;
 import android.media.midi.MidiReceiver;
 import android.media.midi.MidiSender;
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 67a8c2b..a204376 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -31,6 +31,7 @@
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstrumentationInfo;
+import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.KeySet;
 import android.content.pm.ManifestDigest;
 import android.content.pm.PackageInfo;
@@ -725,6 +726,38 @@
      * @hide
      */
     @Override
+    public void verifyIntentFilter(int id, int verificationCode, List<String> outFailedDomains) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public int getIntentVerificationStatus(String packageName, int userId) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean updateIntentVerificationStatus(String packageName, int status, int userId) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
     public VerifierDeviceIdentity getVerifierDeviceIdentity() {
         throw new UnsupportedOperationException();
     }
diff --git a/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java b/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java
index 24e4b54..c72efc2 100644
--- a/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java
+++ b/tools/layoutlib/bridge/src/android/text/GreedyLineBreaker.java
@@ -166,7 +166,7 @@
             if (lineBreaks.breaks.length != mBreaksList.size()) {
                 lineBreaks.breaks = new int[mBreaksList.size()];
                 lineBreaks.widths = new float[mWidthsList.size()];
-                lineBreaks.flags = new boolean[mFlagsList.size()];
+                lineBreaks.flags = new int[mFlagsList.size()];
             }
 
             int i = 0;
@@ -181,7 +181,7 @@
             }
             i = 0;
             for (boolean b : mFlagsList) {
-                lineBreaks.flags[i] = b;
+                lineBreaks.flags[i] = b ? TAB_MASK : 0;
                 i++;
             }
 
diff --git a/tools/layoutlib/bridge/src/android/text/LineBreaker.java b/tools/layoutlib/bridge/src/android/text/LineBreaker.java
index 8be3635..54445a4 100644
--- a/tools/layoutlib/bridge/src/android/text/LineBreaker.java
+++ b/tools/layoutlib/bridge/src/android/text/LineBreaker.java
@@ -26,6 +26,8 @@
 // frameworks/base/core/jni/android_text_StaticLayout.cpp revision b808260
 public abstract class LineBreaker {
 
+    protected static final int TAB_MASK   = 0x20000000;  // keep in sync with StaticLayout
+
     protected final @NonNull List<Primitive> mPrimitives;
     protected final @NonNull LineWidth mLineWidth;
     protected final @NonNull TabStops mTabStops;
diff --git a/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java b/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java
index d5d7798..cd92581 100644
--- a/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java
+++ b/tools/layoutlib/bridge/src/android/text/OptimizingLineBreaker.java
@@ -51,7 +51,7 @@
             assert p.type == PrimitiveType.PENALTY;
             breakInfo.breaks = new int[]{0};
             breakInfo.widths = new float[]{p.width};
-            breakInfo.flags = new boolean[]{false};
+            breakInfo.flags = new int[]{0};
             return;
         }
         Node[] opt = new Node[numBreaks];
@@ -129,7 +129,7 @@
 
             breakInfo.breaks[count] = mPrimitives.get(idx).location;
             breakInfo.widths[count] = opt[idx].mWidth;
-            breakInfo.flags [count] = opt[idx].mHasTabs;
+            breakInfo.flags [count] = opt[idx].mHasTabs ? TAB_MASK : 0;
             idx = opt[idx].mPrev;
         }
     }
@@ -140,7 +140,7 @@
         }
         int[] breaks = new int[size];
         float[] widths = new float[size];
-        boolean[] flags = new boolean[size];
+        int[] flags = new int[size];
 
         int toCopy = Math.min(size, lineBreaks.breaks.length);
         System.arraycopy(lineBreaks.breaks, 0, breaks, 0, toCopy);